CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxImage.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 
34 #include "cxImage.h"
35 
36 #include <QDomDocument>
37 #include <QDir>
38 #include <vtkImageAccumulate.h>
39 #include <vtkImageReslice.h>
40 #include <vtkImageData.h>
41 #include <vtkMatrix4x4.h>
42 #include <vtkPlane.h>
43 #include <vtkPlanes.h>
44 #include <vtkImageResample.h>
45 #include <vtkImageChangeInformation.h>
46 #include <vtkImageClip.h>
47 #include <vtkImageIterator.h>
48 #include <vtkImageShiftScale.h>
49 #include "cxImageTF3D.h"
50 #include "cxBoundingBox3D.h"
51 #include "cxImageLUT2D.h"
53 #include "cxLandmark.h"
54 
55 #include "cxLogger.h"
56 #include "cxTypeConversions.h"
57 #include "cxUtilHelpers.h"
58 #include "cxVolumeHelpers.h"
60 #include "cxDataReaderWriter.h"
61 #include "cxNullDeleter.h"
62 
63 #include "cxUnsignedDerivedImage.h"
64 
65 typedef vtkSmartPointer<vtkImageChangeInformation> vtkImageChangeInformationPtr;
66 
67 namespace cx
68 {
69 
71 {
72  on = true;
73  ambient = 0.2;
74  diffuse = 0.9;
75  specular = 0.3;
76  specularPower = 15.0;
77 }
78 
79 double Image::ShadingStruct::loadAttribute(QDomNode dataNode, QString name, double defVal)
80 {
81  QString text = dataNode.toElement().attribute(name);
82  bool ok;
83  double val = text.toDouble(&ok);
84  if (ok)
85  return val;
86  return defVal;
87 }
88 
89 void Image::ShadingStruct::addXml(QDomNode dataNode)
90 {
91  QDomElement elem = dataNode.toElement();
92  elem.setAttribute("on", on);
93  elem.setAttribute("ambient", ambient);
94  elem.setAttribute("diffuse", diffuse);
95  elem.setAttribute("specular", specular);
96  elem.setAttribute("specularPower", specularPower);
97 }
98 
99 void Image::ShadingStruct::parseXml(QDomNode dataNode)
100 {
101  if (dataNode.isNull())
102  return;
103 
104  on = dataNode.toElement().attribute("on").toInt();
105  // std::cout << "attrib on: " << dataNode.toElement().attribute("on") << " : " << on << std::endl;
106  ambient = loadAttribute(dataNode, "ambient", ambient);
107  diffuse = loadAttribute(dataNode, "diffuse", diffuse);
108  specular = loadAttribute(dataNode, "specular", specular);
109  specularPower = loadAttribute(dataNode, "specularPower", specularPower);
110 }
111 
112 //---------------------------------------------------------
113 //---------------------------------------------------------
114 //---------------------------------------------------------
115 
116 ImagePtr Image::create(const QString& uid, const QString& name)
117 {
118  return ImagePtr(new Image(uid, vtkImageDataPtr(), name));
119 }
120 
122 {
123 }
124 
125 Image::Image(const QString& uid, const vtkImageDataPtr& data, const QString& name) :
126  Data(uid, name), mBaseImageData(data), mMaxRGBIntensity(-1)
127 {
128  mInitialWindowWidth = -1;
129  mInitialWindowLevel = -1;
130 
131  mInterpolationType = VTK_LINEAR_INTERPOLATION;
132  mUseCropping = false;
133  mCroppingBox_d = this->getInitialBoundingBox();
134  mModality = "UNKNOWN";
135 
136  mImageLookupTable2D.reset();
137  mImageTransferFunctions3D.reset();
138 
139  this->setAcquisitionTime(QDateTime::currentDateTime());
140 }
141 
143 {
144  vtkImageDataPtr baseImageDataCopy;
145  if(mBaseImageData)
146  {
147  baseImageDataCopy = vtkImageDataPtr::New();
148  baseImageDataCopy->DeepCopy(mBaseImageData);
149  }
150 
151  ImagePtr retval = ImagePtr(new Image(mUid, baseImageDataCopy, mName));
152 
153  retval->mUnsigned = mUnsigned;
154  retval->mModality = mModality;
155  retval->mImageType = mImageType;
156  retval->mMaxRGBIntensity = mMaxRGBIntensity;
157  retval->mInterpolationType = mInterpolationType;
158  retval->mImageLookupTable2D = mImageLookupTable2D;
159  retval->mImageTransferFunctions3D = mImageTransferFunctions3D;
160  retval->mInitialWindowWidth = mInitialWindowWidth;
161  retval->mInitialWindowLevel = mInitialWindowLevel;
162 
163  //From cx::Data
164  retval->mRegistrationStatus = mRegistrationStatus;
165  retval->m_rMd_History = m_rMd_History;
166 
167  return retval;
168 }
169 
171 {
172  this->get_rMd_History()->setRegistration(parentImage->get_rMd());
173  this->get_rMd_History()->setParentSpace(parentImage->getUid());
174  ImageTF3DPtr transferFunctions = parentImage->getTransferFunctions3D()->createCopy();
175  ImageLUT2DPtr LUT2D = parentImage->getLookupTable2D()->createCopy();
176  this->setLookupTable2D(LUT2D);
177  this->setTransferFunctions3D(transferFunctions);
178  this->setModality(parentImage->getModality());
179  this->setImageType(parentImage->getImageType());
180  this->setShading(parentImage->getShading());
181  mInitialWindowWidth = parentImage->getInitialWindowWidth();
182  mInitialWindowLevel = parentImage->getInitialWindowLevel();
183 }
184 
185 DoubleBoundingBox3D Image::getInitialBoundingBox() const
186 {
187  return DoubleBoundingBox3D(-1, -1, -1, -1, -1, -1);
188 }
189 
191 {
192  CX_ASSERT(this==self.get());
193 
194  if (!mUnsigned)
195  {
196  // self is unsigned: return self
197  if (this->getBaseVtkImageData()->GetScalarTypeMin() >= 0)
198  return self;
199  else // signed: create unsigned adapter
201  }
202 
203  return mUnsigned;
204 }
205 
206 
207 
208 void Image::resetTransferFunctions(bool _2D, bool _3D)
209 {
210  if (!mBaseImageData)
211  {
212  reportWarning("Image has no image data");
213  return;
214  }
215 
216  mBaseImageData->GetScalarRange(); // this line updates some internal vtk value, and (on fedora) removes 4.5s in the second render().
217  mMaxRGBIntensity = -1;
218 
219  ImageDefaultTFGenerator tfGenerator(ImagePtr(this, null_deleter()));
220  if (_3D)
221  {
222  this->resetTransferFunction(tfGenerator.generate3DTFPreset());
223  tfGenerator.resetShading();
224  }
225  if (_2D)
226  this->resetTransferFunction(tfGenerator.generate2DTFPreset());
227 }
228 
229 void Image::resetTransferFunction(ImageTF3DPtr imageTransferFunctions3D, ImageLUT2DPtr imageLookupTable2D)
230 {
231  this->blockSignals(true); // avoid emitting two transferFunctionsChanged() for one call.
232 
233  this->resetTransferFunction(imageTransferFunctions3D);
234  this->resetTransferFunction(imageLookupTable2D);
235 
236  this->blockSignals(false);
238 }
239 
240 void Image::resetTransferFunction(ImageLUT2DPtr imageLookupTable2D)
241 {
242  if (mImageLookupTable2D)
243  {
244  disconnect(mImageLookupTable2D.get(), SIGNAL(transferFunctionsChanged()), this,
245  SIGNAL(transferFunctionsChanged()));
246  }
247 
248  mImageLookupTable2D = imageLookupTable2D;
249 
250  if (mImageLookupTable2D)
251  {
252  connect(mImageLookupTable2D.get(), SIGNAL(transferFunctionsChanged()), this, SIGNAL(transferFunctionsChanged()));
253  }
254 
256 }
257 
258 void Image::resetTransferFunction(ImageTF3DPtr imageTransferFunctions3D)
259 {
260  if (mImageTransferFunctions3D)
261  {
262  disconnect(mImageTransferFunctions3D.get(), SIGNAL(transferFunctionsChanged()), this,
263  SIGNAL(transferFunctionsChanged()));
264  }
265 
266  mImageTransferFunctions3D = imageTransferFunctions3D;
267 
268  if (mImageTransferFunctions3D)
269  {
270  connect(mImageTransferFunctions3D.get(), SIGNAL(transferFunctionsChanged()), this,
271  SIGNAL(transferFunctionsChanged()));
272  }
273 
275 }
276 
278 {
279 }
280 
282 {
283  // important! move thread affinity to main thread - ensures signals/slots is still called correctly
284  this->moveToThread(thread);
285  this->getTransferFunctions3D()->moveToThread(thread);
286  this->getLookupTable2D()->moveToThread(thread);
287  this->get_rMd_History()->moveToThread(thread);
288 }
289 
290 void Image::setVtkImageData(const vtkImageDataPtr& data, bool resetTransferFunctions)
291 {
292  mBaseImageData = data;
294 
295  if (resetTransferFunctions)
296  this->resetTransferFunctions();
297  emit vtkImageDataChanged();
298 }
299 
301 {
302  double windowWidth = mImageLookupTable2D->getWindow();
303  double windowLevel = mImageLookupTable2D->getLevel();
304  return convertImageDataTo8Bit(this->getGrayScaleVtkImageData(), windowWidth, windowLevel);
305 }
306 
308 {
310  {
312  }
313 
316 }
317 
319 {
320  if(!this->mImageTransferFunctions3D)
321  this->resetTransferFunctions(false, true);
322  return mImageTransferFunctions3D;
323 }
324 
326 {
327  this->resetTransferFunction(transferFuntion);
328 }
329 
331 {
332  if(!mImageLookupTable2D)
333  this->resetTransferFunctions(true, false);
334  return mImageLookupTable2D;
335 }
336 
337 void Image::setLookupTable2D(ImageLUT2DPtr imageLookupTable2D)
338 {
339  this->resetTransferFunction(imageLookupTable2D);
340 }
341 
343 {
344  return mBaseImageData;
345 }
346 
347 //vtkImageDataPtr Image::getRefVtkImageData()
348 //{
349 // if (!mReferenceImageData) // optimized: don't init it if you don't need it.
350 // {
351 // // provide a resampled volume for algorithms requiring that (such as PickerRep)
352 // mOrientatorMatrix = vtkMatrix4x4Ptr::New();
353 // mOrientator = vtkImageReslicePtr::New();
354 // mOrientator->SetInput(mBaseImageData);
355 // mOrientator->SetInterpolationModeToLinear();
356 // mOrientator->SetOutputDimensionality(3);
357 // mOrientator->SetResliceAxes(mOrientatorMatrix);
358 // mOrientator->AutoCropOutputOn();
359 // mReferenceImageData = mOrientator->GetOutput();
360 
361 // mReferenceImageData->Update();
362 
363 // this->transformChangedSlot(); // update transform
364 // std::cout << "Warning: Image::getRefVtkImageData() called. Expensive. Do not use." << std::endl;
365 // }
366 
367 // return mReferenceImageData;
368 //}
369 
371 {
372 // mBaseImageData->UpdateInformation();
373  DoubleBoundingBox3D bounds(mBaseImageData->GetBounds());
374  return bounds;
375 }
376 
377 Eigen::Array3d Image::getSpacing() const
378 {
379  return Eigen::Array3d(mBaseImageData->GetSpacing());
380 }
381 
383 {
384  if (mHistogramPtr.GetPointer() == NULL)
385  {
386  mHistogramPtr = vtkImageAccumulatePtr::New();
387  mHistogramPtr->SetInputData(this->getGrayScaleVtkImageData());
388  mHistogramPtr->IgnoreZeroOn(); // required for Sonowand CT volumes, where data are placed between 31K and 35K.
389  // Set up only a 1D histogram for now, so y and z values are set to 0
390  mHistogramPtr->SetComponentExtent(0, this->getRange(), 0, 0, 0, 0);
391  mHistogramPtr->SetComponentOrigin(this->getMin(), 0, 0);
392  mHistogramPtr->SetComponentSpacing(1, 0, 0);
393  }
394  mHistogramPtr->Update();
395  return mHistogramPtr;
396 }
397 
398 template<typename scalartype> static int getRGBMax(vtkImageDataPtr image)
399 {
400  int max = 0;
401  vtkImageIterator<scalartype> iter(image, image->GetExtent());
402  while (!iter.IsAtEnd())
403  {
404  typename vtkImageIterator<scalartype>::SpanIterator siter = iter.BeginSpan();
405  while (siter != iter.EndSpan())
406  {
407  int value = *siter;
408  ++siter;
409  value += *siter;
410  ++siter;
411  value += *siter;
412  ++siter;
413  if (value > max)
414  {
415  max = value;
416  }
417  }
418  iter.NextSpan();
419  }
420  return max/3;
421 }
422 
423 
425 {
426  // Alternatively create max from histogram
427  //IntIntMap::iterator iter = this->getHistogram()->end();
428  //iter--;
429  //return (*iter).first;
430  if (mBaseImageData->GetNumberOfScalarComponents() == 3)
431  {
432  if (mMaxRGBIntensity != -1)
433  {
434  return mMaxRGBIntensity;
435  }
436  QDateTime before = QDateTime::currentDateTime();
437  double max = 0.0;
438  switch (mBaseImageData->GetScalarType())
439  {
440  case VTK_UNSIGNED_CHAR:
441  max = getRGBMax<unsigned char>(mBaseImageData);
442  break;
443  case VTK_UNSIGNED_SHORT:
444  max = getRGBMax<unsigned short>(mBaseImageData);
445  break;
446  default:
447  CX_LOG_ERROR() << "Unhandled RGB data type in image " << this->getUid();
448  break;
449  }
450  mMaxRGBIntensity = max;
451  return (int)mMaxRGBIntensity;
452  }
453  else
454  {
455 // return (int) this->getTransferFunctions3D()->getScalarMax();
456  return mBaseImageData->GetScalarRange()[1];
457  }
458 }
459 
461 {
462  // Alternatively create min from histogram
463  //IntIntMap::iterator iter = this->getHistogram()->begin();
464  //return (*iter).first;
465  return mBaseImageData->GetScalarRange()[0];
466 // return (int) this->getTransferFunctions3D()->getScalarMin();
467 }
468 
470 {
471  return this->getMax() - this->getMin();
472 }
473 
475 {
476  return 255;
477 }
478 
479 //LandmarksPtr Image::getLandmarks()
480 //{
481 // return mLandmarks;
482 //}
483 
484 void Image::addXml(QDomNode& dataNode)
485 {
486  Data::addXml(dataNode);
487  QDomNode imageNode = dataNode;
488  QDomDocument doc = dataNode.ownerDocument();
489 
490  QDomElement tf3DNode = doc.createElement("transferfunctions");
491  this->getTransferFunctions3D()->addXml(tf3DNode);
492  imageNode.appendChild(tf3DNode);
493 
494  QDomElement lut2DNode = doc.createElement("lookuptable2D");
495  this->getLookupTable2D()->addXml(lut2DNode);
496  imageNode.appendChild(lut2DNode);
497 
498  QDomElement shadingNode = doc.createElement("shading");
499  mShading.addXml(shadingNode);
500  imageNode.appendChild(shadingNode);
501 
502 // QDomElement landmarksNode = doc.createElement("landmarks");
503 // mLandmarks->addXml(landmarksNode);
504 // imageNode.appendChild(landmarksNode);
505 
506  QDomElement cropNode = doc.createElement("crop");
507  cropNode.setAttribute("use", mUseCropping);
508  cropNode.appendChild(doc.createTextNode(qstring_cast(mCroppingBox_d)));
509  imageNode.appendChild(cropNode);
510 
511  QDomElement clipNode = doc.createElement("clip");
512  for (unsigned i = 0; i < mPersistentClipPlanes.size(); ++i)
513  {
514  QDomElement planeNode = doc.createElement("plane");
515  Vector3D normal(mPersistentClipPlanes[i]->GetNormal());
516  Vector3D origin(mPersistentClipPlanes[i]->GetOrigin());
517  planeNode.setAttribute("normal", qstring_cast(normal));
518  planeNode.setAttribute("origin", qstring_cast(origin));
519  clipNode.appendChild(planeNode);
520  }
521  imageNode.appendChild(clipNode);
522 
523  QDomElement modalityNode = doc.createElement("modality");
524  modalityNode.appendChild(doc.createTextNode(mModality));
525  imageNode.appendChild(modalityNode);
526 
527  QDomElement imageTypeNode = doc.createElement("imageType");
528  imageTypeNode.appendChild(doc.createTextNode(mImageType));
529  imageNode.appendChild(imageTypeNode);
530 
531  QDomElement interpolationNode = doc.createElement("vtk_interpolation");
532  interpolationNode.setAttribute("type", mInterpolationType);
533  imageNode.appendChild(interpolationNode);
534 
535  QDomElement initialWindowNode = doc.createElement("initialWindow");
536  initialWindowNode.setAttribute("width", mInitialWindowWidth);
537  initialWindowNode.setAttribute("level", mInitialWindowLevel);
538 }
539 
540 double Image::loadAttribute(QDomNode dataNode, QString name, double defVal)
541 {
542  QString text = dataNode.toElement().attribute(name);
543  bool ok;
544  double val = text.toDouble(&ok);
545  if (ok)
546  return val;
547  return defVal;
548 }
549 
550 bool Image::load(QString path)
551 {
552  ImagePtr self = ImagePtr(this, null_deleter());
553  DataReaderWriter().readInto(self, path);
554  return this->getBaseVtkImageData()!=0;
555 }
556 
557 void Image::parseXml(QDomNode& dataNode)
558 {
559  Data::parseXml(dataNode);
560 
561  // image node must be parsed in the data manager to create this Image object
562  // Only subnodes are parsed here
563 
564  if (dataNode.isNull())
565  return;
566 
567  //transferefunctions
568  QDomNode transferfunctionsNode = dataNode.namedItem("transferfunctions");
569  if (!transferfunctionsNode.isNull())
570  this->getTransferFunctions3D()->parseXml(transferfunctionsNode);
571  else
572  {
573  std::cout << "Warning: Image::parseXml() found no transferfunctions";
574  std::cout << std::endl;
575  }
576 
577  mInitialWindowWidth = this->loadAttribute(dataNode.namedItem("initialWindow"), "width", -1);
578  mInitialWindowLevel = this->loadAttribute(dataNode.namedItem("initialWindow"), "level", -1);
579 
580  this->getLookupTable2D()->parseXml(dataNode.namedItem("lookuptable2D"));
581 
582  // backward compatibility:
583  mShading.on = dataNode.namedItem("shading").toElement().text().toInt();
584  //Assign default values if the shading nodes don't exists to allow backward compability
585  if (!dataNode.namedItem("shadingAmbient").isNull())
586  mShading.ambient = dataNode.namedItem("shadingAmbient").toElement().text().toDouble();
587  if (!dataNode.namedItem("shadingDiffuse").isNull())
588  mShading.diffuse = dataNode.namedItem("shadingDiffuse").toElement().text().toDouble();
589  if (!dataNode.namedItem("shadingSpecular").isNull())
590  mShading.specular = dataNode.namedItem("shadingSpecular").toElement().text().toDouble();
591  if (!dataNode.namedItem("shadingSpecularPower").isNull())
592  mShading.specularPower = dataNode.namedItem("shadingSpecularPower").toElement().text().toDouble();
593 
594  // new way:
595  mShading.parseXml(dataNode.namedItem("shading"));
596 
597 // mLandmarks->parseXml(dataNode.namedItem("landmarks"));
598 
599  QDomElement cropNode = dataNode.namedItem("crop").toElement();
600  if (!cropNode.isNull())
601  {
602  mUseCropping = cropNode.attribute("use").toInt();
604  }
605 
606  QDomElement clipNode = dataNode.namedItem("clip").toElement();
607  QDomElement clipPlaneNode = clipNode.firstChildElement("plane");
608  for (; !clipPlaneNode.isNull(); clipPlaneNode = clipPlaneNode.nextSiblingElement("plane"))
609  {
610  Vector3D normal = Vector3D::fromString(clipPlaneNode.attribute("normal"));
611  Vector3D origin = Vector3D::fromString(clipPlaneNode.attribute("origin"));
612  vtkPlanePtr plane = vtkPlanePtr::New();
613  plane->SetNormal(normal.begin());
614  plane->SetOrigin(origin.begin());
615  mPersistentClipPlanes.push_back(plane);
616  }
617 
618  mModality = dataNode.namedItem("modality").toElement().text();
619  mImageType = dataNode.namedItem("imageType").toElement().text();
620 
621  QDomElement interpoationNode = dataNode.namedItem("vtk_interpolation").toElement();
622  if (!interpoationNode.isNull())
623  {
624  mInterpolationType = interpoationNode.attribute("type").toInt();
625  emit vtkImageDataChanged();
626  }
627 }
628 
629 void Image::setInitialWindowLevel(double width, double level)
630 {
631  mInitialWindowWidth = width;
632  mInitialWindowLevel = level;
633 }
634 
635 void Image::setShadingOn(bool on)
636 {
637  mShading.on = on;
639 }
640 
642 {
643  return mShading.on;
644 }
645 
646 void Image::setShadingAmbient(double ambient)
647 {
648  mShading.ambient = ambient;
650 }
651 
652 void Image::setShadingDiffuse(double diffuse)
653 {
654  mShading.diffuse = diffuse;
656 }
657 
658 void Image::setShadingSpecular(double specular)
659 {
660  mShading.specular = specular;
662 }
663 
664 void Image::setShadingSpecularPower(double specularPower)
665 {
666  mShading.specularPower = specularPower;
668 }
669 
671 {
672  return mShading.ambient;
673 }
674 
676 {
677  return mShading.diffuse;
678 }
679 
681 {
682  return mShading.specular;
683 }
684 
686 {
687  return mShading.specularPower;
688 }
689 
691 {
692  return mShading;
693 }
694 
696 {
697  mShading = shading;
699 }
700 
701 // methods for defining and storing a cropping box. Image does not use these data, this is up to the mapper
702 void Image::setCropping(bool on)
703 {
704  if (mUseCropping == on)
705  return;
706 
707  mUseCropping = on;
708  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
709  mCroppingBox_d = this->boundingBox();
710  emit cropBoxChanged();
711 }
712 
713 bool Image::getCropping() const
714 {
715  return mUseCropping;
716 }
717 
719 {
720  if (similar(mCroppingBox_d, bb_d))
721  return;
722  mCroppingBox_d = bb_d;
723  emit cropBoxChanged();
724 }
725 
727 {
728  if (similar(mCroppingBox_d, this->getInitialBoundingBox()))
729  return this->boundingBox();
730  return mCroppingBox_d;
731 }
732 
733 // methods for defining and storing clip planes. Image does not use these data, this is up to the mapper
735 {
736  if (std::count(mPersistentClipPlanes.begin(), mPersistentClipPlanes.end(), plane))
737  return;
738  mPersistentClipPlanes.push_back(plane);
739  emit clipPlanesChanged();
740 }
741 
742 std::vector<vtkPlanePtr> Image::getAllClipPlanes()
743 {
744  std::vector<vtkPlanePtr> retval = mPersistentClipPlanes;
746  retval.push_back(mInteractiveClipPlane);
747  return retval;
748 }
749 
751 {
752  mPersistentClipPlanes.clear();
753  emit clipPlanesChanged();
754 }
755 
757 {
758  mInteractiveClipPlane = plane;
759  emit clipPlanesChanged();
760 }
761 
775 {
776  // the internal CustusX format does not handle extents starting at non-zero.
777  // Move extent to zero and change rMd.
778  Vector3D origin(mBaseImageData->GetOrigin());
779  Vector3D spacing(mBaseImageData->GetSpacing());
780  IntBoundingBox3D extent(mBaseImageData->GetExtent());
781  Vector3D extentShift = multiply_elems(extent.corner(0, 0, 0).cast<double>(), spacing);
782 
783  vtkImageChangeInformationPtr info = vtkImageChangeInformationPtr::New();
784  info->SetInputData(mBaseImageData);
785  info->SetOutputExtentStart(0, 0, 0);
786  info->SetOutputOrigin(0, 0, 0);
787  info->Update();
788  info->UpdateInformation();
789  mBaseImageData = info->GetOutput();
790 
791  mBaseImageData->ComputeBounds();
792 // mBaseImageData->Update();
793 // mBaseImageData->UpdateInformation();
794 
795  this->get_rMd_History()->setRegistration(this->get_rMd() * createTransformTranslate(origin + extentShift));
796 
797  emit vtkImageDataChanged();
798  emit clipPlanesChanged();
799  emit cropBoxChanged();
800 }
801 
802 QString Image::getModality() const
803 {
804  return mModality;
805 }
806 
807 void Image::setModality(const QString& val)
808 {
809  mModality = val;
810  emit propertiesChanged();
811 }
812 
813 QString Image::getImageType() const
814 {
815  return mImageType;
816 }
817 
818 void Image::setImageType(const QString& val)
819 {
820  mImageType = val;
821  emit propertiesChanged();
822 }
823 
824 vtkImageDataPtr Image::createDummyImageData(int axisSize, int maxVoxelValue)
825 {
826  int size = axisSize - 1;//Modify axis size as extent starts with 0, not 1
827  vtkImageDataPtr dummyImageData = vtkImageDataPtr::New();
828  dummyImageData->SetExtent(0, size, 0, size, 0, size);
829  dummyImageData->SetSpacing(1, 1, 1);
830  //dummyImageData->SetScalarTypeToUnsignedShort();
831 // dummyImageData->SetScalarTypeToUnsignedChar();
832 // dummyImageData->SetNumberOfScalarComponents(1);
833 // dummyImageData->AllocateScalars();
834  dummyImageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
835  unsigned char* dataPtr = static_cast<unsigned char*> (dummyImageData->GetScalarPointer());
836 
837  //Init voxel colors
838  int minVoxelValue = 0;
839  int numVoxels = axisSize*axisSize*axisSize;
840  for (int i = 0; i < numVoxels; ++i)
841  {
842  int voxelValue = minVoxelValue + i;
843  if (i == numVoxels)
844  dataPtr[i] = maxVoxelValue;
845  else if (voxelValue < maxVoxelValue)
846  dataPtr[i] = voxelValue;
847  else
848  dataPtr[i] = maxVoxelValue;
849  }
850  setDeepModified(dummyImageData);
851  return dummyImageData;
852 }
853 
855 {
856  this->setInterpolationType(VTK_NEAREST_INTERPOLATION);
857 }
859 {
860  this->setInterpolationType(VTK_LINEAR_INTERPOLATION);
861 }
863 {
864  mInterpolationType = val;
865  emit vtkImageDataChanged();
866 }
868 {
869  return mInterpolationType;
870 }
871 
873 {
874  // also use grayscale as vtk is incapable of rendering 3component color.
875  vtkImageDataPtr retval = this->getGrayScaleVtkImageData();
876 
877  double factor = computeResampleFactor(maxVoxels);
878 
879  if (fabs(1.0-factor)>0.01) // resampling
880  {
881  vtkImageResamplePtr resampler = vtkImageResamplePtr::New();
882  resampler->SetInterpolationModeToLinear();
883  resampler->SetAxisMagnificationFactor(0, factor);
884  resampler->SetAxisMagnificationFactor(1, factor);
885  resampler->SetAxisMagnificationFactor(2, factor);
886  resampler->SetInputData(retval);
887 // resampler->GetOutput()->Update();
888  resampler->Update();
889  resampler->GetOutput()->GetScalarRange();
890  retval = resampler->GetOutput();
891 
892  long voxelsDown = retval->GetNumberOfPoints();
893  long voxelsOrig = this->getBaseVtkImageData()->GetNumberOfPoints();
894 // report("Created downsampled volume in Image: "
895 // + this->getName()
896 // + " below " + qstring_cast(voxelsDown/1000/1000) + "M. "
897 // + "Ratio: " + QString::number(factor, 'g', 2) + ", "
898 // + "Original size: " + qstring_cast(voxelsOrig/1000/1000) + "M.");
899  }
900  return retval;
901 }
902 
903 double Image::computeResampleFactor(long maxVoxels)
904 {
905  if (maxVoxels==0)
906  return 1.0;
907 
908  long voxels = this->getBaseVtkImageData()->GetNumberOfPoints();
909  double factor = (double)maxVoxels/(double)voxels;
910  factor = pow(factor, 1.0/3.0);
911  // cubic function leads to trouble for 138M-volume - must downsample to as low as 5-10 Mv in order to succeed on Mac.
912 
913  if (factor<0.99)
914  {
915  return factor;
916  }
917  return 1.0;
918 }
919 
920 void Image::save(const QString& basePath)
921 {
922  QString filename = basePath + "/Images/" + this->getUid() + ".mhd";
923  this->setFilename(QDir(basePath).relativeFilePath(filename));
924 
925  ImagePtr self = ImagePtr(this, null_deleter());
926  MetaImageReader().saveImage(self, filename);
927 }
928 
929 } // namespace cx
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
Definition: cxImage.cpp:557
QString qstring_cast(const T &val)
static vtkImageDataPtr createDummyImageData(int axisSize, int maxVoxelValue)
Create a moc object of vtkImageData.
Definition: cxImage.cpp:824
Image(const QString &uid, const vtkImageDataPtr &data, const QString &name="")
Definition: cxImage.cpp:125
ShadingStruct mShading
Definition: cxImage.h:200
virtual bool getCropping() const
Definition: cxImage.cpp:713
virtual void setModality(const QString &val)
Definition: cxImage.cpp:807
virtual Transform3D get_rMd() const
Definition: cxData.cpp:100
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:867
virtual Eigen::Array3d getSpacing() const
Definition: cxImage.cpp:377
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:207
void parseXml(QDomNode dataNode)
Definition: cxImage.cpp:99
virtual void setTransferFunctions3D(ImageTF3DPtr transferFuntion)
Definition: cxImage.cpp:325
PlainObject normal() const
virtual double getShadingDiffuse()
Get shading diffuse parmeter.
Definition: cxImage.cpp:675
virtual vtkImageAccumulatePtr getHistogram()
Definition: cxImage.cpp:382
void mergevtkSettingsIntosscTransform()
Definition: cxImage.cpp:774
static ImagePtr create(const QString &uid, const QString &name)
Definition: cxImage.cpp:116
void addXml(QDomNode dataNode)
Definition: cxImage.cpp:89
#define CX_ASSERT(statement)
Definition: cxLogger.h:128
virtual void setShadingDiffuse(double diffuse)
Set shading diffuse parmeter.
Definition: cxImage.cpp:652
virtual QString getModality() const
Definition: cxImage.cpp:802
virtual std::vector< vtkPlanePtr > getAllClipPlanes()
Definition: cxImage.cpp:742
virtual void addPersistentClipPlane(vtkPlanePtr plane)
Definition: cxImage.cpp:734
QString mUid
Definition: cxData.h:118
virtual void setVtkImageData(const vtkImageDataPtr &data, bool resetTransferFunctions=true)
Definition: cxImage.cpp:290
vtkSmartPointer< class vtkImageAccumulate > vtkImageAccumulatePtr
void propertiesChanged()
emitted when one of the metadata properties (uid, name etc) changes
virtual ~Image()
Definition: cxImage.cpp:121
virtual void setLookupTable2D(ImageLUT2DPtr imageLookupTable2D)
Definition: cxImage.cpp:337
QString mName
Definition: cxData.h:119
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
virtual void setInitialWindowLevel(double width, double level)
Definition: cxImage.cpp:629
vtkSmartPointer< vtkImageChangeInformation > vtkImageChangeInformationPtr
Definition: cxImage.cpp:65
double mMaxRGBIntensity
Definition: cxImage.h:208
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:702
virtual Image::ShadingStruct getShading()
Definition: cxImage.cpp:690
virtual void setShadingOn(bool on)
Definition: cxImage.cpp:635
DoubleBoundingBox3D mCroppingBox_d
box defining the cropping size.
Definition: cxImage.h:203
virtual void setShadingAmbient(double ambient)
Set shading ambient parmeter.
Definition: cxImage.cpp:646
void clipPlanesChanged()
virtual vtkImageDataPtr getBaseVtkImageData()
Definition: cxImage.cpp:342
static ImagePtr create(ImagePtr base)
virtual void addXml(QDomNode &dataNode)
adds xml information about the data and its variabels
Definition: cxData.cpp:120
virtual double getShadingSpecular()
Get shading specular parmeter.
Definition: cxImage.cpp:680
virtual void setShadingSpecular(double specular)
Set shading specular parmeter.
Definition: cxImage.cpp:658
virtual void setImageType(const QString &val)
Definition: cxImage.cpp:818
ImagePtr mUnsigned
version of this containing unsigned data.
Definition: cxImage.h:196
virtual ImagePtr getUnsigned(ImagePtr self)
Definition: cxImage.cpp:190
virtual int getMin()
Definition: cxImage.cpp:460
virtual void intitializeFromParentImage(ImagePtr parentImage)
Definition: cxImage.cpp:170
virtual QString getUid() const
Definition: cxData.cpp:78
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:484
virtual int getMax()
Definition: cxImage.cpp:424
virtual RegistrationHistoryPtr get_rMd_History()
Definition: cxData.cpp:105
vtkSmartPointer< class vtkImageChangeInformation > vtkImageChangeInformationPtr
boost::shared_ptr< class ImageLUT2D > ImageLUT2DPtr
vtkPlanePtr mInteractiveClipPlane
Definition: cxImage.h:205
QString mModality
modality of the image, defined as DICOM tag (0008,0060), Section 3, C.7.3.1.1.1
Definition: cxImage.h:206
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
virtual int getMaxAlphaValue()
Max alpha value (probably 255)
Definition: cxImage.cpp:474
#define CX_LOG_ERROR
Definition: cxLogger.h:113
vtkImageDataPtr convertImageDataTo8Bit(vtkImageDataPtr image, double windowWidth, double windowLevel)
Have never been used or tested. Create a test for it.
virtual void parseXml(QDomNode &dataNode)
Use a XML node to load data.
Definition: cxData.cpp:146
virtual void setShadingSpecularPower(double specularPower)
Set shading specular power parmeter.
Definition: cxImage.cpp:664
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:281
virtual int getRange()
For convenience: getMax() - getMin()
Definition: cxImage.cpp:469
virtual void save(const QString &basePath)
Definition: cxImage.cpp:920
void setAcquisitionTime(QDateTime time)
Definition: cxData.cpp:181
void transferFunctionsChanged()
emitted when image transfer functions in 2D or 3D are changed.
Transform3D createTransformTranslate(const Vector3D &translation)
Representation of an integer bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
vtkSmartPointer< class vtkImageResample > vtkImageResamplePtr
void saveImage(ImagePtr image, const QString &filename)
vtkImageAccumulatePtr mHistogramPtr
Histogram.
Definition: cxImage.h:195
vtkImageDataPtr resample(long maxVoxels)
Definition: cxImage.cpp:872
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
Vector3D multiply_elems(const Vector3D &a, const Vector3D &b)
perform element-wise multiplication of a and b.
Definition: cxVector3D.cpp:52
virtual void setInteractiveClipPlane(vtkPlanePtr plane)
set a plane that is not saved
Definition: cxImage.cpp:756
virtual void setShading(Image::ShadingStruct shading)
Definition: cxImage.cpp:695
vtkImageDataPtr convertImageDataToGrayScale(vtkImageDataPtr image)
RegistrationHistoryPtr m_rMd_History
Definition: cxData.h:125
Superclass for all data objects.
Definition: cxData.h:69
void setDeepModified(vtkImageDataPtr image)
void readInto(DataPtr data, QString path)
virtual DoubleBoundingBox3D getCroppingBox() const
Definition: cxImage.cpp:726
std::vector< vtkPlanePtr > mPersistentClipPlanes
Definition: cxImage.h:204
virtual void setCroppingBox(const DoubleBoundingBox3D &bb_d)
Definition: cxImage.cpp:718
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:124
virtual double getShadingAmbient()
Get shading ambient parmeter.
Definition: cxImage.cpp:670
virtual bool load(QString path)
Definition: cxImage.cpp:550
void setInterpolationType(int val)
Definition: cxImage.cpp:862
virtual QString getImageType() const
Definition: cxImage.cpp:813
void setInterpolationTypeToNearest()
Definition: cxImage.cpp:854
ImagePtr copy()
Definition: cxImage.cpp:142
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
virtual double getShadingSpecularPower()
Get shading specular power parmeter.
Definition: cxImage.cpp:685
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:209
vtkSmartPointer< class vtkPlane > vtkPlanePtr
void setInterpolationTypeToLinear()
Definition: cxImage.cpp:858
virtual void clearPersistentClipPlanes()
Definition: cxImage.cpp:750
boost::shared_ptr< class ImageTF3D > ImageTF3DPtr
virtual void setFilename(QString val)
Definition: cxData.cpp:92
virtual void transformChangedSlot()
Definition: cxImage.cpp:277
virtual ImageLUT2DPtr getLookupTable2D()
Definition: cxImage.cpp:330
virtual bool getShadingOn() const
Definition: cxImage.cpp:641
void resetTransferFunctions(bool _2D=true, bool _3D=true)
Resets the transfer functions and creates new defaut values.
Definition: cxImage.cpp:208
virtual DoubleBoundingBox3D boundingBox() const
bounding box in image space
Definition: cxImage.cpp:370
vtkImageDataPtr mBaseGrayScaleImageData
image data in data space
Definition: cxImage.h:191