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