Fraxinus  18.10
An IGT application
cxContourFilter.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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxContourFilter.h"
13 
14 #include <vtkImageShrink3D.h>
15 #include <vtkMarchingCubes.h>
16 #include <vtkWindowedSincPolyDataFilter.h>
17 #include <vtkTriangleFilter.h>
18 #include <vtkDecimatePro.h>
19 #include <vtkPolyDataNormals.h>
20 #include <vtkImageData.h>
21 
22 
24 #include "cxTypeConversions.h"
25 #include "cxImage.h"
26 #include "cxMesh.h"
27 #include "cxDoubleProperty.h"
28 #include "cxBoolProperty.h"
29 #include "cxColorProperty.h"
31 #include "cxPatientModelService.h"
32 #include "cxViewService.h"
33 #include "cxVisServices.h"
34 
35 namespace cx
36 {
37 
39  FilterImpl(services)
40 {
41 }
42 
43 QString ContourFilter::getName() const
44 {
45  return "Contour";
46 }
47 
48 QString ContourFilter::getType() const
49 {
50  return "contour_filter";
51 }
52 
53 QString ContourFilter::getHelp() const
54 {
55  return "<html>"
56  "<h3>Surfacing.</h3>"
57  "<p><i>Find the surface of a binary volume using marching cubes.</i></p>"
58  "<p>- Optional factor 2 reduction</p>"
59  "<p>- Marching Cubes contouring</p>"
60  "<p>- Optional Windowed Sinc smoothing</p>"
61  "<p>- Decimation of triangles</p>"
62  "</html>";
63 }
64 
66 {
67  return "_ge";
68 }
69 
71 {
72  return BoolProperty::initialize("Reduce input", "",
73  "Reduce input volumes resolution by a factor of 2 in all directions.", false, root);
74 }
75 
77 {
78  return BoolProperty::initialize("Smoothing", "",
79  "Smooth the output contour", true, root);
80 }
81 
83 {
84  return BoolProperty::initialize("Preserve mesh topology", "",
85  "Preserve mesh topology during reduction", true, root);
86 }
87 
89 {
90  DoublePropertyPtr retval = DoubleProperty::initialize("Threshold", "",
91  "Values from this threshold and above will be included",
92  100.0, DoubleRange(-1000, 1000, 1), 0, root);
93  retval->setGuiRepresentation(DoublePropertyBase::grSLIDER);
94  return retval;
95 }
96 
98 {
99  DoublePropertyPtr retval = DoubleProperty::initialize("Decimation %", "",
100  "Reduce number of triangles in output surface",
101  0.2, DoubleRange(0, 1, 0.01), 0, root);
102  retval->setInternal2Display(100);
103  return retval;
104 }
105 
107 {
108  return ColorProperty::initialize("Color", "",
109  "Color of output model.",
110  QColor("green"), root);
111 }
112 
114 {
115  return DoubleProperty::initialize("Number of iterations (smoothing)", "",
116  "Number of iterations in smoothing filter. Higher number = more smoothing",
117  15, DoubleRange(1, 50, 1), 0, root);
118 }
119 
121 {
122  return DoubleProperty::initialize("Band pass smoothing", "",
123  "Band pass width in smoothing filter. Smaller number = more smoothing",
124  0.30, DoubleRange(0.05, 0.95, 0.05), 2, root);
125 }
126 
128 {
129  mReduceResolutionOption = this->getReduceResolutionOption(mOptions);
130  mOptionsAdapters.push_back(mReduceResolutionOption);
131 
132  mSurfaceThresholdOption = this->getSurfaceThresholdOption(mOptions);
133  connect(mSurfaceThresholdOption.get(), SIGNAL(changed()), this, SLOT(thresholdSlot()));
134  mOptionsAdapters.push_back(mSurfaceThresholdOption);
135 
136  mOptionsAdapters.push_back(this->getSmoothingOption(mOptions));
138  mOptionsAdapters.push_back(this->getPassBandOption(mOptions));
141 
142  mOptionsAdapters.push_back(this->getColorOption(mOptions));
143 }
144 
146 {
148 
149  temp = StringPropertySelectImage::New(mServices->patient());
150  temp->setValueName("Input");
151  temp->setHelp("Select image input for contouring");
152  connect(temp.get(), SIGNAL(dataChanged(QString)), this, SLOT(imageChangedSlot(QString)));
153  mInputTypes.push_back(temp);
154 }
155 
157 {
159 
160  temp = StringPropertySelectMesh::New(mServices->patient());
161  temp->setValueName("Output");
162  temp->setHelp("Output contour");
163  mOutputTypes.push_back(temp);
164 }
165 
167 {
169 
170  if (!mActive)
171  this->stopPreview();
172 }
173 
174 void ContourFilter::stopPreview()
175 {
176  if(mPreviewImage)
177  mPreviewImage->stopThresholdPreview();
178  mPreviewImage.reset();
179 }
180 
181 void ContourFilter::imageChangedSlot(QString uid)
182 {
183  ImagePtr image = mServices->patient()->getData<Image>(uid);
184  if(!image)
185  return;
186 
187  this->updateThresholdFromImageChange(uid, mSurfaceThresholdOption);
188  this->stopPreview();
189 
190  int extent[6];
191  image->getBaseVtkImageData()->GetExtent(extent);
192  mReduceResolutionOption->setHelp("Current input resolution: " + qstring_cast(extent[1])
193  + " " + qstring_cast(extent[3]) + " " + qstring_cast(extent[5])
194  + " (If checked: " + qstring_cast(extent[1]/2)+ " " + qstring_cast(extent[3]/2) + " "
195  + qstring_cast(extent[5]/2) + ")");
196 }
197 
198 void ContourFilter::thresholdSlot()
199 {
200  if (mActive)
201  {
202  mPreviewImage = boost::dynamic_pointer_cast<Image>(mInputTypes[0]->getData());
203  if(mPreviewImage)
204  {
205  Eigen::Vector2d threshold = Eigen::Vector2d(mSurfaceThresholdOption->getValue(), mPreviewImage->getMax());
206  mPreviewImage->startThresholdPreview(threshold);
207  }
208  }
209 }
210 
212 {
213  this->stopPreview();
214  return FilterImpl::preProcess();
215 }
216 
218 {
219  ImagePtr input = this->getCopiedInputImage();
220  if (!input)
221  return false;
222 
223  // std::cout << "ContourFilter::execute : " << mCopiedOptions.ownerDocument().toString() << std::endl;
224 
225  BoolPropertyPtr reduceResolutionOption = this->getReduceResolutionOption(mCopiedOptions);
226  BoolPropertyPtr smoothingOption = this->getSmoothingOption(mCopiedOptions);
227  DoublePropertyPtr numberOfIterationsOption = this->getNumberOfIterationsOption(mCopiedOptions);
228  DoublePropertyPtr passBandOption = this->getPassBandOption(mCopiedOptions);
229  BoolPropertyPtr preserveTopologyOption = this->getPreserveTopologyOption(mCopiedOptions);
230  DoublePropertyPtr surfaceThresholdOption = this->getSurfaceThresholdOption(mCopiedOptions);
231  DoublePropertyPtr decimationOption = this->getDecimationOption(mCopiedOptions);
232 
233  // report(QString("Creating contour from \"%1\"...").arg(input->getName()));
234 
235  mRawResult = this->execute(input->getBaseVtkImageData(),
236  surfaceThresholdOption->getValue(),
237  reduceResolutionOption->getValue(),
238  smoothingOption->getValue(),
239  preserveTopologyOption->getValue(),
240  decimationOption->getValue(),
241  numberOfIterationsOption->getValue(),
242  passBandOption->getValue());
243  return true;
244 }
245 
247  double threshold,
248  bool reduceResolution,
249  bool smoothing,
250  bool preserveTopology,
251  double decimation,
252  double numberOfIterations,
253  double passBand)
254 {
255  if (!input)
256  return vtkPolyDataPtr();
257 
258  //Shrink input volume
259  vtkImageShrink3DPtr shrinker = vtkImageShrink3DPtr::New();
260  if(reduceResolution)
261  {
262 // std::cout << "smooth" << std::endl;
263  shrinker->SetInputData(input);
264  shrinker->SetShrinkFactors(2,2,2);
265  shrinker->Update();
266  }
267 
268  // Find countour
269  vtkMarchingCubesPtr convert = vtkMarchingCubesPtr::New();
270  if(reduceResolution)
271  convert->SetInputConnection(shrinker->GetOutputPort());
272  else
273  convert->SetInputData(input);
274 
275  convert->SetValue(0, threshold);
276  convert->Update();
277 
278  vtkPolyDataPtr cubesPolyData = convert->GetOutput();
279 // vtkPolyDataPtr cubesPolyData = vtkPolyDataPtr::New();
280 // cubesPolyData = convert->GetOutput();
281 // std::cout << "convert->GetOutput(); " << cubesPolyData.GetPointer() << std::endl;
282 
283  // Smooth surface model
284  vtkWindowedSincPolyDataFilterPtr smoother = vtkWindowedSincPolyDataFilterPtr::New();
285  if(smoothing)
286  {
287  smoother->SetInputData(cubesPolyData);
288  smoother->SetNumberOfIterations(numberOfIterations);// Higher number = more smoothing - default 15
289  smoother->SetBoundarySmoothing(false);
290  smoother->SetFeatureEdgeSmoothing(false);
291  smoother->SetNormalizeCoordinates(true);
292  smoother->SetFeatureAngle(120);
293  smoother->SetPassBand(passBand);//Lower number = more smoothing - default 0.3
294  smoother->Update();
295  cubesPolyData = smoother->GetOutput();
296  }
297 
298  //Create a surface of triangles
299 
300  //Decimate surface model (remove a percentage of the polygons)
301  vtkTriangleFilterPtr trifilt = vtkTriangleFilterPtr::New();
302  vtkDecimateProPtr deci = vtkDecimateProPtr::New();
303  vtkPolyDataNormalsPtr normals = vtkPolyDataNormalsPtr::New();
304  if (decimation > 0.000001)
305  {
306  trifilt->SetInputData(cubesPolyData);
307  trifilt->Update();
308  deci->SetInputConnection(trifilt->GetOutputPort());
309  deci->SetTargetReduction(decimation);
310  deci->SetPreserveTopology(preserveTopology);
311  // deci->PreserveTopologyOn();
312  deci->Update();
313  cubesPolyData = deci->GetOutput();
314  }
315 
316  normals->SetInputData(cubesPolyData);
317  normals->Update();
318 
319  cubesPolyData->DeepCopy(normals->GetOutput());
320 
321  return cubesPolyData;
322 }
323 
325 {
326  if (!mRawResult)
327  return false;
328 
329  ImagePtr input = this->getCopiedInputImage();
330 
331  if (!input)
332  return false;
333 
334  ColorPropertyPtr colorOption = this->getColorOption(mOptions);
335  MeshPtr output = this->postProcess(mServices->patient(), mRawResult, input, colorOption->getValue());
336  mRawResult = NULL;
337 
338  if (output)
339  mOutputTypes.front()->setValue(output->getUid());
340 
341  return true;
342 }
343 
345 {
346  if (!contour || !base)
347  return MeshPtr();
348 
349  QString uid = base->getUid() + ContourFilter::getNameSuffix() + "%1";
350  QString name = base->getName()+ ContourFilter::getNameSuffix() + "%1";
351  MeshPtr output = patient->createSpecificData<Mesh>(uid, name);
352  output->setVtkPolyData(contour);
353  if (!output)
354  return MeshPtr();
355 
356  output->get_rMd_History()->setRegistration(base->get_rMd());
357  output->get_rMd_History()->setParentSpace(base->getUid());
358 
359  output->setColor(color);
360 
361  patient->insertData(output);
362 
363  return output;
364 }
365 
366 } // namespace cx
QString qstring_cast(const T &val)
vtkSmartPointer< class vtkDecimatePro > vtkDecimateProPtr
static BoolPropertyPtr initialize(const QString &uid, QString name, QString help, bool value, QDomNode root=QDomNode())
std::vector< SelectDataStringPropertyBasePtr > mInputTypes
Definition: cxFilterImpl.h:73
QDomElement mCopiedOptions
Definition: cxFilterImpl.h:80
DoublePropertyPtr getPassBandOption(QDomElement root)
A mesh data set.
Definition: cxMesh.h:45
DoublePropertyPtr getSurfaceThresholdOption(QDomElement root)
vtkSmartPointer< class vtkMarchingCubes > vtkMarchingCubesPtr
boost::shared_ptr< class ColorProperty > ColorPropertyPtr
virtual void createOptions()
boost::shared_ptr< class VisServices > VisServicesPtr
Definition: cxMainWindow.h:40
virtual void setActive(bool on)
virtual bool preProcess()
DoublePropertyPtr getNumberOfIterationsOption(QDomElement root)
Utility class for describing a bounded numeric range.
Definition: cxDoubleRange.h:32
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
vtkSmartPointer< class vtkWindowedSincPolyDataFilter > vtkWindowedSincPolyDataFilterPtr
ColorPropertyPtr getColorOption(QDomElement root)
BoolPropertyPtr getPreserveTopologyOption(QDomElement root)
std::vector< PropertyPtr > mOptionsAdapters
Definition: cxFilterImpl.h:75
VisServicesPtr mServices
Definition: cxFilterImpl.h:82
boost::shared_ptr< class SelectDataStringPropertyBase > SelectDataStringPropertyBasePtr
virtual void createInputTypes()
virtual QString getHelp() const
virtual bool execute()
static QString getNameSuffix()
void setVtkPolyData(const vtkPolyDataPtr &polyData)
Definition: cxMesh.cpp:91
virtual QString getType() const
virtual void createOutputTypes()
BoolPropertyPtr getSmoothingOption(QDomElement root)
A volumetric data set.
Definition: cxImage.h:45
vtkSmartPointer< class vtkPolyDataNormals > vtkPolyDataNormalsPtr
DoublePropertyPtr getDecimationOption(QDomElement root)
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
ContourFilter(VisServicesPtr services)
vtkSmartPointer< class vtkTriangleFilter > vtkTriangleFilterPtr
vtkSmartPointer< class vtkImageShrink3D > vtkImageShrink3DPtr
ImagePtr getCopiedInputImage(int index=0)
virtual void setActive(bool on)
QDomElement mOptions
Definition: cxFilterImpl.h:76
vtkSmartPointer< vtkPolyData > vtkPolyDataPtr
boost::shared_ptr< class DoubleProperty > DoublePropertyPtr
virtual QString getName() const
BoolPropertyPtr getReduceResolutionOption(QDomElement root)
std::vector< SelectDataStringPropertyBasePtr > mOutputTypes
Definition: cxFilterImpl.h:74
static DoublePropertyPtr initialize(const QString &uid, QString name, QString help, double value, DoubleRange range, int decimals, QDomNode root=QDomNode())
static StringPropertySelectMeshPtr New(PatientModelServicePtr patientModelService)
virtual bool postProcess()
static StringPropertySelectImagePtr New(PatientModelServicePtr patientModelService)
static ColorPropertyPtr initialize(const QString &uid, QString name, QString help, QColor value, QDomNode root=QDomNode())
boost::shared_ptr< class BoolProperty > BoolPropertyPtr
boost::shared_ptr< class Mesh > MeshPtr
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
void updateThresholdFromImageChange(QString uid, DoublePropertyPtr threshold)
void changed()
Namespace for all CustusX production code.