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