CustusX  15.8
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxImage.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 
34 #include "cxImage.h"
35 
36 #include <QDomDocument>
37 #include <QDir>
38 #include <vtkImageAccumulate.h>
39 #include <vtkImageReslice.h>
40 #include <vtkImageData.h>
41 #include <vtkMatrix4x4.h>
42 #include <vtkPlane.h>
43 #include <vtkPlanes.h>
44 #include <vtkImageResample.h>
45 #include <vtkImageChangeInformation.h>
46 #include <vtkImageClip.h>
47 #include <vtkImageIterator.h>
48 #include <vtkPiecewiseFunction.h>
49 #include <vtkColorTransferFunction.h>
50 #include "cxImageTF3D.h"
51 #include "cxBoundingBox3D.h"
52 #include "cxImageLUT2D.h"
54 #include "cxLandmark.h"
55 
56 #include "cxLogger.h"
57 #include "cxTypeConversions.h"
58 #include "cxUtilHelpers.h"
59 #include "cxVolumeHelpers.h"
61 #include "cxDataReaderWriter.h"
62 #include "cxNullDeleter.h"
63 
64 #include "cxUnsignedDerivedImage.h"
65 
66 typedef vtkSmartPointer<vtkImageChangeInformation> vtkImageChangeInformationPtr;
67 
68 namespace cx
69 {
70 
72 {
73  on = true;
74  ambient = 0.2;
75  diffuse = 0.9;
76  specular = 0.3;
77  specularPower = 15.0;
78 }
79 
80 double Image::ShadingStruct::loadAttribute(QDomNode dataNode, QString name, double defVal)
81 {
82  QString text = dataNode.toElement().attribute(name);
83  bool ok;
84  double val = text.toDouble(&ok);
85  if (ok)
86  return val;
87  return defVal;
88 }
89 
90 void Image::ShadingStruct::addXml(QDomNode dataNode)
91 {
92  QDomElement elem = dataNode.toElement();
93  elem.setAttribute("on", on);
94  elem.setAttribute("ambient", ambient);
95  elem.setAttribute("diffuse", diffuse);
96  elem.setAttribute("specular", specular);
97  elem.setAttribute("specularPower", specularPower);
98 }
99 
100 void Image::ShadingStruct::parseXml(QDomNode dataNode)
101 {
102  if (dataNode.isNull())
103  return;
104 
105  on = dataNode.toElement().attribute("on").toInt();
106  // std::cout << "attrib on: " << dataNode.toElement().attribute("on") << " : " << on << std::endl;
107  ambient = loadAttribute(dataNode, "ambient", ambient);
108  diffuse = loadAttribute(dataNode, "diffuse", diffuse);
109  specular = loadAttribute(dataNode, "specular", specular);
110  specularPower = loadAttribute(dataNode, "specularPower", specularPower);
111 }
112 
113 //---------------------------------------------------------
114 //---------------------------------------------------------
115 //---------------------------------------------------------
116 
117 ImagePtr Image::create(const QString& uid, const QString& name)
118 {
119  return ImagePtr(new Image(uid, vtkImageDataPtr(), name));
120 }
121 
123 {
124 }
125 
126 Image::Image(const QString& uid, const vtkImageDataPtr& data, const QString& name) :
127  Data(uid, name), mBaseImageData(data), mMaxRGBIntensity(-1), mThresholdPreview(false)
128 {
129  mInitialWindowWidth = -1;
130  mInitialWindowLevel = -1;
131 
132  mInterpolationType = VTK_LINEAR_INTERPOLATION;
133  mUseCropping = false;
134  mCroppingBox_d = this->getInitialBoundingBox();
135  mModality = "UNKNOWN";
136 
137  mImageLookupTable2D.reset();
138  mImageTransferFunctions3D.reset();
139 
140  this->setAcquisitionTime(QDateTime::currentDateTime());
141 }
142 
144 {
145  vtkImageDataPtr baseImageDataCopy;
146  if(mBaseImageData)
147  {
148  baseImageDataCopy = vtkImageDataPtr::New();
149  baseImageDataCopy->DeepCopy(mBaseImageData);
150  }
151 
152  ImagePtr retval = ImagePtr(new Image(mUid, baseImageDataCopy, mName));
153 
154  retval->mUnsigned = mUnsigned;
155  retval->mModality = mModality;
156  retval->mImageType = mImageType;
157  retval->mMaxRGBIntensity = mMaxRGBIntensity;
158  retval->mInterpolationType = mInterpolationType;
159  retval->mImageLookupTable2D = mImageLookupTable2D;
160  retval->mImageTransferFunctions3D = mImageTransferFunctions3D;
161  retval->mInitialWindowWidth = mInitialWindowWidth;
162  retval->mInitialWindowLevel = mInitialWindowLevel;
163 
164  //From cx::Data
165  retval->mRegistrationStatus = mRegistrationStatus;
166  retval->m_rMd_History = m_rMd_History;
167 
168  return retval;
169 }
170 
172 {
173  this->get_rMd_History()->setRegistration(parentImage->get_rMd());
174  this->get_rMd_History()->setParentSpace(parentImage->getUid());
175  ImageTF3DPtr transferFunctions = parentImage->getUnmodifiedTransferFunctions3D()->createCopy();
176  ImageLUT2DPtr LUT2D = parentImage->getUnmodifiedLookupTable2D()->createCopy();
177  this->setLookupTable2D(LUT2D);
178  this->setTransferFunctions3D(transferFunctions);
179  this->setModality(parentImage->getModality());
180  this->setImageType(parentImage->getImageType());
181  this->setShading(parentImage->getShading());
182  mInitialWindowWidth = parentImage->getInitialWindowWidth();
183  mInitialWindowLevel = parentImage->getInitialWindowLevel();
184 }
185 
186 DoubleBoundingBox3D Image::getInitialBoundingBox() const
187 {
188  return DoubleBoundingBox3D(-1, -1, -1, -1, -1, -1);
189 }
190 
192 {
193  CX_ASSERT(this==self.get());
194 
195  if (!mUnsigned)
196  {
197  // self is unsigned: return self
198  if (this->getBaseVtkImageData()->GetScalarTypeMin() >= 0)
199  return self;
200  else // signed: create unsigned adapter
202  }
203 
204  return mUnsigned;
205 }
206 
207 
208 
209 void Image::resetTransferFunctions(bool _2D, bool _3D)
210 {
211  if (!mBaseImageData)
212  {
213  reportWarning("Image has no image data");
214  return;
215  }
216 
217  mBaseImageData->GetScalarRange(); // this line updates some internal vtk value, and (on fedora) removes 4.5s in the second render().
218  mMaxRGBIntensity = -1;
219 
220  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
221  if (_3D)
222  {
223  this->resetTransferFunction(tfGenerator.generate3DTFPreset());
224  tfGenerator.resetShading();
225  }
226  if (_2D)
227  this->resetTransferFunction(tfGenerator.generate2DTFPreset());
228 }
229 
230 void Image::resetTransferFunction(ImageTF3DPtr imageTransferFunctions3D, ImageLUT2DPtr imageLookupTable2D)
231 {
232  this->blockSignals(true); // avoid emitting two transferFunctionsChanged() for one call.
233 
234  this->resetTransferFunction(imageTransferFunctions3D);
235  this->resetTransferFunction(imageLookupTable2D);
236 
237  this->blockSignals(false);
239 }
240 
241 void Image::resetTransferFunction(ImageLUT2DPtr imageLookupTable2D)
242 {
243  if (mImageLookupTable2D)
244  {
245  disconnect(mImageLookupTable2D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
246  }
247 
248  mImageLookupTable2D = imageLookupTable2D;
249 
250  if (mImageLookupTable2D)
251  {
252  connect(mImageLookupTable2D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
253  }
254 
256 }
257 
258 void Image::resetTransferFunction(ImageTF3DPtr imageTransferFunctions3D)
259 {
260  if (mImageTransferFunctions3D)
261  {
262  disconnect(mImageTransferFunctions3D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
263  }
264 
265  mImageTransferFunctions3D = imageTransferFunctions3D;
266 
267  if (mImageTransferFunctions3D)
268  {
269  connect(mImageTransferFunctions3D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
270  }
271 
273 }
274 
276 {
277 }
278 
280 {
281  // important! move thread affinity to main thread - ensures signals/slots is still called correctly
282  this->moveToThread(thread);
283  this->getUnmodifiedTransferFunctions3D()->moveToThread(thread);
284  this->getUnmodifiedLookupTable2D()->moveToThread(thread);
285  this->get_rMd_History()->moveToThread(thread);
286 }
287 
288 void Image::setVtkImageData(const vtkImageDataPtr& data, bool resetTransferFunctions)
289 {
290  mBaseImageData = data;
292  mHistogramPtr = NULL;
293 
294  if (resetTransferFunctions)
295  this->resetTransferFunctions();
296  emit vtkImageDataChanged();
297 }
298 
300 {
301  double windowWidth = mImageLookupTable2D->getWindow();
302  double windowLevel = mImageLookupTable2D->getLevel();
303  return convertImageDataTo8Bit(this->getGrayScaleVtkImageData(), windowWidth, windowLevel);
304 }
305 
307 {
309  {
311  }
312 
315 }
316 
318 {
319  if(mThresholdPreview)
320  return mTresholdPreviewTransferfunctions3D;
321  return getUnmodifiedTransferFunctions3D();
322 }
323 
324 ImageTF3DPtr Image::getUnmodifiedTransferFunctions3D()
325 {
326  if(!this->mImageTransferFunctions3D)
327  this->resetTransferFunctions(false, true);
328  return mImageTransferFunctions3D;
329 }
330 
332 {
333  this->resetTransferFunction(transferFuntion);
334 }
335 
337 {
338  if(mThresholdPreview)
339  return mTresholdPreviewLookupTable2D;
340  return getUnmodifiedLookupTable2D();
341 }
342 
343 ImageLUT2DPtr Image::getUnmodifiedLookupTable2D()
344 {
345  if(!mImageLookupTable2D)
346  this->resetTransferFunctions(true, false);
347  return mImageLookupTable2D;
348 }
349 
350 void Image::setLookupTable2D(ImageLUT2DPtr imageLookupTable2D)
351 {
352  this->resetTransferFunction(imageLookupTable2D);
353 }
354 
356 {
357  return mBaseImageData;
358 }
359 
361 {
362 // mBaseImageData->UpdateInformation();
363  DoubleBoundingBox3D bounds(mBaseImageData->GetBounds());
364  return bounds;
365 }
366 
367 Eigen::Array3d Image::getSpacing() const
368 {
369  return Eigen::Array3d(mBaseImageData->GetSpacing());
370 }
371 
373 {
374  if (mHistogramPtr.GetPointer() == NULL)
375  {
376  mHistogramPtr = vtkImageAccumulatePtr::New();
377  mHistogramPtr->SetInputData(this->getGrayScaleVtkImageData());
378  mHistogramPtr->IgnoreZeroOn(); // required for Sonowand CT volumes, where data are placed between 31K and 35K.
379  // Set up only a 1D histogram for now, so y and z values are set to 0
380  mHistogramPtr->SetComponentExtent(0, this->getRange(), 0, 0, 0, 0);
381  mHistogramPtr->SetComponentOrigin(this->getMin(), 0, 0);
382  mHistogramPtr->SetComponentSpacing(1, 0, 0);
383  }
384  mHistogramPtr->Update();
385  return mHistogramPtr;
386 }
387 
388 template<typename scalartype> static int getRGBMax(vtkImageDataPtr image)
389 {
390  int max = 0;
391  vtkImageIterator<scalartype> iter(image, image->GetExtent());
392  while (!iter.IsAtEnd())
393  {
394  typename vtkImageIterator<scalartype>::SpanIterator siter = iter.BeginSpan();
395  while (siter != iter.EndSpan())
396  {
397  int value = *siter;
398  ++siter;
399  value += *siter;
400  ++siter;
401  value += *siter;
402  ++siter;
403  if (value > max)
404  {
405  max = value;
406  }
407  }
408  iter.NextSpan();
409  }
410  return max/3;
411 }
412 
413 
415 {
416  // Alternatively create max from histogram
417  //IntIntMap::iterator iter = this->getHistogram()->end();
418  //iter--;
419  //return (*iter).first;
420  if (mBaseImageData->GetNumberOfScalarComponents() == 3)
421  {
422  if (mMaxRGBIntensity != -1)
423  {
424  return mMaxRGBIntensity;
425  }
426  QDateTime before = QDateTime::currentDateTime();
427  double max = 0.0;
428  switch (mBaseImageData->GetScalarType())
429  {
430  case VTK_UNSIGNED_CHAR:
431  max = getRGBMax<unsigned char>(mBaseImageData);
432  break;
433  case VTK_UNSIGNED_SHORT:
434  max = getRGBMax<unsigned short>(mBaseImageData);
435  break;
436  default:
437  CX_LOG_ERROR() << "Unhandled RGB data type in image " << this->getUid();
438  break;
439  }
440  mMaxRGBIntensity = max;
441  return (int)mMaxRGBIntensity;
442  }
443  else
444  {
445 // return (int) this->getTransferFunctions3D()->getScalarMax();
446  return mBaseImageData->GetScalarRange()[1];
447  }
448 }
449 
451 {
452  // Alternatively create min from histogram
453  //IntIntMap::iterator iter = this->getHistogram()->begin();
454  //return (*iter).first;
455  return mBaseImageData->GetScalarRange()[0];
456 // return (int) this->getTransferFunctions3D()->getScalarMin();
457 }
458 
460 {
461  return this->getMax() - this->getMin();
462 }
463 
465 {
466  return 255;
467 }
468 
470 {
471  int vtkScalarType = mBaseImageData->GetScalarType();
472 
473  if (vtkScalarType==VTK_CHAR)
474  return VTK_CHAR_MIN;
475  else if (vtkScalarType==VTK_UNSIGNED_CHAR)
476  return VTK_UNSIGNED_CHAR_MIN;
477  else if (vtkScalarType==VTK_SIGNED_CHAR)
478  return VTK_SIGNED_CHAR_MIN;
479  else if (vtkScalarType==VTK_UNSIGNED_SHORT)
480  return VTK_UNSIGNED_SHORT_MIN;
481  else if (vtkScalarType==VTK_SHORT)
482  return VTK_SHORT_MIN;
483  else if (vtkScalarType==VTK_UNSIGNED_INT)
484  return VTK_UNSIGNED_INT_MIN;
485  else if (vtkScalarType==VTK_INT)
486  return VTK_INT_MIN;
487  else
488  reportError(QString("Unknown VTK ScalarType: %1").arg(vtkScalarType));
489  return 0;
490 }
491 
493 {
494  int vtkScalarType = mBaseImageData->GetScalarType();
495 
496  if (vtkScalarType==VTK_CHAR)
497  return VTK_CHAR_MAX;
498  else if (vtkScalarType==VTK_UNSIGNED_CHAR)
499  return VTK_UNSIGNED_CHAR_MAX;
500  else if (vtkScalarType==VTK_SIGNED_CHAR)
501  return VTK_SIGNED_CHAR_MAX;
502  else if (vtkScalarType==VTK_UNSIGNED_SHORT)
503  return VTK_UNSIGNED_SHORT_MAX;
504  else if (vtkScalarType==VTK_SHORT)
505  return VTK_SHORT_MAX;
506  else if (vtkScalarType==VTK_UNSIGNED_INT)
507  return VTK_UNSIGNED_INT_MAX;
508  else if (vtkScalarType==VTK_INT)
509  return VTK_INT_MAX;
510  else
511  reportError(QString("Unknown VTK ScalarType: %1").arg(vtkScalarType));
512  return 0;
513 }
514 
515 void Image::addXml(QDomNode& dataNode)
516 {
517  Data::addXml(dataNode);
518  QDomNode imageNode = dataNode;
519  QDomDocument doc = dataNode.ownerDocument();
520 
521  QDomElement tf3DNode = doc.createElement("transferfunctions");
522  this->getUnmodifiedTransferFunctions3D()->addXml(tf3DNode);
523  imageNode.appendChild(tf3DNode);
524 
525  QDomElement lut2DNode = doc.createElement("lookuptable2D");
526  this->getUnmodifiedLookupTable2D()->addXml(lut2DNode);
527  imageNode.appendChild(lut2DNode);
528 
529  QDomElement shadingNode = doc.createElement("shading");
530  mShading.addXml(shadingNode);
531  imageNode.appendChild(shadingNode);
532 
533 // QDomElement landmarksNode = doc.createElement("landmarks");
534 // mLandmarks->addXml(landmarksNode);
535 // imageNode.appendChild(landmarksNode);
536 
537  QDomElement cropNode = doc.createElement("crop");
538  cropNode.setAttribute("use", mUseCropping);
539  cropNode.appendChild(doc.createTextNode(qstring_cast(mCroppingBox_d)));
540  imageNode.appendChild(cropNode);
541 
542  QDomElement clipNode = doc.createElement("clip");
543  for (unsigned i = 0; i < mPersistentClipPlanes.size(); ++i)
544  {
545  QDomElement planeNode = doc.createElement("plane");
546  Vector3D normal(mPersistentClipPlanes[i]->GetNormal());
547  Vector3D origin(mPersistentClipPlanes[i]->GetOrigin());
548  planeNode.setAttribute("normal", qstring_cast(normal));
549  planeNode.setAttribute("origin", qstring_cast(origin));
550  clipNode.appendChild(planeNode);
551  }
552  imageNode.appendChild(clipNode);
553 
554  QDomElement modalityNode = doc.createElement("modality");
555  modalityNode.appendChild(doc.createTextNode(mModality));
556  imageNode.appendChild(modalityNode);
557 
558  QDomElement imageTypeNode = doc.createElement("imageType");
559  imageTypeNode.appendChild(doc.createTextNode(mImageType));
560  imageNode.appendChild(imageTypeNode);
561 
562  QDomElement interpolationNode = doc.createElement("vtk_interpolation");
563  interpolationNode.setAttribute("type", mInterpolationType);
564  imageNode.appendChild(interpolationNode);
565 
566  QDomElement initialWindowNode = doc.createElement("initialWindow");
567  initialWindowNode.setAttribute("width", mInitialWindowWidth);
568  initialWindowNode.setAttribute("level", mInitialWindowLevel);
569 }
570 
571 double Image::loadAttribute(QDomNode dataNode, QString name, double defVal)
572 {
573  QString text = dataNode.toElement().attribute(name);
574  bool ok;
575  double val = text.toDouble(&ok);
576  if (ok)
577  return val;
578  return defVal;
579 }
580 
581 bool Image::load(QString path)
582 {
583  ImagePtr self = ImagePtr(this, null_deleter());
584  DataReaderWriter().readInto(self, path);
585  return this->getBaseVtkImageData()!=0;
586 }
587 
588 void Image::parseXml(QDomNode& dataNode)
589 {
590  Data::parseXml(dataNode);
591 
592  // image node must be parsed in the data manager to create this Image object
593  // Only subnodes are parsed here
594 
595  if (dataNode.isNull())
596  return;
597 
598  //transferefunctions
599  QDomNode transferfunctionsNode = dataNode.namedItem("transferfunctions");
600  if (!transferfunctionsNode.isNull())
601  this->getUnmodifiedTransferFunctions3D()->parseXml(transferfunctionsNode);
602  else
603  {
604  std::cout << "Warning: Image::parseXml() found no transferfunctions";
605  std::cout << std::endl;
606  }
607 
608  mInitialWindowWidth = this->loadAttribute(dataNode.namedItem("initialWindow"), "width", -1);
609  mInitialWindowLevel = this->loadAttribute(dataNode.namedItem("initialWindow"), "level", -1);
610 
611  this->getUnmodifiedLookupTable2D()->parseXml(dataNode.namedItem("lookuptable2D"));
612 
613  // backward compatibility:
614  mShading.on = dataNode.namedItem("shading").toElement().text().toInt();
615  //Assign default values if the shading nodes don't exists to allow backward compability
616  if (!dataNode.namedItem("shadingAmbient").isNull())
617  mShading.ambient = dataNode.namedItem("shadingAmbient").toElement().text().toDouble();
618  if (!dataNode.namedItem("shadingDiffuse").isNull())
619  mShading.diffuse = dataNode.namedItem("shadingDiffuse").toElement().text().toDouble();
620  if (!dataNode.namedItem("shadingSpecular").isNull())
621  mShading.specular = dataNode.namedItem("shadingSpecular").toElement().text().toDouble();
622  if (!dataNode.namedItem("shadingSpecularPower").isNull())
623  mShading.specularPower = dataNode.namedItem("shadingSpecularPower").toElement().text().toDouble();
624 
625  // new way:
626  mShading.parseXml(dataNode.namedItem("shading"));
627 
628 // mLandmarks->parseXml(dataNode.namedItem("landmarks"));
629 
630  QDomElement cropNode = dataNode.namedItem("crop").toElement();
631  if (!cropNode.isNull())
632  {
633  mUseCropping = cropNode.attribute("use").toInt();
635  }
636 
637  QDomElement clipNode = dataNode.namedItem("clip").toElement();
638  QDomElement clipPlaneNode = clipNode.firstChildElement("plane");
639  for (; !clipPlaneNode.isNull(); clipPlaneNode = clipPlaneNode.nextSiblingElement("plane"))
640  {
641  Vector3D normal = Vector3D::fromString(clipPlaneNode.attribute("normal"));
642  Vector3D origin = Vector3D::fromString(clipPlaneNode.attribute("origin"));
643  vtkPlanePtr plane = vtkPlanePtr::New();
644  plane->SetNormal(normal.begin());
645  plane->SetOrigin(origin.begin());
646  mPersistentClipPlanes.push_back(plane);
647  }
648 
649  mModality = dataNode.namedItem("modality").toElement().text();
650  mImageType = dataNode.namedItem("imageType").toElement().text();
651 
652  QDomElement interpoationNode = dataNode.namedItem("vtk_interpolation").toElement();
653  if (!interpoationNode.isNull())
654  {
655  mInterpolationType = interpoationNode.attribute("type").toInt();
656  emit vtkImageDataChanged();
657  }
658 }
659 
660 void Image::setInitialWindowLevel(double width, double level)
661 {
662  mInitialWindowWidth = width;
663  mInitialWindowLevel = level;
664 }
665 
666 void Image::setShadingOn(bool on)
667 {
668  mShading.on = on;
670 }
671 
673 {
674  if (mThresholdPreview)
675  return true;
676  return mShading.on;
677 }
678 
679 void Image::setShadingAmbient(double ambient)
680 {
681  mShading.ambient = ambient;
683 }
684 
685 void Image::setShadingDiffuse(double diffuse)
686 {
687  mShading.diffuse = diffuse;
689 }
690 
691 void Image::setShadingSpecular(double specular)
692 {
693  mShading.specular = specular;
695 }
696 
697 void Image::setShadingSpecularPower(double specularPower)
698 {
699  mShading.specularPower = specularPower;
701 }
702 
704 {
705  return mShading.ambient;
706 }
707 
709 {
710  return mShading.diffuse;
711 }
712 
714 {
715  return mShading.specular;
716 }
717 
719 {
720  return mShading.specularPower;
721 }
722 
724 {
725  return mShading;
726 }
727 
729 {
730  mShading = shading;
732 }
733 
734 // methods for defining and storing a cropping box. Image does not use these data, this is up to the mapper
735 void Image::setCropping(bool on)
736 {
737  if (mUseCropping == on)
738  return;
739 
740  mUseCropping = on;
741  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
742  mCroppingBox_d = this->boundingBox();
743  emit cropBoxChanged();
744 }
745 
746 bool Image::getCropping() const
747 {
748  return mUseCropping;
749 }
750 
752 {
753  if (similar(mCroppingBox_d, bb_d))
754  return;
755  mCroppingBox_d = bb_d;
756  emit cropBoxChanged();
757 }
758 
760 {
761  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
762  return this->boundingBox();
763  return mCroppingBox_d;
764 }
765 
766 // methods for defining and storing clip planes. Image does not use these data, this is up to the mapper
768 {
769  if (std::count(mPersistentClipPlanes.begin(), mPersistentClipPlanes.end(), plane))
770  return;
771  mPersistentClipPlanes.push_back(plane);
772  emit clipPlanesChanged();
773 }
774 
775 std::vector<vtkPlanePtr> Image::getAllClipPlanes()
776 {
777  std::vector<vtkPlanePtr> retval = mPersistentClipPlanes;
779  retval.push_back(mInteractiveClipPlane);
780  return retval;
781 }
782 
784 {
785  mPersistentClipPlanes.clear();
786  emit clipPlanesChanged();
787 }
788 
790 {
791  mInteractiveClipPlane = plane;
792  emit clipPlanesChanged();
793 }
794 
808 {
809  // the internal CustusX format does not handle extents starting at non-zero.
810  // Move extent to zero and change rMd.
811  Vector3D origin(mBaseImageData->GetOrigin());
812  Vector3D spacing(mBaseImageData->GetSpacing());
813  IntBoundingBox3D extent(mBaseImageData->GetExtent());
814  Vector3D extentShift = multiply_elems(extent.corner(0, 0, 0).cast<double>(), spacing);
815 
816  vtkImageChangeInformationPtr info = vtkImageChangeInformationPtr::New();
817  info->SetInputData(mBaseImageData);
818  info->SetOutputExtentStart(0, 0, 0);
819  info->SetOutputOrigin(0, 0, 0);
820  info->Update();
821  info->UpdateInformation();
822  mBaseImageData = info->GetOutput();
823 
824  mBaseImageData->ComputeBounds();
825 // mBaseImageData->Update();
826 // mBaseImageData->UpdateInformation();
827 
828  this->get_rMd_History()->setRegistration(this->get_rMd() * createTransformTranslate(origin + extentShift));
829 
830  emit vtkImageDataChanged();
831  emit clipPlanesChanged();
832  emit cropBoxChanged();
833 }
834 
835 QString Image::getModality() const
836 {
837  return mModality;
838 }
839 
840 void Image::setModality(const QString& val)
841 {
842  mModality = val;
843  emit propertiesChanged();
844 }
845 
846 QString Image::getImageType() const
847 {
848  return mImageType;
849 }
850 
851 void Image::setImageType(const QString& val)
852 {
853  mImageType = val;
854  emit propertiesChanged();
855 }
856 
857 vtkImageDataPtr Image::createDummyImageData(int axisSize, int maxVoxelValue)
858 {
859  int size = axisSize - 1;//Modify axis size as extent starts with 0, not 1
860  vtkImageDataPtr dummyImageData = vtkImageDataPtr::New();
861  dummyImageData->SetExtent(0, size, 0, size, 0, size);
862  dummyImageData->SetSpacing(1, 1, 1);
863  //dummyImageData->SetScalarTypeToUnsignedShort();
864 // dummyImageData->SetScalarTypeToUnsignedChar();
865 // dummyImageData->SetNumberOfScalarComponents(1);
866 // dummyImageData->AllocateScalars();
867  dummyImageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
868  unsigned char* dataPtr = static_cast<unsigned char*> (dummyImageData->GetScalarPointer());
869 
870  //Init voxel colors
871  int minVoxelValue = 0;
872  int numVoxels = axisSize*axisSize*axisSize;
873  for (int i = 0; i < numVoxels; ++i)
874  {
875  int voxelValue = minVoxelValue + i;
876  if (i == numVoxels)
877  dataPtr[i] = maxVoxelValue;
878  else if (voxelValue < maxVoxelValue)
879  dataPtr[i] = voxelValue;
880  else
881  dataPtr[i] = maxVoxelValue;
882  }
883  setDeepModified(dummyImageData);
884  return dummyImageData;
885 }
886 
888 {
889  this->setInterpolationType(VTK_NEAREST_INTERPOLATION);
890 }
892 {
893  this->setInterpolationType(VTK_LINEAR_INTERPOLATION);
894 }
896 {
897  if (mThresholdPreview)
898  return;
899  mInterpolationType = val;
900  emit vtkImageDataChanged();
901 }
903 {
904  if (mThresholdPreview)
905  return VTK_NEAREST_INTERPOLATION;
906  return mInterpolationType;
907 }
908 
910 {
911  // also use grayscale as vtk is incapable of rendering 3component color.
912  vtkImageDataPtr retval = this->getGrayScaleVtkImageData();
913 
914  double factor = computeResampleFactor(maxVoxels);
915 
916  if (fabs(1.0-factor)>0.01) // resampling
917  {
918  vtkImageResamplePtr resampler = vtkImageResamplePtr::New();
919  resampler->SetInterpolationModeToLinear();
920  resampler->SetAxisMagnificationFactor(0, factor);
921  resampler->SetAxisMagnificationFactor(1, factor);
922  resampler->SetAxisMagnificationFactor(2, factor);
923  resampler->SetInputData(retval);
924 // resampler->GetOutput()->Update();
925  resampler->Update();
926  resampler->GetOutput()->GetScalarRange();
927  retval = resampler->GetOutput();
928 
929  long voxelsDown = retval->GetNumberOfPoints();
930  long voxelsOrig = this->getBaseVtkImageData()->GetNumberOfPoints();
931 // report("Created downsampled volume in Image: "
932 // + this->getName()
933 // + " below " + qstring_cast(voxelsDown/1000/1000) + "M. "
934 // + "Ratio: " + QString::number(factor, 'g', 2) + ", "
935 // + "Original size: " + qstring_cast(voxelsOrig/1000/1000) + "M.");
936  }
937  return retval;
938 }
939 
940 double Image::computeResampleFactor(long maxVoxels)
941 {
942  if (maxVoxels==0)
943  return 1.0;
944 
945  long voxels = this->getBaseVtkImageData()->GetNumberOfPoints();
946  double factor = (double)maxVoxels/(double)voxels;
947  factor = pow(factor, 1.0/3.0);
948  // cubic function leads to trouble for 138M-volume - must downsample to as low as 5-10 Mv in order to succeed on Mac.
949 
950  if (factor<0.99)
951  {
952  return factor;
953  }
954  return 1.0;
955 }
956 
957 void Image::save(const QString& basePath)
958 {
959  QString filename = basePath + "/Images/" + this->getUid() + ".mhd";
960  this->setFilename(QDir(basePath).relativeFilePath(filename));
961 
962  ImagePtr self = ImagePtr(this, null_deleter());
963  MetaImageReader().saveImage(self, filename);
964 }
965 
966 void Image::startThresholdPreview(const Eigen::Vector2d &threshold)
967 {
968  mThresholdPreview = true;
969 
970  this->createThresholdPreviewTransferFunctions3D(threshold);
971  this->createThresholdPreviewLookupTable2D(threshold);
972 
974 }
975 
976 void Image::createThresholdPreviewTransferFunctions3D(const Eigen::Vector2d &threshold)
977 {
978  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
979 
980  ColorMap colors = this->createPreviewColorMap(threshold);
981  IntIntMap opacity = this->createPreviewOpacityMap(threshold);
982 
983  mTresholdPreviewTransferfunctions3D = tfGenerator.generate3DTFPreset();
984  mTresholdPreviewTransferfunctions3D->resetColor(colors);
985  mTresholdPreviewTransferfunctions3D->resetAlpha(opacity);
986 }
987 
988 void Image::createThresholdPreviewLookupTable2D(const Eigen::Vector2d &threshold)
989 {
990  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
991 
992  ColorMap colors = this->createPreviewColorMap(threshold);
993 
994  mTresholdPreviewLookupTable2D = tfGenerator.generate2DTFPreset();
995  mTresholdPreviewLookupTable2D->resetColor(colors);
996  mTresholdPreviewLookupTable2D->setLLR(threshold[0]);
997 }
998 
999 ColorMap Image::createPreviewColorMap(const Eigen::Vector2d &threshold)
1000 {
1001  double lower = threshold[0];
1002  ColorMap colors;
1003  colors[lower] = Qt::green;
1004  colors[this->getMax()] = Qt::green;
1005  return colors;
1006 }
1007 
1008 IntIntMap Image::createPreviewOpacityMap(const Eigen::Vector2d &threshold)
1009 {
1010  double lower = threshold[0];
1011  double upper = threshold[1];
1012  IntIntMap opacity;
1013  opacity[lower - 1] = 0;
1014  opacity[lower] = this->getMaxAlphaValue();
1015  opacity[upper] = this->getMaxAlphaValue();
1016  opacity[upper + 1] = 0;
1017  return opacity;
1018 }
1019 
1021 {
1022  mThresholdPreview = false;
1023  mTresholdPreviewTransferfunctions3D.reset();
1024  mTresholdPreviewLookupTable2D.reset();
1025 
1026  //Need to tag these transfer functions as modified to tell the VTK pipeline that we got new TFs
1027  this->getTransferFunctions3D()->getColorTF()->Modified();
1028  this->getTransferFunctions3D()->getOpacityTF()->Modified();
1029 
1030  emit transferFunctionsChanged();
1031 }
1032 
1033 } // namespace cx
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
Definition: cxImage.cpp:588
QString qstring_cast(const T &val)
void transferFunctionsChanged()
static vtkImageDataPtr createDummyImageData(int axisSize, int maxVoxelValue)
Create a moc object of vtkImageData.
Definition: cxImage.cpp:857
Image(const QString &uid, const vtkImageDataPtr &data, const QString &name="")
Definition: cxImage.cpp:126
ShadingStruct mShading
Definition: cxImage.h:205
virtual bool getCropping() const
Definition: cxImage.cpp:746
void reportError(QString msg)
Definition: cxLogger.cpp:92
virtual void setModality(const QString &val)
Definition: cxImage.cpp:840
virtual Transform3D get_rMd() const
Definition: cxData.cpp:100
bool mUseCropping
image should be cropped using mCroppingBox
Definition: cxImage.h:207
virtual vtkImageDataPtr getGrayScaleVtkImageData()
as getBaseVtkImageData(), but constrained to 1 component if multicolor.
Definition: cxImage.cpp:306
int getInterpolationType() const
Definition: cxImage.cpp:902
virtual Eigen::Array3d getSpacing() const
Definition: cxImage.cpp:367
QString mImageType
type of the image, defined as DICOM tag (0008,0008) (mainly value 3, but might be a merge of value 4)...
Definition: cxImage.h:212
void parseXml(QDomNode dataNode)
Definition: cxImage.cpp:100
virtual void setTransferFunctions3D(ImageTF3DPtr transferFuntion)
Definition: cxImage.cpp:331
PlainObject normal() const
virtual double getShadingDiffuse()
Get shading diffuse parmeter.
Definition: cxImage.cpp:708
virtual vtkImageAccumulatePtr getHistogram()
Definition: cxImage.cpp:372
void mergevtkSettingsIntosscTransform()
Definition: cxImage.cpp:807
static ImagePtr create(const QString &uid, const QString &name)
Definition: cxImage.cpp:117
void addXml(QDomNode dataNode)
Definition: cxImage.cpp:90
#define CX_ASSERT(statement)
Definition: cxLogger.h:128
virtual void setShadingDiffuse(double diffuse)
Set shading diffuse parmeter.
Definition: cxImage.cpp:685
virtual QString getModality() const
Definition: cxImage.cpp:835
virtual std::vector< vtkPlanePtr > getAllClipPlanes()
Definition: cxImage.cpp:775
virtual void addPersistentClipPlane(vtkPlanePtr plane)
Definition: cxImage.cpp:767
QString mUid
Definition: cxData.h:118
virtual void setVtkImageData(const vtkImageDataPtr &data, bool resetTransferFunctions=true)
Definition: cxImage.cpp:288
vtkSmartPointer< class vtkImageAccumulate > vtkImageAccumulatePtr
void propertiesChanged()
emitted when one of the metadata properties (uid, name etc) changes
virtual ~Image()
Definition: cxImage.cpp:122
virtual void setLookupTable2D(ImageLUT2DPtr imageLookupTable2D)
Definition: cxImage.cpp:350
QString mName
Definition: cxData.h:119
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
virtual void setInitialWindowLevel(double width, double level)
Definition: cxImage.cpp:660
vtkSmartPointer< vtkImageChangeInformation > vtkImageChangeInformationPtr
Definition: cxImage.cpp:66
double mMaxRGBIntensity
Definition: cxImage.h:213
static DoubleBoundingBox3D fromString(const QString &text)
construct a bb from a string containing 6 whitespace-separated numbers
virtual void setCropping(bool on)
Definition: cxImage.cpp:735
virtual Image::ShadingStruct getShading()
Definition: cxImage.cpp:723
virtual void setShadingOn(bool on)
Definition: cxImage.cpp:666
DoubleBoundingBox3D mCroppingBox_d
box defining the cropping size.
Definition: cxImage.h:208
virtual void setShadingAmbient(double ambient)
Set shading ambient parmeter.
Definition: cxImage.cpp:679
void clipPlanesChanged()
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:355
static ImagePtr create(ImagePtr base)
virtual void addXml(QDomNode &dataNode)
adds xml information about the data and its variabels
Definition: cxData.cpp:120
virtual double getShadingSpecular()
Get shading specular parmeter.
Definition: cxImage.cpp:713
virtual void setShadingSpecular(double specular)
Set shading specular parmeter.
Definition: cxImage.cpp:691
virtual void setImageType(const QString &val)
Definition: cxImage.cpp:851
ImagePtr mUnsigned
version of this containing unsigned data.
Definition: cxImage.h:201
virtual ImagePtr getUnsigned(ImagePtr self)
Definition: cxImage.cpp:191
virtual int getMin()
Definition: cxImage.cpp:450
virtual void intitializeFromParentImage(ImagePtr parentImage)
Definition: cxImage.cpp:171
virtual QString getUid() const
Definition: cxData.cpp:78
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
virtual ImageTF3DPtr getTransferFunctions3D()
Definition: cxImage.cpp:317
void addXml(QDomNode &dataNode)
adds xml information about the image and its variabels
Definition: cxImage.cpp:515
virtual int getMax()
Definition: cxImage.cpp:414
virtual RegistrationHistoryPtr get_rMd_History()
Definition: cxData.cpp:105
std::map< int, QColor > ColorMap
Definition: cxImage.h:57
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
void startThresholdPreview(const Eigen::Vector2d &threshold)
Definition: cxImage.cpp:966
vtkPlanePtr mInteractiveClipPlane
Definition: cxImage.h:210
QString mModality
modality of the image, defined as DICOM tag (0008,0060), Section 3, C.7.3.1.1.1
Definition: cxImage.h:211
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
virtual int getMaxAlphaValue()
Max alpha value (probably 255)
Definition: cxImage.cpp:464
#define CX_LOG_ERROR
Definition: cxLogger.h:113
vtkImageDataPtr convertImageDataTo8Bit(vtkImageDataPtr image, double windowWidth, double windowLevel)
Have never been used or tested. Create a test for it.
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
Definition: cxData.cpp:146
virtual void setShadingSpecularPower(double specularPower)
Set shading specular power parmeter.
Definition: cxImage.cpp:697
int getVTKMinValue()
Definition: cxImage.cpp:469
void moveThisAndChildrenToThread(QThread *thread)
Move this and all children to thread. Use the thread is generated in a worker thread and the result i...
Definition: cxImage.cpp:279
virtual int getRange()
For convenience: getMax() - getMin()
Definition: cxImage.cpp:459
virtual void save(const QString &basePath)
Definition: cxImage.cpp:957
void setAcquisitionTime(QDateTime time)
Definition: cxData.cpp:181
void transferFunctionsChanged()
emitted when image transfer functions in 2D or 3D are changed.
Transform3D createTransformTranslate(const Vector3D &translation)
Representation of an integer bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
vtkSmartPointer< class vtkImageResample > vtkImageResamplePtr
void saveImage(ImagePtr image, const QString &filename)
vtkImageAccumulatePtr mHistogramPtr
Histogram.
Definition: cxImage.h:200
vtkImageDataPtr resample(long maxVoxels)
Definition: cxImage.cpp:909
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
Vector3D multiply_elems(const Vector3D &a, const Vector3D &b)
perform element-wise multiplication of a and b.
Definition: cxVector3D.cpp:52
virtual void setInteractiveClipPlane(vtkPlanePtr plane)
set a plane that is not saved
Definition: cxImage.cpp:789
virtual void setShading(Image::ShadingStruct shading)
Definition: cxImage.cpp:728
vtkImageDataPtr convertImageDataToGrayScale(vtkImageDataPtr image)
RegistrationHistoryPtr m_rMd_History
Definition: cxData.h:125
Superclass for all data objects.
Definition: cxData.h:69
std::map< int, int > IntIntMap
Definition: cxImage.h:56
void setDeepModified(vtkImageDataPtr image)
void readInto(DataPtr data, QString path)
virtual DoubleBoundingBox3D getCroppingBox() const
Definition: cxImage.cpp:759
std::vector< vtkPlanePtr > mPersistentClipPlanes
Definition: cxImage.h:209
virtual void setCroppingBox(const DoubleBoundingBox3D &bb_d)
Definition: cxImage.cpp:751
void cropBoxChanged()
int getVTKMaxValue()
Definition: cxImage.cpp:492
void vtkImageDataChanged()
emitted when the vktimagedata are invalidated and must be retrieved anew.
virtual vtkImageDataPtr get8bitGrayScaleVtkImageData()
Have never been used or tested. Create a test for it.
Definition: cxImage.cpp:299
REGISTRATION_STATUS mRegistrationStatus
Definition: cxData.h:124
virtual double getShadingAmbient()
Get shading ambient parmeter.
Definition: cxImage.cpp:703
virtual bool load(QString path)
Definition: cxImage.cpp:581
void stopThresholdPreview()
Definition: cxImage.cpp:1020
void setInterpolationType(int val)
Definition: cxImage.cpp:895
virtual QString getImageType() const
Definition: cxImage.cpp:846
void setInterpolationTypeToNearest()
Definition: cxImage.cpp:887
ImagePtr copy()
Definition: cxImage.cpp:143
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
virtual double getShadingSpecularPower()
Get shading specular power parmeter.
Definition: cxImage.cpp:718
Reader for metaheader .mhd files.
vtkImageDataPtr mBaseImageData
image data in data space
Definition: cxImage.h:195
int mInterpolationType
mirror the interpolationType in vtkVolumeProperty
Definition: cxImage.h:214
vtkSmartPointer< class vtkPlane > vtkPlanePtr
void setInterpolationTypeToLinear()
Definition: cxImage.cpp:891
virtual void clearPersistentClipPlanes()
Definition: cxImage.cpp:783
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr
virtual void setFilename(QString val)
Definition: cxData.cpp:92
virtual void transformChangedSlot()
Definition: cxImage.cpp:275
virtual ImageLUT2DPtr getLookupTable2D()
Definition: cxImage.cpp:336
virtual bool getShadingOn() const
Definition: cxImage.cpp:672
void resetTransferFunctions(bool _2D=true, bool _3D=true)
Resets the transfer functions and creates new default values.
Definition: cxImage.cpp:209
virtual DoubleBoundingBox3D boundingBox() const
bounding box in image space
Definition: cxImage.cpp:360
vtkImageDataPtr mBaseGrayScaleImageData
image data in data space
Definition: cxImage.h:196