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