NorMIT-nav  18.04
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 
35 #include "cxLogger.h"
36 #include "cxTypeConversions.h"
37 #include "cxUtilHelpers.h"
38 #include "cxVolumeHelpers.h"
40 #include "cxDataReaderWriter.h"
41 #include "cxNullDeleter.h"
42 #include "cxSettings.h"
43 
44 #include "cxUnsignedDerivedImage.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();
115  mModality = "UNKNOWN";
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::addXml(QDomNode& dataNode)
504 {
505  Data::addXml(dataNode);
506  QDomNode imageNode = dataNode;
507  QDomDocument doc = dataNode.ownerDocument();
508 
509  QDomElement tf3DNode = doc.createElement("transferfunctions");
510  this->getUnmodifiedTransferFunctions3D()->addXml(tf3DNode);
511  imageNode.appendChild(tf3DNode);
512 
513  QDomElement lut2DNode = doc.createElement("lookuptable2D");
514  this->getUnmodifiedLookupTable2D()->addXml(lut2DNode);
515  imageNode.appendChild(lut2DNode);
516 
517  QDomElement shadingNode = doc.createElement("shading");
518  mShading.addXml(shadingNode);
519  imageNode.appendChild(shadingNode);
520 
521 // QDomElement landmarksNode = doc.createElement("landmarks");
522 // mLandmarks->addXml(landmarksNode);
523 // imageNode.appendChild(landmarksNode);
524 
525  QDomElement cropNode = doc.createElement("crop");
526  cropNode.setAttribute("use", mUseCropping);
527  cropNode.appendChild(doc.createTextNode(qstring_cast(mCroppingBox_d)));
528  imageNode.appendChild(cropNode);
529 
530  QDomElement clipNode = doc.createElement("clip");
531  for (unsigned i = 0; i < mPersistentClipPlanes.size(); ++i)
532  {
533  QDomElement planeNode = doc.createElement("plane");
534  Vector3D normal(mPersistentClipPlanes[i]->GetNormal());
535  Vector3D origin(mPersistentClipPlanes[i]->GetOrigin());
536  planeNode.setAttribute("normal", qstring_cast(normal));
537  planeNode.setAttribute("origin", qstring_cast(origin));
538  clipNode.appendChild(planeNode);
539  }
540  imageNode.appendChild(clipNode);
541 
542  QDomElement modalityNode = doc.createElement("modality");
543  modalityNode.appendChild(doc.createTextNode(mModality));
544  imageNode.appendChild(modalityNode);
545 
546  QDomElement imageTypeNode = doc.createElement("imageType");
547  imageTypeNode.appendChild(doc.createTextNode(mImageType));
548  imageNode.appendChild(imageTypeNode);
549 
550  QDomElement interpolationNode = doc.createElement("vtk_interpolation");
551  interpolationNode.setAttribute("type", mInterpolationType);
552  imageNode.appendChild(interpolationNode);
553 
554  QDomElement initialWindowNode = doc.createElement("initialWindow");
555  initialWindowNode.setAttribute("width", mInitialWindowWidth);
556  initialWindowNode.setAttribute("level", mInitialWindowLevel);
557  imageNode.appendChild(initialWindowNode);
558 }
559 
560 double Image::loadAttribute(QDomNode dataNode, QString name, double defVal)
561 {
562  QString text = dataNode.toElement().attribute(name);
563  bool ok;
564  double val = text.toDouble(&ok);
565  if (ok)
566  return val;
567  return defVal;
568 }
569 
570 bool Image::load(QString path)
571 {
572  ImagePtr self = ImagePtr(this, null_deleter());
573  DataReaderWriter().readInto(self, path);
574  return this->getBaseVtkImageData()!=0;
575 }
576 
577 void Image::parseXml(QDomNode& dataNode)
578 {
579  Data::parseXml(dataNode);
580 
581  // image node must be parsed in the data manager to create this Image object
582  // Only subnodes are parsed here
583 
584  if (dataNode.isNull())
585  return;
586 
587  //transferefunctions
588  QDomNode transferfunctionsNode = dataNode.namedItem("transferfunctions");
589  if (!transferfunctionsNode.isNull())
590  this->getUnmodifiedTransferFunctions3D()->parseXml(transferfunctionsNode);
591  else
592  {
593  std::cout << "Warning: Image::parseXml() found no transferfunctions";
594  std::cout << std::endl;
595  }
596 
597  mInitialWindowWidth = this->loadAttribute(dataNode.namedItem("initialWindow"), "width", mInitialWindowWidth);
598  mInitialWindowLevel = this->loadAttribute(dataNode.namedItem("initialWindow"), "level", mInitialWindowLevel);
599 
600  this->getUnmodifiedLookupTable2D()->parseXml(dataNode.namedItem("lookuptable2D"));
601 
602  // backward compatibility:
603  mShading.on = dataNode.namedItem("shading").toElement().text().toInt();
604  //Assign default values if the shading nodes don't exists to allow backward compability
605  if (!dataNode.namedItem("shadingAmbient").isNull())
606  mShading.ambient = dataNode.namedItem("shadingAmbient").toElement().text().toDouble();
607  if (!dataNode.namedItem("shadingDiffuse").isNull())
608  mShading.diffuse = dataNode.namedItem("shadingDiffuse").toElement().text().toDouble();
609  if (!dataNode.namedItem("shadingSpecular").isNull())
610  mShading.specular = dataNode.namedItem("shadingSpecular").toElement().text().toDouble();
611  if (!dataNode.namedItem("shadingSpecularPower").isNull())
612  mShading.specularPower = dataNode.namedItem("shadingSpecularPower").toElement().text().toDouble();
613 
614  // new way:
615  mShading.parseXml(dataNode.namedItem("shading"));
616 
617 // mLandmarks->parseXml(dataNode.namedItem("landmarks"));
618 
619  QDomElement cropNode = dataNode.namedItem("crop").toElement();
620  if (!cropNode.isNull())
621  {
622  mUseCropping = cropNode.attribute("use").toInt();
624  }
625 
626  QDomElement clipNode = dataNode.namedItem("clip").toElement();
627  QDomElement clipPlaneNode = clipNode.firstChildElement("plane");
628  for (; !clipPlaneNode.isNull(); clipPlaneNode = clipPlaneNode.nextSiblingElement("plane"))
629  {
630  Vector3D normal = Vector3D::fromString(clipPlaneNode.attribute("normal"));
631  Vector3D origin = Vector3D::fromString(clipPlaneNode.attribute("origin"));
632  vtkPlanePtr plane = vtkPlanePtr::New();
633  plane->SetNormal(normal.begin());
634  plane->SetOrigin(origin.begin());
635  mPersistentClipPlanes.push_back(plane);
636  }
637 
638  mModality = dataNode.namedItem("modality").toElement().text();
639  mImageType = dataNode.namedItem("imageType").toElement().text();
640 
641  QDomElement interpoationNode = dataNode.namedItem("vtk_interpolation").toElement();
642  if (!interpoationNode.isNull())
643  {
644  mInterpolationType = interpoationNode.attribute("type").toInt();
646  }
647 }
648 
649 void Image::setInitialWindowLevel(double width, double level)
650 {
651  mInitialWindowWidth = width;
652  mInitialWindowLevel = level;
653 }
654 
655 void Image::setShadingOn(bool on)
656 {
657  mShading.on = on;
659 }
660 
662 {
663  if (mThresholdPreview)
664  return true;
665  return mShading.on;
666 }
667 
668 void Image::setShadingAmbient(double ambient)
669 {
670  mShading.ambient = ambient;
672 }
673 
674 void Image::setShadingDiffuse(double diffuse)
675 {
676  mShading.diffuse = diffuse;
678 }
679 
680 void Image::setShadingSpecular(double specular)
681 {
682  mShading.specular = specular;
684 }
685 
686 void Image::setShadingSpecularPower(double specularPower)
687 {
688  mShading.specularPower = specularPower;
690 }
691 
693 {
694  return mShading.ambient;
695 }
696 
698 {
699  return mShading.diffuse;
700 }
701 
703 {
704  return mShading.specular;
705 }
706 
708 {
709  return mShading.specularPower;
710 }
711 
713 {
714  return mShading;
715 }
716 
718 {
719  mShading = shading;
721 }
722 
723 // methods for defining and storing a cropping box. Image does not use these data, this is up to the mapper
724 void Image::setCropping(bool on)
725 {
726  if (mUseCropping == on)
727  return;
728 
729  mUseCropping = on;
730  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
731  mCroppingBox_d = this->boundingBox();
732  emit cropBoxChanged();
733 }
734 
735 bool Image::getCropping() const
736 {
737  return mUseCropping;
738 }
739 
741 {
742  if (similar(mCroppingBox_d, bb_d))
743  return;
744  mCroppingBox_d = bb_d;
745  emit cropBoxChanged();
746 }
747 
749 {
750  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
751  return this->boundingBox();
752  return mCroppingBox_d;
753 }
754 
768 {
769  // the internal CustusX format does not handle extents starting at non-zero.
770  // Move extent to zero and change rMd.
771  Vector3D origin(mBaseImageData->GetOrigin());
772  Vector3D spacing(mBaseImageData->GetSpacing());
773  IntBoundingBox3D extent(mBaseImageData->GetExtent());
774  Vector3D extentShift = multiply_elems(extent.corner(0, 0, 0).cast<double>(), spacing);
775 
776  vtkImageChangeInformationPtr info = vtkImageChangeInformationPtr::New();
777  info->SetInputData(mBaseImageData);
778  info->SetOutputExtentStart(0, 0, 0);
779  info->SetOutputOrigin(0, 0, 0);
780  info->Update();
781  info->UpdateInformation();
782  mBaseImageData = info->GetOutput();
783 
784  mBaseImageData->ComputeBounds();
785 // mBaseImageData->Update();
786 // mBaseImageData->UpdateInformation();
787 
788  this->get_rMd_History()->setRegistration(this->get_rMd() * createTransformTranslate(origin + extentShift));
789 
791  emit clipPlanesChanged();
792  emit cropBoxChanged();
793 }
794 
795 QString Image::getModality() const
796 {
797  return mModality;
798 }
799 
800 void Image::setModality(const QString& val)
801 {
802  mModality = val;
803  emit propertiesChanged();
804 }
805 
806 QString Image::getImageType() const
807 {
808  return mImageType;
809 }
810 
811 void Image::setImageType(const QString& val)
812 {
813  mImageType = val;
814  emit propertiesChanged();
815 }
816 
817 vtkImageDataPtr Image::createDummyImageData(int axisSize, int maxVoxelValue)
818 {
819  int size = axisSize - 1;//Modify axis size as extent starts with 0, not 1
820  vtkImageDataPtr dummyImageData = vtkImageDataPtr::New();
821  dummyImageData->SetExtent(0, size, 0, size, 0, size);
822  dummyImageData->SetSpacing(1, 1, 1);
823  //dummyImageData->SetScalarTypeToUnsignedShort();
824 // dummyImageData->SetScalarTypeToUnsignedChar();
825 // dummyImageData->SetNumberOfScalarComponents(1);
826 // dummyImageData->AllocateScalars();
827  dummyImageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
828  unsigned char* dataPtr = static_cast<unsigned char*> (dummyImageData->GetScalarPointer());
829 
830  //Init voxel colors
831  int minVoxelValue = 0;
832  int numVoxels = axisSize*axisSize*axisSize;
833  for (int i = 0; i < numVoxels; ++i)
834  {
835  int voxelValue = minVoxelValue + i;
836  if (i == numVoxels)
837  dataPtr[i] = maxVoxelValue;
838  else if (voxelValue < maxVoxelValue)
839  dataPtr[i] = voxelValue;
840  else
841  dataPtr[i] = maxVoxelValue;
842  }
843  setDeepModified(dummyImageData);
844  return dummyImageData;
845 }
846 
847 //void Image::setInterpolationTypeToNearest()
848 //{
849 // this->setInterpolationType(VTK_NEAREST_INTERPOLATION);
850 //}
851 //void Image::setInterpolationTypeToLinear()
852 //{
853 // this->setInterpolationType(VTK_LINEAR_INTERPOLATION);
854 //}
855 
857 {
858  if (mThresholdPreview)
859  return;
860  mInterpolationType = val;
862 }
864 {
865  if (mThresholdPreview)
866  return VTK_NEAREST_INTERPOLATION;
867  return mInterpolationType;
868 }
869 
871 {
872  // also use grayscale as vtk is incapable of rendering 3component color.
873  vtkImageDataPtr retval = this->getGrayScaleVtkImageData();
874 
875  double factor = computeResampleFactor(maxVoxels);
876 
877  if (fabs(1.0-factor)>0.01) // resampling
878  {
879  vtkImageResamplePtr resampler = vtkImageResamplePtr::New();
880  resampler->SetInterpolationModeToLinear();
881  resampler->SetAxisMagnificationFactor(0, factor);
882  resampler->SetAxisMagnificationFactor(1, factor);
883  resampler->SetAxisMagnificationFactor(2, factor);
884  resampler->SetInputData(retval);
885 // resampler->GetOutput()->Update();
886  resampler->Update();
887  resampler->GetOutput()->GetScalarRange();
888  retval = resampler->GetOutput();
889 
890 // long voxelsDown = retval->GetNumberOfPoints();
891 // long voxelsOrig = this->getBaseVtkImageData()->GetNumberOfPoints();
892 // report("Created downsampled volume in Image: "
893 // + this->getName()
894 // + " below " + qstring_cast(voxelsDown/1000/1000) + "M. "
895 // + "Ratio: " + QString::number(factor, 'g', 2) + ", "
896 // + "Original size: " + qstring_cast(voxelsOrig/1000/1000) + "M.");
897  }
898  return retval;
899 }
900 
901 double Image::computeResampleFactor(long maxVoxels)
902 {
903  if (maxVoxels==0)
904  return 1.0;
905 
906  long voxels = this->getBaseVtkImageData()->GetNumberOfPoints();
907  double factor = (double)maxVoxels/(double)voxels;
908  factor = pow(factor, 1.0/3.0);
909  // cubic function leads to trouble for 138M-volume - must downsample to as low as 5-10 Mv in order to succeed on Mac.
910 
911  if (factor<0.99)
912  {
913  return factor;
914  }
915  return 1.0;
916 }
917 
918 void Image::save(const QString& basePath)
919 {
920  QString filename = basePath + "/Images/" + this->getUid() + ".mhd";
921  this->setFilename(QDir(basePath).relativeFilePath(filename));
922 
923  ImagePtr self = ImagePtr(this, null_deleter());
924  MetaImageReader().saveImage(self, filename);
925 }
926 
927 void Image::startThresholdPreview(const Eigen::Vector2d &threshold)
928 {
929  mThresholdPreview = true;
930 
931  this->createThresholdPreviewTransferFunctions3D(threshold);
932  this->createThresholdPreviewLookupTable2D(threshold);
933 
935 }
936 
937 void Image::createThresholdPreviewTransferFunctions3D(const Eigen::Vector2d &threshold)
938 {
939  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
940 
941  ColorMap colors = this->createPreviewColorMap(threshold);
942  IntIntMap opacity = this->createPreviewOpacityMap(threshold);
943 
944  mTresholdPreviewTransferfunctions3D = tfGenerator.generate3DTFPreset();
945  mTresholdPreviewTransferfunctions3D->resetColor(colors);
946  mTresholdPreviewTransferfunctions3D->resetAlpha(opacity);
947 }
948 
949 void Image::createThresholdPreviewLookupTable2D(const Eigen::Vector2d &threshold)
950 {
951  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
952 
953  ColorMap colors = this->createPreviewColorMap(threshold);
954 
955  mTresholdPreviewLookupTable2D = tfGenerator.generate2DTFPreset();
956  mTresholdPreviewLookupTable2D->resetColor(colors);
957  mTresholdPreviewLookupTable2D->setLLR(threshold[0]);
958 }
959 
960 ColorMap Image::createPreviewColorMap(const Eigen::Vector2d &threshold)
961 {
962  double lower = threshold[0];
963  ColorMap colors;
964  colors[lower] = Qt::green;
965  colors[this->getMax()] = Qt::green;
966  return colors;
967 }
968 
969 IntIntMap Image::createPreviewOpacityMap(const Eigen::Vector2d &threshold)
970 {
971  double lower = threshold[0];
972  double upper = threshold[1];
973  IntIntMap opacity;
974  opacity[lower - 1] = 0;
975  opacity[lower] = this->getMaxAlphaValue();
976  opacity[upper] = this->getMaxAlphaValue();
977  opacity[upper + 1] = 0;
978  return opacity;
979 }
980 
982 {
983  mThresholdPreview = false;
984  mTresholdPreviewTransferfunctions3D.reset();
985  mTresholdPreviewLookupTable2D.reset();
986 
987  //Need to tag these transfer functions as modified to tell the VTK pipeline that we got new TFs
988  this->getTransferFunctions3D()->getColorTF()->Modified();
989  this->getTransferFunctions3D()->getOpacityTF()->Modified();
990 
992 }
993 
994 } // namespace cx
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
Definition: cxImage.cpp:577
QString qstring_cast(const T &val)
void transferFunctionsChanged()
static vtkImageDataPtr createDummyImageData(int axisSize, int maxVoxelValue)
Create a moc object of vtkImageData.
Definition: cxImage.cpp:817
Image(const QString &uid, const vtkImageDataPtr &data, const QString &name="")
Definition: cxImage.cpp:106
ShadingStruct mShading
Definition: cxImage.h:180
bool is2D()
Definition: cxImage.cpp:498
virtual bool getCropping() const
Definition: cxImage.cpp:735
void reportError(QString msg)
Definition: cxLogger.cpp:71
virtual void setModality(const QString &val)
Definition: cxImage.cpp:800
virtual Transform3D get_rMd() const
Definition: cxData.cpp:86
bool mUseCropping
image should be cropped using mCroppingBox
Definition: cxImage.h:182
virtual vtkImageDataPtr getGrayScaleVtkImageData()
as getBaseVtkImageData(), but constrained to 1 component if multicolor.
Definition: cxImage.cpp:286
int getInterpolationType() const
Definition: cxImage.cpp:863
virtual Eigen::Array3d getSpacing() const
Definition: cxImage.cpp:347
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:186
void parseXml(QDomNode dataNode)
Definition: cxImage.cpp:80
virtual void setTransferFunctions3D(ImageTF3DPtr transferFuntion)
Definition: cxImage.cpp:311
PlainObject normal() const
virtual double getShadingDiffuse()
Get shading diffuse parmeter.
Definition: cxImage.cpp:697
virtual vtkImageAccumulatePtr getHistogram()
Definition: cxImage.cpp:352
void mergevtkSettingsIntosscTransform()
Definition: cxImage.cpp:767
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:674
virtual QString getModality() const
Definition: cxImage.cpp:795
QString mUid
Definition: cxData.h:152
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
virtual ~Image()
Definition: cxImage.cpp:102
virtual void setLookupTable2D(ImageLUT2DPtr imageLookupTable2D)
Definition: cxImage.cpp:330
QString mName
Definition: cxData.h:153
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
virtual void setInitialWindowLevel(double width, double level)
Definition: cxImage.cpp:649
vtkSmartPointer< vtkImageChangeInformation > vtkImageChangeInformationPtr
Definition: cxImage.cpp:46
double mMaxRGBIntensity
Definition: cxImage.h:187
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:724
virtual Image::ShadingStruct getShading()
Definition: cxImage.cpp:712
virtual void setShadingOn(bool on)
Definition: cxImage.cpp:655
DoubleBoundingBox3D mCroppingBox_d
box defining the cropping size.
Definition: cxImage.h:183
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:668
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:335
static ImagePtr create(ImagePtr base)
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:702
virtual void setShadingSpecular(double specular)
Set shading specular parmeter.
Definition: cxImage.cpp:680
virtual void setImageType(const QString &val)
Definition: cxImage.cpp:811
ImagePtr mUnsigned
version of this containing unsigned data.
Definition: cxImage.h:176
virtual ImagePtr getUnsigned(ImagePtr self)
Definition: cxImage.cpp:171
virtual int getMin()
Definition: cxImage.cpp:429
double getVTKMinValue()
Definition: cxImage.cpp:448
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:503
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
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
void startThresholdPreview(const Eigen::Vector2d &threshold)
Definition: cxImage.cpp:927
QString mModality
modality of the image, defined as DICOM tag (0008,0060), Section 3, C.7.3.1.1.1
Definition: cxImage.h:185
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 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:686
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
virtual int getRange()
For convenience: getMax() - getMin()
Definition: cxImage.cpp:438
virtual void save(const QString &basePath)
Definition: cxImage.cpp:918
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)
Read or write vtk or ssc data objects from/to file.
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
void saveImage(ImagePtr image, const QString &filename)
vtkImageAccumulatePtr mHistogramPtr
Histogram.
Definition: cxImage.h:175
vtkImageDataPtr resample(long maxVoxels)
Definition: cxImage.cpp:870
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:717
vtkImageDataPtr convertImageDataToGrayScale(vtkImageDataPtr image)
RegistrationHistoryPtr m_rMd_History
Definition: cxData.h:159
Superclass for all data objects.
Definition: cxData.h:88
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.
void readInto(DataPtr data, QString path)
virtual DoubleBoundingBox3D getCroppingBox() const
Definition: cxImage.cpp:748
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:740
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:158
virtual double getShadingAmbient()
Get shading ambient parmeter.
Definition: cxImage.cpp:692
virtual bool load(QString path)
Definition: cxImage.cpp:570
void stopThresholdPreview()
Definition: cxImage.cpp:981
void setInterpolationType(int val)
Definition: cxImage.cpp:856
virtual QString getImageType() const
Definition: cxImage.cpp:806
ImagePtr copy()
Definition: cxImage.cpp:123
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
virtual double getShadingSpecularPower()
Get shading specular power parmeter.
Definition: cxImage.cpp:707
Reader for metaheader .mhd files.
vtkImageDataPtr mBaseImageData
image data in data space
Definition: cxImage.h:170
int mInterpolationType
mirror the interpolationType in vtkVolumeProperty
Definition: cxImage.h:188
vtkSmartPointer< class vtkPlane > vtkPlanePtr
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:661
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:160
vtkImageDataPtr mBaseGrayScaleImageData
image data in data space
Definition: cxImage.h:171