CustusX  16.5.0-rc9
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxImage.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 
34 #include "cxImage.h"
35 
36 #include <QDomDocument>
37 #include <QDir>
38 #include <vtkImageAccumulate.h>
39 #include <vtkImageReslice.h>
40 #include <vtkImageData.h>
41 #include <vtkMatrix4x4.h>
42 #include <vtkPlane.h>
43 #include <vtkPlanes.h>
44 #include <vtkImageResample.h>
45 #include <vtkImageChangeInformation.h>
46 #include <vtkImageClip.h>
47 #include <vtkImageIterator.h>
48 #include <vtkPiecewiseFunction.h>
49 #include <vtkColorTransferFunction.h>
50 #include "cxImageTF3D.h"
51 #include "cxBoundingBox3D.h"
52 #include "cxImageLUT2D.h"
54 #include "cxLandmark.h"
55 
56 #include "cxLogger.h"
57 #include "cxTypeConversions.h"
58 #include "cxUtilHelpers.h"
59 #include "cxVolumeHelpers.h"
61 #include "cxDataReaderWriter.h"
62 #include "cxNullDeleter.h"
63 #include "cxSettings.h"
64 
65 #include "cxUnsignedDerivedImage.h"
66 
67 typedef vtkSmartPointer<vtkImageChangeInformation> vtkImageChangeInformationPtr;
68 
69 namespace cx
70 {
71 
73 {
74  on = settings()->value("View/shadingOn").value<bool>();
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  mHistogramPtr = NULL;
294 
295  if (resetTransferFunctions)
296  this->resetTransferFunctions();
297  emit vtkImageDataChanged();
298 }
299 
301 {
302  double windowWidth = this->getUnmodifiedLookupTable2D()->getWindow();
303  double windowLevel = this->getUnmodifiedLookupTable2D()->getLevel();
304  return convertImageDataTo8Bit(this->getGrayScaleVtkImageData(), windowWidth, windowLevel);
305 }
306 
308 {
310  {
312  }
313 
316 }
317 
319 {
320  if(mThresholdPreview)
321  return mTresholdPreviewTransferfunctions3D;
322  return getUnmodifiedTransferFunctions3D();
323 }
324 
325 ImageTF3DPtr Image::getUnmodifiedTransferFunctions3D()
326 {
327  if(!this->mImageTransferFunctions3D)
328  this->resetTransferFunctions(false, true);
329  return mImageTransferFunctions3D;
330 }
331 
333 {
334  this->resetTransferFunction(transferFuntion);
335 }
336 
338 {
339  if(mThresholdPreview)
340  return mTresholdPreviewLookupTable2D;
341  return getUnmodifiedLookupTable2D();
342 }
343 
344 ImageLUT2DPtr Image::getUnmodifiedLookupTable2D()
345 {
346  if(!mImageLookupTable2D)
347  this->resetTransferFunctions(true, false);
348  return mImageLookupTable2D;
349 }
350 
351 void Image::setLookupTable2D(ImageLUT2DPtr imageLookupTable2D)
352 {
353  this->resetTransferFunction(imageLookupTable2D);
354 }
355 
357 {
358  return mBaseImageData;
359 }
360 
362 {
363 // mBaseImageData->UpdateInformation();
364  DoubleBoundingBox3D bounds(mBaseImageData->GetBounds());
365  return bounds;
366 }
367 
368 Eigen::Array3d Image::getSpacing() const
369 {
370  return Eigen::Array3d(mBaseImageData->GetSpacing());
371 }
372 
374 {
375  if (mHistogramPtr.GetPointer() == NULL)
376  {
377  mHistogramPtr = vtkImageAccumulatePtr::New();
378  mHistogramPtr->SetInputData(this->getGrayScaleVtkImageData());
379  mHistogramPtr->IgnoreZeroOn(); // required for Sonowand CT volumes, where data are placed between 31K and 35K.
380  // Set up only a 1D histogram for now, so y and z values are set to 0
381  mHistogramPtr->SetComponentExtent(0, this->getRange(), 0, 0, 0, 0);
382  mHistogramPtr->SetComponentOrigin(this->getMin(), 0, 0);
383  mHistogramPtr->SetComponentSpacing(1, 0, 0);
384  }
385  mHistogramPtr->Update();
386  return mHistogramPtr;
387 }
388 
389 template<typename scalartype> static int getRGBMax(vtkImageDataPtr image)
390 {
391  int max = 0;
392  vtkImageIterator<scalartype> iter(image, image->GetExtent());
393  while (!iter.IsAtEnd())
394  {
395  typename vtkImageIterator<scalartype>::SpanIterator siter = iter.BeginSpan();
396  while (siter != iter.EndSpan())
397  {
398  int value = *siter;
399  ++siter;
400  value += *siter;
401  ++siter;
402  value += *siter;
403  ++siter;
404  if (value > max)
405  {
406  max = value;
407  }
408  }
409  iter.NextSpan();
410  }
411  return max/3;
412 }
413 
414 
416 {
417  // Alternatively create max from histogram
418  //IntIntMap::iterator iter = this->getHistogram()->end();
419  //iter--;
420  //return (*iter).first;
421  if (mBaseImageData->GetNumberOfScalarComponents() == 3)
422  {
423  if (mMaxRGBIntensity != -1)
424  {
425  return mMaxRGBIntensity;
426  }
427  double max = 0.0;
428  switch (mBaseImageData->GetScalarType())
429  {
430  case VTK_UNSIGNED_CHAR:
431  max = getRGBMax<unsigned char>(mBaseImageData);
432  break;
433  case VTK_UNSIGNED_SHORT:
434  max = getRGBMax<unsigned short>(mBaseImageData);
435  break;
436  default:
437  CX_LOG_ERROR() << "Unhandled RGB data type in image " << this->getUid();
438  break;
439  }
440  mMaxRGBIntensity = max;
441  return (int)mMaxRGBIntensity;
442  }
443  else
444  {
445 // return (int) this->getTransferFunctions3D()->getScalarMax();
446  return mBaseImageData->GetScalarRange()[1];
447  }
448 }
449 
451 {
452  // Alternatively create min from histogram
453  //IntIntMap::iterator iter = this->getHistogram()->begin();
454  //return (*iter).first;
455  return mBaseImageData->GetScalarRange()[0];
456 // return (int) this->getTransferFunctions3D()->getScalarMin();
457 }
458 
460 {
461  return this->getMax() - this->getMin();
462 }
463 
465 {
466  return 255;
467 }
468 
470 {
471  int vtkScalarType = mBaseImageData->GetScalarType();
472 
473  if (vtkScalarType==VTK_CHAR)
474  return VTK_CHAR_MIN;
475  else if (vtkScalarType==VTK_UNSIGNED_CHAR)
476  return VTK_UNSIGNED_CHAR_MIN;
477  else if (vtkScalarType==VTK_SIGNED_CHAR)
478  return VTK_SIGNED_CHAR_MIN;
479  else if (vtkScalarType==VTK_UNSIGNED_SHORT)
480  return VTK_UNSIGNED_SHORT_MIN;
481  else if (vtkScalarType==VTK_SHORT)
482  return VTK_SHORT_MIN;
483  else if (vtkScalarType==VTK_UNSIGNED_INT)
484  return VTK_UNSIGNED_INT_MIN;
485  else if (vtkScalarType==VTK_INT)
486  return VTK_INT_MIN;
487  else
488  reportError(QString("Unknown VTK ScalarType: %1").arg(vtkScalarType));
489  return 0;
490 }
491 
493 {
494  int vtkScalarType = mBaseImageData->GetScalarType();
495 
496  if (vtkScalarType==VTK_CHAR)
497  return VTK_CHAR_MAX;
498  else if (vtkScalarType==VTK_UNSIGNED_CHAR)
499  return VTK_UNSIGNED_CHAR_MAX;
500  else if (vtkScalarType==VTK_SIGNED_CHAR)
501  return VTK_SIGNED_CHAR_MAX;
502  else if (vtkScalarType==VTK_UNSIGNED_SHORT)
503  return VTK_UNSIGNED_SHORT_MAX;
504  else if (vtkScalarType==VTK_SHORT)
505  return VTK_SHORT_MAX;
506  else if (vtkScalarType==VTK_UNSIGNED_INT)
507  return VTK_UNSIGNED_INT_MAX;
508  else if (vtkScalarType==VTK_INT)
509  return VTK_INT_MAX;
510  else
511  reportError(QString("Unknown VTK ScalarType: %1").arg(vtkScalarType));
512  return 0;
513 }
514 
515 void Image::addXml(QDomNode& dataNode)
516 {
517  Data::addXml(dataNode);
518  QDomNode imageNode = dataNode;
519  QDomDocument doc = dataNode.ownerDocument();
520 
521  QDomElement tf3DNode = doc.createElement("transferfunctions");
522  this->getUnmodifiedTransferFunctions3D()->addXml(tf3DNode);
523  imageNode.appendChild(tf3DNode);
524 
525  QDomElement lut2DNode = doc.createElement("lookuptable2D");
526  this->getUnmodifiedLookupTable2D()->addXml(lut2DNode);
527  imageNode.appendChild(lut2DNode);
528 
529  QDomElement shadingNode = doc.createElement("shading");
530  mShading.addXml(shadingNode);
531  imageNode.appendChild(shadingNode);
532 
533 // QDomElement landmarksNode = doc.createElement("landmarks");
534 // mLandmarks->addXml(landmarksNode);
535 // imageNode.appendChild(landmarksNode);
536 
537  QDomElement cropNode = doc.createElement("crop");
538  cropNode.setAttribute("use", mUseCropping);
539  cropNode.appendChild(doc.createTextNode(qstring_cast(mCroppingBox_d)));
540  imageNode.appendChild(cropNode);
541 
542  QDomElement clipNode = doc.createElement("clip");
543  for (unsigned i = 0; i < mPersistentClipPlanes.size(); ++i)
544  {
545  QDomElement planeNode = doc.createElement("plane");
546  Vector3D normal(mPersistentClipPlanes[i]->GetNormal());
547  Vector3D origin(mPersistentClipPlanes[i]->GetOrigin());
548  planeNode.setAttribute("normal", qstring_cast(normal));
549  planeNode.setAttribute("origin", qstring_cast(origin));
550  clipNode.appendChild(planeNode);
551  }
552  imageNode.appendChild(clipNode);
553 
554  QDomElement modalityNode = doc.createElement("modality");
555  modalityNode.appendChild(doc.createTextNode(mModality));
556  imageNode.appendChild(modalityNode);
557 
558  QDomElement imageTypeNode = doc.createElement("imageType");
559  imageTypeNode.appendChild(doc.createTextNode(mImageType));
560  imageNode.appendChild(imageTypeNode);
561 
562  QDomElement interpolationNode = doc.createElement("vtk_interpolation");
563  interpolationNode.setAttribute("type", mInterpolationType);
564  imageNode.appendChild(interpolationNode);
565 
566  QDomElement initialWindowNode = doc.createElement("initialWindow");
567  initialWindowNode.setAttribute("width", mInitialWindowWidth);
568  initialWindowNode.setAttribute("level", mInitialWindowLevel);
569  imageNode.appendChild(initialWindowNode);
570 }
571 
572 double Image::loadAttribute(QDomNode dataNode, QString name, double defVal)
573 {
574  QString text = dataNode.toElement().attribute(name);
575  bool ok;
576  double val = text.toDouble(&ok);
577  if (ok)
578  return val;
579  return defVal;
580 }
581 
582 bool Image::load(QString path)
583 {
584  ImagePtr self = ImagePtr(this, null_deleter());
585  DataReaderWriter().readInto(self, path);
586  return this->getBaseVtkImageData()!=0;
587 }
588 
589 void Image::parseXml(QDomNode& dataNode)
590 {
591  Data::parseXml(dataNode);
592 
593  // image node must be parsed in the data manager to create this Image object
594  // Only subnodes are parsed here
595 
596  if (dataNode.isNull())
597  return;
598 
599  //transferefunctions
600  QDomNode transferfunctionsNode = dataNode.namedItem("transferfunctions");
601  if (!transferfunctionsNode.isNull())
602  this->getUnmodifiedTransferFunctions3D()->parseXml(transferfunctionsNode);
603  else
604  {
605  std::cout << "Warning: Image::parseXml() found no transferfunctions";
606  std::cout << std::endl;
607  }
608 
609  mInitialWindowWidth = this->loadAttribute(dataNode.namedItem("initialWindow"), "width", mInitialWindowWidth);
610  mInitialWindowLevel = this->loadAttribute(dataNode.namedItem("initialWindow"), "level", mInitialWindowLevel);
611 
612  this->getUnmodifiedLookupTable2D()->parseXml(dataNode.namedItem("lookuptable2D"));
613 
614  // backward compatibility:
615  mShading.on = dataNode.namedItem("shading").toElement().text().toInt();
616  //Assign default values if the shading nodes don't exists to allow backward compability
617  if (!dataNode.namedItem("shadingAmbient").isNull())
618  mShading.ambient = dataNode.namedItem("shadingAmbient").toElement().text().toDouble();
619  if (!dataNode.namedItem("shadingDiffuse").isNull())
620  mShading.diffuse = dataNode.namedItem("shadingDiffuse").toElement().text().toDouble();
621  if (!dataNode.namedItem("shadingSpecular").isNull())
622  mShading.specular = dataNode.namedItem("shadingSpecular").toElement().text().toDouble();
623  if (!dataNode.namedItem("shadingSpecularPower").isNull())
624  mShading.specularPower = dataNode.namedItem("shadingSpecularPower").toElement().text().toDouble();
625 
626  // new way:
627  mShading.parseXml(dataNode.namedItem("shading"));
628 
629 // mLandmarks->parseXml(dataNode.namedItem("landmarks"));
630 
631  QDomElement cropNode = dataNode.namedItem("crop").toElement();
632  if (!cropNode.isNull())
633  {
634  mUseCropping = cropNode.attribute("use").toInt();
636  }
637 
638  QDomElement clipNode = dataNode.namedItem("clip").toElement();
639  QDomElement clipPlaneNode = clipNode.firstChildElement("plane");
640  for (; !clipPlaneNode.isNull(); clipPlaneNode = clipPlaneNode.nextSiblingElement("plane"))
641  {
642  Vector3D normal = Vector3D::fromString(clipPlaneNode.attribute("normal"));
643  Vector3D origin = Vector3D::fromString(clipPlaneNode.attribute("origin"));
644  vtkPlanePtr plane = vtkPlanePtr::New();
645  plane->SetNormal(normal.begin());
646  plane->SetOrigin(origin.begin());
647  mPersistentClipPlanes.push_back(plane);
648  }
649 
650  mModality = dataNode.namedItem("modality").toElement().text();
651  mImageType = dataNode.namedItem("imageType").toElement().text();
652 
653  QDomElement interpoationNode = dataNode.namedItem("vtk_interpolation").toElement();
654  if (!interpoationNode.isNull())
655  {
656  mInterpolationType = interpoationNode.attribute("type").toInt();
657  emit vtkImageDataChanged();
658  }
659 }
660 
661 void Image::setInitialWindowLevel(double width, double level)
662 {
663  mInitialWindowWidth = width;
664  mInitialWindowLevel = level;
665 }
666 
667 void Image::setShadingOn(bool on)
668 {
669  mShading.on = on;
671 }
672 
674 {
675  if (mThresholdPreview)
676  return true;
677  return mShading.on;
678 }
679 
680 void Image::setShadingAmbient(double ambient)
681 {
682  mShading.ambient = ambient;
684 }
685 
686 void Image::setShadingDiffuse(double diffuse)
687 {
688  mShading.diffuse = diffuse;
690 }
691 
692 void Image::setShadingSpecular(double specular)
693 {
694  mShading.specular = specular;
696 }
697 
698 void Image::setShadingSpecularPower(double specularPower)
699 {
700  mShading.specularPower = specularPower;
702 }
703 
705 {
706  return mShading.ambient;
707 }
708 
710 {
711  return mShading.diffuse;
712 }
713 
715 {
716  return mShading.specular;
717 }
718 
720 {
721  return mShading.specularPower;
722 }
723 
725 {
726  return mShading;
727 }
728 
730 {
731  mShading = shading;
733 }
734 
735 // methods for defining and storing a cropping box. Image does not use these data, this is up to the mapper
736 void Image::setCropping(bool on)
737 {
738  if (mUseCropping == on)
739  return;
740 
741  mUseCropping = on;
742  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
743  mCroppingBox_d = this->boundingBox();
744  emit cropBoxChanged();
745 }
746 
747 bool Image::getCropping() const
748 {
749  return mUseCropping;
750 }
751 
753 {
754  if (similar(mCroppingBox_d, bb_d))
755  return;
756  mCroppingBox_d = bb_d;
757  emit cropBoxChanged();
758 }
759 
761 {
762  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
763  return this->boundingBox();
764  return mCroppingBox_d;
765 }
766 
780 {
781  // the internal CustusX format does not handle extents starting at non-zero.
782  // Move extent to zero and change rMd.
783  Vector3D origin(mBaseImageData->GetOrigin());
784  Vector3D spacing(mBaseImageData->GetSpacing());
785  IntBoundingBox3D extent(mBaseImageData->GetExtent());
786  Vector3D extentShift = multiply_elems(extent.corner(0, 0, 0).cast<double>(), spacing);
787 
788  vtkImageChangeInformationPtr info = vtkImageChangeInformationPtr::New();
789  info->SetInputData(mBaseImageData);
790  info->SetOutputExtentStart(0, 0, 0);
791  info->SetOutputOrigin(0, 0, 0);
792  info->Update();
793  info->UpdateInformation();
794  mBaseImageData = info->GetOutput();
795 
796  mBaseImageData->ComputeBounds();
797 // mBaseImageData->Update();
798 // mBaseImageData->UpdateInformation();
799 
800  this->get_rMd_History()->setRegistration(this->get_rMd() * createTransformTranslate(origin + extentShift));
801 
802  emit vtkImageDataChanged();
803  emit clipPlanesChanged();
804  emit cropBoxChanged();
805 }
806 
807 QString Image::getModality() const
808 {
809  return mModality;
810 }
811 
812 void Image::setModality(const QString& val)
813 {
814  mModality = val;
815  emit propertiesChanged();
816 }
817 
818 QString Image::getImageType() const
819 {
820  return mImageType;
821 }
822 
823 void Image::setImageType(const QString& val)
824 {
825  mImageType = val;
826  emit propertiesChanged();
827 }
828 
829 vtkImageDataPtr Image::createDummyImageData(int axisSize, int maxVoxelValue)
830 {
831  int size = axisSize - 1;//Modify axis size as extent starts with 0, not 1
832  vtkImageDataPtr dummyImageData = vtkImageDataPtr::New();
833  dummyImageData->SetExtent(0, size, 0, size, 0, size);
834  dummyImageData->SetSpacing(1, 1, 1);
835  //dummyImageData->SetScalarTypeToUnsignedShort();
836 // dummyImageData->SetScalarTypeToUnsignedChar();
837 // dummyImageData->SetNumberOfScalarComponents(1);
838 // dummyImageData->AllocateScalars();
839  dummyImageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
840  unsigned char* dataPtr = static_cast<unsigned char*> (dummyImageData->GetScalarPointer());
841 
842  //Init voxel colors
843  int minVoxelValue = 0;
844  int numVoxels = axisSize*axisSize*axisSize;
845  for (int i = 0; i < numVoxels; ++i)
846  {
847  int voxelValue = minVoxelValue + i;
848  if (i == numVoxels)
849  dataPtr[i] = maxVoxelValue;
850  else if (voxelValue < maxVoxelValue)
851  dataPtr[i] = voxelValue;
852  else
853  dataPtr[i] = maxVoxelValue;
854  }
855  setDeepModified(dummyImageData);
856  return dummyImageData;
857 }
858 
859 //void Image::setInterpolationTypeToNearest()
860 //{
861 // this->setInterpolationType(VTK_NEAREST_INTERPOLATION);
862 //}
863 //void Image::setInterpolationTypeToLinear()
864 //{
865 // this->setInterpolationType(VTK_LINEAR_INTERPOLATION);
866 //}
867 
869 {
870  if (mThresholdPreview)
871  return;
872  mInterpolationType = val;
873  emit vtkImageDataChanged();
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)
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  MetaImageReader().saveImage(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:589
QString qstring_cast(const T &val)
void transferFunctionsChanged()
static vtkImageDataPtr createDummyImageData(int axisSize, int maxVoxelValue)
Create a moc object of vtkImageData.
Definition: cxImage.cpp:829
Image(const QString &uid, const vtkImageDataPtr &data, const QString &name="")
Definition: cxImage.cpp:127
ShadingStruct mShading
Definition: cxImage.h:199
virtual bool getCropping() const
Definition: cxImage.cpp:747
void reportError(QString msg)
Definition: cxLogger.cpp:92
virtual void setModality(const QString &val)
Definition: cxImage.cpp:812
virtual Transform3D get_rMd() const
Definition: cxData.cpp:106
bool mUseCropping
image should be cropped using mCroppingBox
Definition: cxImage.h:201
virtual vtkImageDataPtr getGrayScaleVtkImageData()
as getBaseVtkImageData(), but constrained to 1 component if multicolor.
Definition: cxImage.cpp:307
int getInterpolationType() const
Definition: cxImage.cpp:875
virtual Eigen::Array3d getSpacing() const
Definition: cxImage.cpp:368
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:205
void parseXml(QDomNode dataNode)
Definition: cxImage.cpp:101
virtual void setTransferFunctions3D(ImageTF3DPtr transferFuntion)
Definition: cxImage.cpp:332
PlainObject normal() const
virtual double getShadingDiffuse()
Get shading diffuse parmeter.
Definition: cxImage.cpp:709
virtual vtkImageAccumulatePtr getHistogram()
Definition: cxImage.cpp:373
void mergevtkSettingsIntosscTransform()
Definition: cxImage.cpp:779
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:131
virtual void setShadingDiffuse(double diffuse)
Set shading diffuse parmeter.
Definition: cxImage.cpp:686
virtual QString getModality() const
Definition: cxImage.cpp:807
QString mUid
Definition: cxData.h:171
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:351
QString mName
Definition: cxData.h:172
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
virtual void setInitialWindowLevel(double width, double level)
Definition: cxImage.cpp:661
vtkSmartPointer< vtkImageChangeInformation > vtkImageChangeInformationPtr
Definition: cxImage.cpp:67
double mMaxRGBIntensity
Definition: cxImage.h:206
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:736
virtual Image::ShadingStruct getShading()
Definition: cxImage.cpp:724
virtual void setShadingOn(bool on)
Definition: cxImage.cpp:667
DoubleBoundingBox3D mCroppingBox_d
box defining the cropping size.
Definition: cxImage.h:202
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:87
virtual void setShadingAmbient(double ambient)
Set shading ambient parmeter.
Definition: cxImage.cpp:680
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:356
static ImagePtr create(ImagePtr base)
virtual void addXml(QDomNode &dataNode)
adds xml information about the data and its variabels
Definition: cxData.cpp:126
virtual double getShadingSpecular()
Get shading specular parmeter.
Definition: cxImage.cpp:714
virtual void setShadingSpecular(double specular)
Set shading specular parmeter.
Definition: cxImage.cpp:692
virtual void setImageType(const QString &val)
Definition: cxImage.cpp:823
ImagePtr mUnsigned
version of this containing unsigned data.
Definition: cxImage.h:195
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:84
void clipPlanesChanged()
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
virtual ImageTF3DPtr getTransferFunctions3D()
Definition: cxImage.cpp:318
void addXml(QDomNode &dataNode)
adds xml information about the image and its variabels
Definition: cxImage.cpp:515
virtual int getMax()
Definition: cxImage.cpp:415
virtual RegistrationHistoryPtr get_rMd_History()
Definition: cxData.cpp:111
std::map< int, QColor > ColorMap
Definition: cxImage.h:57
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
void startThresholdPreview(const Eigen::Vector2d &threshold)
Definition: cxImage.cpp:939
QString mModality
modality of the image, defined as DICOM tag (0008,0060), Section 3, C.7.3.1.1.1
Definition: cxImage.h:204
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:114
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:152
virtual void setShadingSpecularPower(double specularPower)
Set shading specular power parmeter.
Definition: cxImage.cpp:698
int getVTKMinValue()
Definition: cxImage.cpp:469
void moveThisAndChildrenToThread(QThread *thread)
Move this and all children to thread. Use the thread is generated in a worker thread and the result i...
Definition: cxImage.cpp:280
virtual int getRange()
For convenience: getMax() - getMin()
Definition: cxImage.cpp:459
virtual void save(const QString &basePath)
Definition: cxImage.cpp:930
void setAcquisitionTime(QDateTime time)
Definition: cxData.cpp:192
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:42
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:194
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:63
Vector3D multiply_elems(const Vector3D &a, const Vector3D &b)
perform element-wise multiplication of a and b.
Definition: cxVector3D.cpp:52
virtual void setShading(Image::ShadingStruct shading)
Definition: cxImage.cpp:729
vtkImageDataPtr convertImageDataToGrayScale(vtkImageDataPtr image)
RegistrationHistoryPtr m_rMd_History
Definition: cxData.h:178
Superclass for all data objects.
Definition: cxData.h:109
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:760
virtual void setCroppingBox(const DoubleBoundingBox3D &bb_d)
Definition: cxImage.cpp:752
void cropBoxChanged()
int getVTKMaxValue()
Definition: cxImage.cpp:492
void vtkImageDataChanged()
emitted when the vktimagedata are invalidated and must be retrieved anew.
virtual vtkImageDataPtr get8bitGrayScaleVtkImageData()
Have never been used or tested. Create a test for it.
Definition: cxImage.cpp:300
REGISTRATION_STATUS mRegistrationStatus
Definition: cxData.h:177
virtual double getShadingAmbient()
Get shading ambient parmeter.
Definition: cxImage.cpp:704
virtual bool load(QString path)
Definition: cxImage.cpp:582
void stopThresholdPreview()
Definition: cxImage.cpp:993
void setInterpolationType(int val)
Definition: cxImage.cpp:868
virtual QString getImageType() const
Definition: cxImage.cpp:818
ImagePtr copy()
Definition: cxImage.cpp:144
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
virtual double getShadingSpecularPower()
Get shading specular power parmeter.
Definition: cxImage.cpp:719
Reader for metaheader .mhd files.
vtkImageDataPtr mBaseImageData
image data in data space
Definition: cxImage.h:189
int mInterpolationType
mirror the interpolationType in vtkVolumeProperty
Definition: cxImage.h:207
vtkSmartPointer< class vtkPlane > vtkPlanePtr
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr
virtual void setFilename(QString val)
Definition: cxData.cpp:98
virtual void transformChangedSlot()
Definition: cxImage.cpp:276
virtual ImageLUT2DPtr getLookupTable2D()
Definition: cxImage.cpp:337
virtual bool getShadingOn() const
Definition: cxImage.cpp:673
void resetTransferFunctions(bool _2D=true, bool _3D=true)
Resets the transfer functions and creates new default values.
Definition: cxImage.cpp:210
virtual DoubleBoundingBox3D boundingBox() const
bounding box in image space
Definition: cxImage.cpp:361
std::vector< vtkPlanePtr > mPersistentClipPlanes
Definition: cxData.h:179
vtkImageDataPtr mBaseGrayScaleImageData
image data in data space
Definition: cxImage.h:190