CustusX  15.4.0-beta
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 <vtkImageShiftScale.h>
49 #include <vtkPiecewiseFunction.h>
50 #include <vtkColorTransferFunction.h>
51 #include "cxImageTF3D.h"
52 #include "cxBoundingBox3D.h"
53 #include "cxImageLUT2D.h"
55 #include "cxLandmark.h"
56 
57 #include "cxLogger.h"
58 #include "cxTypeConversions.h"
59 #include "cxUtilHelpers.h"
60 #include "cxVolumeHelpers.h"
62 #include "cxDataReaderWriter.h"
63 #include "cxNullDeleter.h"
64 
65 #include "cxUnsignedDerivedImage.h"
66 
67 typedef vtkSmartPointer<vtkImageChangeInformation> vtkImageChangeInformationPtr;
68 
69 namespace cx
70 {
71 
73 {
74  on = true;
75  ambient = 0.2;
76  diffuse = 0.9;
77  specular = 0.3;
78  specularPower = 15.0;
79 }
80 
81 double Image::ShadingStruct::loadAttribute(QDomNode dataNode, QString name, double defVal)
82 {
83  QString text = dataNode.toElement().attribute(name);
84  bool ok;
85  double val = text.toDouble(&ok);
86  if (ok)
87  return val;
88  return defVal;
89 }
90 
91 void Image::ShadingStruct::addXml(QDomNode dataNode)
92 {
93  QDomElement elem = dataNode.toElement();
94  elem.setAttribute("on", on);
95  elem.setAttribute("ambient", ambient);
96  elem.setAttribute("diffuse", diffuse);
97  elem.setAttribute("specular", specular);
98  elem.setAttribute("specularPower", specularPower);
99 }
100 
101 void Image::ShadingStruct::parseXml(QDomNode dataNode)
102 {
103  if (dataNode.isNull())
104  return;
105 
106  on = dataNode.toElement().attribute("on").toInt();
107  // std::cout << "attrib on: " << dataNode.toElement().attribute("on") << " : " << on << std::endl;
108  ambient = loadAttribute(dataNode, "ambient", ambient);
109  diffuse = loadAttribute(dataNode, "diffuse", diffuse);
110  specular = loadAttribute(dataNode, "specular", specular);
111  specularPower = loadAttribute(dataNode, "specularPower", specularPower);
112 }
113 
114 //---------------------------------------------------------
115 //---------------------------------------------------------
116 //---------------------------------------------------------
117 
118 ImagePtr Image::create(const QString& uid, const QString& name)
119 {
120  return ImagePtr(new Image(uid, vtkImageDataPtr(), name));
121 }
122 
124 {
125 }
126 
127 Image::Image(const QString& uid, const vtkImageDataPtr& data, const QString& name) :
128  Data(uid, name), mBaseImageData(data), mMaxRGBIntensity(-1), mThresholdPreview(false)
129 {
130  mInitialWindowWidth = -1;
131  mInitialWindowLevel = -1;
132 
133  mInterpolationType = VTK_LINEAR_INTERPOLATION;
134  mUseCropping = false;
135  mCroppingBox_d = this->getInitialBoundingBox();
136  mModality = "UNKNOWN";
137 
138  mImageLookupTable2D.reset();
139  mImageTransferFunctions3D.reset();
140 
141  this->setAcquisitionTime(QDateTime::currentDateTime());
142 }
143 
145 {
146  vtkImageDataPtr baseImageDataCopy;
147  if(mBaseImageData)
148  {
149  baseImageDataCopy = vtkImageDataPtr::New();
150  baseImageDataCopy->DeepCopy(mBaseImageData);
151  }
152 
153  ImagePtr retval = ImagePtr(new Image(mUid, baseImageDataCopy, mName));
154 
155  retval->mUnsigned = mUnsigned;
156  retval->mModality = mModality;
157  retval->mImageType = mImageType;
158  retval->mMaxRGBIntensity = mMaxRGBIntensity;
159  retval->mInterpolationType = mInterpolationType;
160  retval->mImageLookupTable2D = mImageLookupTable2D;
161  retval->mImageTransferFunctions3D = mImageTransferFunctions3D;
162  retval->mInitialWindowWidth = mInitialWindowWidth;
163  retval->mInitialWindowLevel = mInitialWindowLevel;
164 
165  //From cx::Data
166  retval->mRegistrationStatus = mRegistrationStatus;
167  retval->m_rMd_History = m_rMd_History;
168 
169  return retval;
170 }
171 
173 {
174  this->get_rMd_History()->setRegistration(parentImage->get_rMd());
175  this->get_rMd_History()->setParentSpace(parentImage->getUid());
176  ImageTF3DPtr transferFunctions = parentImage->getUnmodifiedTransferFunctions3D()->createCopy();
177  ImageLUT2DPtr LUT2D = parentImage->getUnmodifiedLookupTable2D()->createCopy();
178  this->setLookupTable2D(LUT2D);
179  this->setTransferFunctions3D(transferFunctions);
180  this->setModality(parentImage->getModality());
181  this->setImageType(parentImage->getImageType());
182  this->setShading(parentImage->getShading());
183  mInitialWindowWidth = parentImage->getInitialWindowWidth();
184  mInitialWindowLevel = parentImage->getInitialWindowLevel();
185 }
186 
187 DoubleBoundingBox3D Image::getInitialBoundingBox() const
188 {
189  return DoubleBoundingBox3D(-1, -1, -1, -1, -1, -1);
190 }
191 
193 {
194  CX_ASSERT(this==self.get());
195 
196  if (!mUnsigned)
197  {
198  // self is unsigned: return self
199  if (this->getBaseVtkImageData()->GetScalarTypeMin() >= 0)
200  return self;
201  else // signed: create unsigned adapter
203  }
204 
205  return mUnsigned;
206 }
207 
208 
209 
210 void Image::resetTransferFunctions(bool _2D, bool _3D)
211 {
212  if (!mBaseImageData)
213  {
214  reportWarning("Image has no image data");
215  return;
216  }
217 
218  mBaseImageData->GetScalarRange(); // this line updates some internal vtk value, and (on fedora) removes 4.5s in the second render().
219  mMaxRGBIntensity = -1;
220 
221  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
222  if (_3D)
223  {
224  this->resetTransferFunction(tfGenerator.generate3DTFPreset());
225  tfGenerator.resetShading();
226  }
227  if (_2D)
228  this->resetTransferFunction(tfGenerator.generate2DTFPreset());
229 }
230 
231 void Image::resetTransferFunction(ImageTF3DPtr imageTransferFunctions3D, ImageLUT2DPtr imageLookupTable2D)
232 {
233  this->blockSignals(true); // avoid emitting two transferFunctionsChanged() for one call.
234 
235  this->resetTransferFunction(imageTransferFunctions3D);
236  this->resetTransferFunction(imageLookupTable2D);
237 
238  this->blockSignals(false);
240 }
241 
242 void Image::resetTransferFunction(ImageLUT2DPtr imageLookupTable2D)
243 {
244  if (mImageLookupTable2D)
245  {
246  disconnect(mImageLookupTable2D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
247  }
248 
249  mImageLookupTable2D = imageLookupTable2D;
250 
251  if (mImageLookupTable2D)
252  {
253  connect(mImageLookupTable2D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
254  }
255 
257 }
258 
259 void Image::resetTransferFunction(ImageTF3DPtr imageTransferFunctions3D)
260 {
261  if (mImageTransferFunctions3D)
262  {
263  disconnect(mImageTransferFunctions3D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
264  }
265 
266  mImageTransferFunctions3D = imageTransferFunctions3D;
267 
268  if (mImageTransferFunctions3D)
269  {
270  connect(mImageTransferFunctions3D.get(), &ImageTFData::transferFunctionsChanged, this, &Image::transferFunctionsChanged);
271  }
272 
274 }
275 
277 {
278 }
279 
281 {
282  // important! move thread affinity to main thread - ensures signals/slots is still called correctly
283  this->moveToThread(thread);
284  this->getUnmodifiedTransferFunctions3D()->moveToThread(thread);
285  this->getUnmodifiedLookupTable2D()->moveToThread(thread);
286  this->get_rMd_History()->moveToThread(thread);
287 }
288 
289 void Image::setVtkImageData(const vtkImageDataPtr& data, bool resetTransferFunctions)
290 {
291  mBaseImageData = data;
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 
469 void Image::addXml(QDomNode& dataNode)
470 {
471  Data::addXml(dataNode);
472  QDomNode imageNode = dataNode;
473  QDomDocument doc = dataNode.ownerDocument();
474 
475  QDomElement tf3DNode = doc.createElement("transferfunctions");
476  this->getUnmodifiedTransferFunctions3D()->addXml(tf3DNode);
477  imageNode.appendChild(tf3DNode);
478 
479  QDomElement lut2DNode = doc.createElement("lookuptable2D");
480  this->getUnmodifiedLookupTable2D()->addXml(lut2DNode);
481  imageNode.appendChild(lut2DNode);
482 
483  QDomElement shadingNode = doc.createElement("shading");
484  mShading.addXml(shadingNode);
485  imageNode.appendChild(shadingNode);
486 
487 // QDomElement landmarksNode = doc.createElement("landmarks");
488 // mLandmarks->addXml(landmarksNode);
489 // imageNode.appendChild(landmarksNode);
490 
491  QDomElement cropNode = doc.createElement("crop");
492  cropNode.setAttribute("use", mUseCropping);
493  cropNode.appendChild(doc.createTextNode(qstring_cast(mCroppingBox_d)));
494  imageNode.appendChild(cropNode);
495 
496  QDomElement clipNode = doc.createElement("clip");
497  for (unsigned i = 0; i < mPersistentClipPlanes.size(); ++i)
498  {
499  QDomElement planeNode = doc.createElement("plane");
500  Vector3D normal(mPersistentClipPlanes[i]->GetNormal());
501  Vector3D origin(mPersistentClipPlanes[i]->GetOrigin());
502  planeNode.setAttribute("normal", qstring_cast(normal));
503  planeNode.setAttribute("origin", qstring_cast(origin));
504  clipNode.appendChild(planeNode);
505  }
506  imageNode.appendChild(clipNode);
507 
508  QDomElement modalityNode = doc.createElement("modality");
509  modalityNode.appendChild(doc.createTextNode(mModality));
510  imageNode.appendChild(modalityNode);
511 
512  QDomElement imageTypeNode = doc.createElement("imageType");
513  imageTypeNode.appendChild(doc.createTextNode(mImageType));
514  imageNode.appendChild(imageTypeNode);
515 
516  QDomElement interpolationNode = doc.createElement("vtk_interpolation");
517  interpolationNode.setAttribute("type", mInterpolationType);
518  imageNode.appendChild(interpolationNode);
519 
520  QDomElement initialWindowNode = doc.createElement("initialWindow");
521  initialWindowNode.setAttribute("width", mInitialWindowWidth);
522  initialWindowNode.setAttribute("level", mInitialWindowLevel);
523 }
524 
525 double Image::loadAttribute(QDomNode dataNode, QString name, double defVal)
526 {
527  QString text = dataNode.toElement().attribute(name);
528  bool ok;
529  double val = text.toDouble(&ok);
530  if (ok)
531  return val;
532  return defVal;
533 }
534 
535 bool Image::load(QString path)
536 {
537  ImagePtr self = ImagePtr(this, null_deleter());
538  DataReaderWriter().readInto(self, path);
539  return this->getBaseVtkImageData()!=0;
540 }
541 
542 void Image::parseXml(QDomNode& dataNode)
543 {
544  Data::parseXml(dataNode);
545 
546  // image node must be parsed in the data manager to create this Image object
547  // Only subnodes are parsed here
548 
549  if (dataNode.isNull())
550  return;
551 
552  //transferefunctions
553  QDomNode transferfunctionsNode = dataNode.namedItem("transferfunctions");
554  if (!transferfunctionsNode.isNull())
555  this->getUnmodifiedTransferFunctions3D()->parseXml(transferfunctionsNode);
556  else
557  {
558  std::cout << "Warning: Image::parseXml() found no transferfunctions";
559  std::cout << std::endl;
560  }
561 
562  mInitialWindowWidth = this->loadAttribute(dataNode.namedItem("initialWindow"), "width", -1);
563  mInitialWindowLevel = this->loadAttribute(dataNode.namedItem("initialWindow"), "level", -1);
564 
565  this->getUnmodifiedLookupTable2D()->parseXml(dataNode.namedItem("lookuptable2D"));
566 
567  // backward compatibility:
568  mShading.on = dataNode.namedItem("shading").toElement().text().toInt();
569  //Assign default values if the shading nodes don't exists to allow backward compability
570  if (!dataNode.namedItem("shadingAmbient").isNull())
571  mShading.ambient = dataNode.namedItem("shadingAmbient").toElement().text().toDouble();
572  if (!dataNode.namedItem("shadingDiffuse").isNull())
573  mShading.diffuse = dataNode.namedItem("shadingDiffuse").toElement().text().toDouble();
574  if (!dataNode.namedItem("shadingSpecular").isNull())
575  mShading.specular = dataNode.namedItem("shadingSpecular").toElement().text().toDouble();
576  if (!dataNode.namedItem("shadingSpecularPower").isNull())
577  mShading.specularPower = dataNode.namedItem("shadingSpecularPower").toElement().text().toDouble();
578 
579  // new way:
580  mShading.parseXml(dataNode.namedItem("shading"));
581 
582 // mLandmarks->parseXml(dataNode.namedItem("landmarks"));
583 
584  QDomElement cropNode = dataNode.namedItem("crop").toElement();
585  if (!cropNode.isNull())
586  {
587  mUseCropping = cropNode.attribute("use").toInt();
589  }
590 
591  QDomElement clipNode = dataNode.namedItem("clip").toElement();
592  QDomElement clipPlaneNode = clipNode.firstChildElement("plane");
593  for (; !clipPlaneNode.isNull(); clipPlaneNode = clipPlaneNode.nextSiblingElement("plane"))
594  {
595  Vector3D normal = Vector3D::fromString(clipPlaneNode.attribute("normal"));
596  Vector3D origin = Vector3D::fromString(clipPlaneNode.attribute("origin"));
597  vtkPlanePtr plane = vtkPlanePtr::New();
598  plane->SetNormal(normal.begin());
599  plane->SetOrigin(origin.begin());
600  mPersistentClipPlanes.push_back(plane);
601  }
602 
603  mModality = dataNode.namedItem("modality").toElement().text();
604  mImageType = dataNode.namedItem("imageType").toElement().text();
605 
606  QDomElement interpoationNode = dataNode.namedItem("vtk_interpolation").toElement();
607  if (!interpoationNode.isNull())
608  {
609  mInterpolationType = interpoationNode.attribute("type").toInt();
610  emit vtkImageDataChanged();
611  }
612 }
613 
614 void Image::setInitialWindowLevel(double width, double level)
615 {
616  mInitialWindowWidth = width;
617  mInitialWindowLevel = level;
618 }
619 
620 void Image::setShadingOn(bool on)
621 {
622  mShading.on = on;
624 }
625 
627 {
628  if (mThresholdPreview)
629  return true;
630  return mShading.on;
631 }
632 
633 void Image::setShadingAmbient(double ambient)
634 {
635  mShading.ambient = ambient;
637 }
638 
639 void Image::setShadingDiffuse(double diffuse)
640 {
641  mShading.diffuse = diffuse;
643 }
644 
645 void Image::setShadingSpecular(double specular)
646 {
647  mShading.specular = specular;
649 }
650 
651 void Image::setShadingSpecularPower(double specularPower)
652 {
653  mShading.specularPower = specularPower;
655 }
656 
658 {
659  return mShading.ambient;
660 }
661 
663 {
664  return mShading.diffuse;
665 }
666 
668 {
669  return mShading.specular;
670 }
671 
673 {
674  return mShading.specularPower;
675 }
676 
678 {
679  return mShading;
680 }
681 
683 {
684  mShading = shading;
686 }
687 
688 // methods for defining and storing a cropping box. Image does not use these data, this is up to the mapper
689 void Image::setCropping(bool on)
690 {
691  if (mUseCropping == on)
692  return;
693 
694  mUseCropping = on;
695  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
696  mCroppingBox_d = this->boundingBox();
697  emit cropBoxChanged();
698 }
699 
700 bool Image::getCropping() const
701 {
702  return mUseCropping;
703 }
704 
706 {
707  if (similar(mCroppingBox_d, bb_d))
708  return;
709  mCroppingBox_d = bb_d;
710  emit cropBoxChanged();
711 }
712 
714 {
715  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
716  return this->boundingBox();
717  return mCroppingBox_d;
718 }
719 
720 // methods for defining and storing clip planes. Image does not use these data, this is up to the mapper
722 {
723  if (std::count(mPersistentClipPlanes.begin(), mPersistentClipPlanes.end(), plane))
724  return;
725  mPersistentClipPlanes.push_back(plane);
726  emit clipPlanesChanged();
727 }
728 
729 std::vector<vtkPlanePtr> Image::getAllClipPlanes()
730 {
731  std::vector<vtkPlanePtr> retval = mPersistentClipPlanes;
733  retval.push_back(mInteractiveClipPlane);
734  return retval;
735 }
736 
738 {
739  mPersistentClipPlanes.clear();
740  emit clipPlanesChanged();
741 }
742 
744 {
745  mInteractiveClipPlane = plane;
746  emit clipPlanesChanged();
747 }
748 
762 {
763  // the internal CustusX format does not handle extents starting at non-zero.
764  // Move extent to zero and change rMd.
765  Vector3D origin(mBaseImageData->GetOrigin());
766  Vector3D spacing(mBaseImageData->GetSpacing());
767  IntBoundingBox3D extent(mBaseImageData->GetExtent());
768  Vector3D extentShift = multiply_elems(extent.corner(0, 0, 0).cast<double>(), spacing);
769 
770  vtkImageChangeInformationPtr info = vtkImageChangeInformationPtr::New();
771  info->SetInputData(mBaseImageData);
772  info->SetOutputExtentStart(0, 0, 0);
773  info->SetOutputOrigin(0, 0, 0);
774  info->Update();
775  info->UpdateInformation();
776  mBaseImageData = info->GetOutput();
777 
778  mBaseImageData->ComputeBounds();
779 // mBaseImageData->Update();
780 // mBaseImageData->UpdateInformation();
781 
782  this->get_rMd_History()->setRegistration(this->get_rMd() * createTransformTranslate(origin + extentShift));
783 
784  emit vtkImageDataChanged();
785  emit clipPlanesChanged();
786  emit cropBoxChanged();
787 }
788 
789 QString Image::getModality() const
790 {
791  return mModality;
792 }
793 
794 void Image::setModality(const QString& val)
795 {
796  mModality = val;
797  emit propertiesChanged();
798 }
799 
800 QString Image::getImageType() const
801 {
802  return mImageType;
803 }
804 
805 void Image::setImageType(const QString& val)
806 {
807  mImageType = val;
808  emit propertiesChanged();
809 }
810 
811 vtkImageDataPtr Image::createDummyImageData(int axisSize, int maxVoxelValue)
812 {
813  int size = axisSize - 1;//Modify axis size as extent starts with 0, not 1
814  vtkImageDataPtr dummyImageData = vtkImageDataPtr::New();
815  dummyImageData->SetExtent(0, size, 0, size, 0, size);
816  dummyImageData->SetSpacing(1, 1, 1);
817  //dummyImageData->SetScalarTypeToUnsignedShort();
818 // dummyImageData->SetScalarTypeToUnsignedChar();
819 // dummyImageData->SetNumberOfScalarComponents(1);
820 // dummyImageData->AllocateScalars();
821  dummyImageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
822  unsigned char* dataPtr = static_cast<unsigned char*> (dummyImageData->GetScalarPointer());
823 
824  //Init voxel colors
825  int minVoxelValue = 0;
826  int numVoxels = axisSize*axisSize*axisSize;
827  for (int i = 0; i < numVoxels; ++i)
828  {
829  int voxelValue = minVoxelValue + i;
830  if (i == numVoxels)
831  dataPtr[i] = maxVoxelValue;
832  else if (voxelValue < maxVoxelValue)
833  dataPtr[i] = voxelValue;
834  else
835  dataPtr[i] = maxVoxelValue;
836  }
837  setDeepModified(dummyImageData);
838  return dummyImageData;
839 }
840 
842 {
843  this->setInterpolationType(VTK_NEAREST_INTERPOLATION);
844 }
846 {
847  this->setInterpolationType(VTK_LINEAR_INTERPOLATION);
848 }
850 {
851  if (mThresholdPreview)
852  return;
853  mInterpolationType = val;
854  emit vtkImageDataChanged();
855 }
857 {
858  if (mThresholdPreview)
859  return VTK_NEAREST_INTERPOLATION;
860  return mInterpolationType;
861 }
862 
864 {
865  // also use grayscale as vtk is incapable of rendering 3component color.
866  vtkImageDataPtr retval = this->getGrayScaleVtkImageData();
867 
868  double factor = computeResampleFactor(maxVoxels);
869 
870  if (fabs(1.0-factor)>0.01) // resampling
871  {
872  vtkImageResamplePtr resampler = vtkImageResamplePtr::New();
873  resampler->SetInterpolationModeToLinear();
874  resampler->SetAxisMagnificationFactor(0, factor);
875  resampler->SetAxisMagnificationFactor(1, factor);
876  resampler->SetAxisMagnificationFactor(2, factor);
877  resampler->SetInputData(retval);
878 // resampler->GetOutput()->Update();
879  resampler->Update();
880  resampler->GetOutput()->GetScalarRange();
881  retval = resampler->GetOutput();
882 
883  long voxelsDown = retval->GetNumberOfPoints();
884  long voxelsOrig = this->getBaseVtkImageData()->GetNumberOfPoints();
885 // report("Created downsampled volume in Image: "
886 // + this->getName()
887 // + " below " + qstring_cast(voxelsDown/1000/1000) + "M. "
888 // + "Ratio: " + QString::number(factor, 'g', 2) + ", "
889 // + "Original size: " + qstring_cast(voxelsOrig/1000/1000) + "M.");
890  }
891  return retval;
892 }
893 
894 double Image::computeResampleFactor(long maxVoxels)
895 {
896  if (maxVoxels==0)
897  return 1.0;
898 
899  long voxels = this->getBaseVtkImageData()->GetNumberOfPoints();
900  double factor = (double)maxVoxels/(double)voxels;
901  factor = pow(factor, 1.0/3.0);
902  // cubic function leads to trouble for 138M-volume - must downsample to as low as 5-10 Mv in order to succeed on Mac.
903 
904  if (factor<0.99)
905  {
906  return factor;
907  }
908  return 1.0;
909 }
910 
911 void Image::save(const QString& basePath)
912 {
913  QString filename = basePath + "/Images/" + this->getUid() + ".mhd";
914  this->setFilename(QDir(basePath).relativeFilePath(filename));
915 
916  ImagePtr self = ImagePtr(this, null_deleter());
917  MetaImageReader().saveImage(self, filename);
918 }
919 
920 void Image::startThresholdPreview(const Eigen::Vector2d &threshold)
921 {
922  mThresholdPreview = true;
923 
924  this->createThresholdPreviewTransferFunctions3D(threshold);
925  this->createThresholdPreviewLookupTable2D(threshold);
926 
928 }
929 
930 void Image::createThresholdPreviewTransferFunctions3D(const Eigen::Vector2d &threshold)
931 {
932  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
933 
934  ColorMap colors = this->createPreviewColorMap(threshold);
935  IntIntMap opacity = this->createPreviewOpacityMap(threshold);
936 
937  mTresholdPreviewTransferfunctions3D = tfGenerator.generate3DTFPreset();
938  mTresholdPreviewTransferfunctions3D->resetColor(colors);
939  mTresholdPreviewTransferfunctions3D->resetAlpha(opacity);
940 }
941 
942 void Image::createThresholdPreviewLookupTable2D(const Eigen::Vector2d &threshold)
943 {
944  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
945 
946  ColorMap colors = this->createPreviewColorMap(threshold);
947 
948  mTresholdPreviewLookupTable2D = tfGenerator.generate2DTFPreset();
949  mTresholdPreviewLookupTable2D->resetColor(colors);
950  mTresholdPreviewLookupTable2D->setLLR(threshold[0]);
951 }
952 
953 ColorMap Image::createPreviewColorMap(const Eigen::Vector2d &threshold)
954 {
955  double lower = threshold[0];
956  ColorMap colors;
957  colors[lower] = Qt::green;
958  colors[this->getMax()] = Qt::green;
959  return colors;
960 }
961 
962 IntIntMap Image::createPreviewOpacityMap(const Eigen::Vector2d &threshold)
963 {
964  double lower = threshold[0];
965  double upper = threshold[1];
966  IntIntMap opacity;
967  opacity[lower - 1] = 0;
968  opacity[lower] = this->getMaxAlphaValue();
969  opacity[upper] = this->getMaxAlphaValue();
970  opacity[upper + 1] = 0;
971  return opacity;
972 }
973 
975 {
976  mThresholdPreview = false;
977  mTresholdPreviewTransferfunctions3D.reset();
978  mTresholdPreviewLookupTable2D.reset();
979 
980  //Need to tag these transfer functions as modified to tell the VTK pipeline that we got new TFs
981  this->getTransferFunctions3D()->getColorTF()->Modified();
982  this->getTransferFunctions3D()->getOpacityTF()->Modified();
983 
985 }
986 
987 } // namespace cx
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
Definition: cxImage.cpp:542
QString qstring_cast(const T &val)
void transferFunctionsChanged()
static vtkImageDataPtr createDummyImageData(int axisSize, int maxVoxelValue)
Create a moc object of vtkImageData.
Definition: cxImage.cpp:811
Image(const QString &uid, const vtkImageDataPtr &data, const QString &name="")
Definition: cxImage.cpp:127
ShadingStruct mShading
Definition: cxImage.h:203
virtual bool getCropping() const
Definition: cxImage.cpp:700
virtual void setModality(const QString &val)
Definition: cxImage.cpp:794
virtual Transform3D get_rMd() const
Definition: cxData.cpp:100
bool mUseCropping
image should be cropped using mCroppingBox
Definition: cxImage.h:205
virtual vtkImageDataPtr getGrayScaleVtkImageData()
as getBaseVtkImageData(), but constrained to 1 component if multicolor.
Definition: cxImage.cpp:306
int getInterpolationType() const
Definition: cxImage.cpp:856
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:210
void parseXml(QDomNode dataNode)
Definition: cxImage.cpp:101
virtual void setTransferFunctions3D(ImageTF3DPtr transferFuntion)
Definition: cxImage.cpp:331
PlainObject normal() const
virtual double getShadingDiffuse()
Get shading diffuse parmeter.
Definition: cxImage.cpp:662
virtual vtkImageAccumulatePtr getHistogram()
Definition: cxImage.cpp:372
void mergevtkSettingsIntosscTransform()
Definition: cxImage.cpp:761
static ImagePtr create(const QString &uid, const QString &name)
Definition: cxImage.cpp:118
void addXml(QDomNode dataNode)
Definition: cxImage.cpp:91
#define CX_ASSERT(statement)
Definition: cxLogger.h:128
virtual void setShadingDiffuse(double diffuse)
Set shading diffuse parmeter.
Definition: cxImage.cpp:639
virtual QString getModality() const
Definition: cxImage.cpp:789
virtual std::vector< vtkPlanePtr > getAllClipPlanes()
Definition: cxImage.cpp:729
virtual void addPersistentClipPlane(vtkPlanePtr plane)
Definition: cxImage.cpp:721
QString mUid
Definition: cxData.h:118
virtual void setVtkImageData(const vtkImageDataPtr &data, bool resetTransferFunctions=true)
Definition: cxImage.cpp:289
vtkSmartPointer< class vtkImageAccumulate > vtkImageAccumulatePtr
void propertiesChanged()
emitted when one of the metadata properties (uid, name etc) changes
virtual ~Image()
Definition: cxImage.cpp:123
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:614
vtkSmartPointer< vtkImageChangeInformation > vtkImageChangeInformationPtr
Definition: cxImage.cpp:67
double mMaxRGBIntensity
Definition: cxImage.h:211
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:689
virtual Image::ShadingStruct getShading()
Definition: cxImage.cpp:677
virtual void setShadingOn(bool on)
Definition: cxImage.cpp:620
DoubleBoundingBox3D mCroppingBox_d
box defining the cropping size.
Definition: cxImage.h:206
virtual void setShadingAmbient(double ambient)
Set shading ambient parmeter.
Definition: cxImage.cpp:633
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:667
virtual void setShadingSpecular(double specular)
Set shading specular parmeter.
Definition: cxImage.cpp:645
virtual void setImageType(const QString &val)
Definition: cxImage.cpp:805
ImagePtr mUnsigned
version of this containing unsigned data.
Definition: cxImage.h:199
virtual ImagePtr getUnsigned(ImagePtr self)
Definition: cxImage.cpp:192
virtual int getMin()
Definition: cxImage.cpp:450
virtual void intitializeFromParentImage(ImagePtr parentImage)
Definition: cxImage.cpp:172
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:469
virtual int getMax()
Definition: cxImage.cpp:414
virtual RegistrationHistoryPtr get_rMd_History()
Definition: cxData.cpp:105
vtkSmartPointer< class vtkImageChangeInformation > vtkImageChangeInformationPtr
std::map< int, QColor > ColorMap
Definition: cxImage.h:57
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
void startThresholdPreview(const Eigen::Vector2d &threshold)
Definition: cxImage.cpp:920
vtkPlanePtr mInteractiveClipPlane
Definition: cxImage.h:208
QString mModality
modality of the image, defined as DICOM tag (0008,0060), Section 3, C.7.3.1.1.1
Definition: cxImage.h:209
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:651
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:280
virtual int getRange()
For convenience: getMax() - getMin()
Definition: cxImage.cpp:459
virtual void save(const QString &basePath)
Definition: cxImage.cpp:911
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:198
vtkImageDataPtr resample(long maxVoxels)
Definition: cxImage.cpp:863
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:743
virtual void setShading(Image::ShadingStruct shading)
Definition: cxImage.cpp:682
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:713
std::vector< vtkPlanePtr > mPersistentClipPlanes
Definition: cxImage.h:207
virtual void setCroppingBox(const DoubleBoundingBox3D &bb_d)
Definition: cxImage.cpp:705
void cropBoxChanged()
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:657
virtual bool load(QString path)
Definition: cxImage.cpp:535
void stopThresholdPreview()
Definition: cxImage.cpp:974
void setInterpolationType(int val)
Definition: cxImage.cpp:849
virtual QString getImageType() const
Definition: cxImage.cpp:800
void setInterpolationTypeToNearest()
Definition: cxImage.cpp:841
ImagePtr copy()
Definition: cxImage.cpp:144
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
virtual double getShadingSpecularPower()
Get shading specular power parmeter.
Definition: cxImage.cpp:672
Reader for metaheader .mhd files.
vtkImageDataPtr mBaseImageData
image data in data space
Definition: cxImage.h:193
int mInterpolationType
mirror the interpolationType in vtkVolumeProperty
Definition: cxImage.h:212
vtkSmartPointer< class vtkPlane > vtkPlanePtr
void setInterpolationTypeToLinear()
Definition: cxImage.cpp:845
virtual void clearPersistentClipPlanes()
Definition: cxImage.cpp:737
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr
virtual void setFilename(QString val)
Definition: cxData.cpp:92
virtual void transformChangedSlot()
Definition: cxImage.cpp:276
virtual ImageLUT2DPtr getLookupTable2D()
Definition: cxImage.cpp:336
virtual bool getShadingOn() const
Definition: cxImage.cpp:626
void resetTransferFunctions(bool _2D=true, bool _3D=true)
Resets the transfer functions and creates new defaut values.
Definition: cxImage.cpp:210
virtual DoubleBoundingBox3D boundingBox() const
bounding box in image space
Definition: cxImage.cpp:360
vtkImageDataPtr mBaseGrayScaleImageData
image data in data space
Definition: cxImage.h:194