NorMIT-nav  18.04
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  mReduceResolutionOption = this->getReduceResolutionOption(mOptions);
116  mOptionsAdapters.push_back(mReduceResolutionOption);
117 
118  mSurfaceThresholdOption = this->getSurfaceThresholdOption(mOptions);
119  connect(mSurfaceThresholdOption.get(), SIGNAL(changed()), this, SLOT(thresholdSlot()));
120  mOptionsAdapters.push_back(mSurfaceThresholdOption);
121 
122  mOptionsAdapters.push_back(this->getSmoothingOption(mOptions));
125 
126  mOptionsAdapters.push_back(this->getColorOption(mOptions));
127 }
128 
130 {
132 
133  temp = StringPropertySelectImage::New(mServices->patient());
134  temp->setValueName("Input");
135  temp->setHelp("Select image input for contouring");
136  connect(temp.get(), SIGNAL(dataChanged(QString)), this, SLOT(imageChangedSlot(QString)));
137  mInputTypes.push_back(temp);
138 }
139 
141 {
143 
144  temp = StringPropertySelectMesh::New(mServices->patient());
145  temp->setValueName("Output");
146  temp->setHelp("Output contour");
147  mOutputTypes.push_back(temp);
148 }
149 
151 {
153 
154  if (!mActive)
155  this->stopPreview();
156 }
157 
158 void ContourFilter::stopPreview()
159 {
160  if(mPreviewImage)
161  mPreviewImage->stopThresholdPreview();
162  mPreviewImage.reset();
163 }
164 
165 void ContourFilter::imageChangedSlot(QString uid)
166 {
167  ImagePtr image = mServices->patient()->getData<Image>(uid);
168  if(!image)
169  return;
170 
171  this->updateThresholdFromImageChange(uid, mSurfaceThresholdOption);
172  this->stopPreview();
173 
174  int extent[6];
175  image->getBaseVtkImageData()->GetExtent(extent);
176  mReduceResolutionOption->setHelp("Current input resolution: " + qstring_cast(extent[1])
177  + " " + qstring_cast(extent[3]) + " " + qstring_cast(extent[5])
178  + " (If checked: " + qstring_cast(extent[1]/2)+ " " + qstring_cast(extent[3]/2) + " "
179  + qstring_cast(extent[5]/2) + ")");
180 }
181 
182 void ContourFilter::thresholdSlot()
183 {
184  if (mActive)
185  {
186  mPreviewImage = boost::dynamic_pointer_cast<Image>(mInputTypes[0]->getData());
187  if(mPreviewImage)
188  {
189  Eigen::Vector2d threshold = Eigen::Vector2d(mSurfaceThresholdOption->getValue(), mPreviewImage->getMax());
190  mPreviewImage->startThresholdPreview(threshold);
191  }
192  }
193 }
194 
196 {
197  this->stopPreview();
198  return FilterImpl::preProcess();
199 }
200 
202 {
203  ImagePtr input = this->getCopiedInputImage();
204  if (!input)
205  return false;
206 
207  // std::cout << "ContourFilter::execute : " << mCopiedOptions.ownerDocument().toString() << std::endl;
208 
209  BoolPropertyPtr reduceResolutionOption = this->getReduceResolutionOption(mCopiedOptions);
210  BoolPropertyPtr smoothingOption = this->getSmoothingOption(mCopiedOptions);
211  BoolPropertyPtr preserveTopologyOption = this->getPreserveTopologyOption(mCopiedOptions);
212  DoublePropertyPtr surfaceThresholdOption = this->getSurfaceThresholdOption(mCopiedOptions);
213  DoublePropertyPtr decimationOption = this->getDecimationOption(mCopiedOptions);
214 
215  // report(QString("Creating contour from \"%1\"...").arg(input->getName()));
216 
217  mRawResult = this->execute(input->getBaseVtkImageData(),
218  surfaceThresholdOption->getValue(),
219  reduceResolutionOption->getValue(),
220  smoothingOption->getValue(),
221  preserveTopologyOption->getValue(),
222  decimationOption->getValue());
223  return true;
224 }
225 
227  double threshold,
228  bool reduceResolution,
229  bool smoothing,
230  bool preserveTopology,
231  double decimation)
232 {
233  if (!input)
234  return vtkPolyDataPtr();
235 
236  //Shrink input volume
237  vtkImageShrink3DPtr shrinker = vtkImageShrink3DPtr::New();
238  if(reduceResolution)
239  {
240 // std::cout << "smooth" << std::endl;
241  shrinker->SetInputData(input);
242  shrinker->SetShrinkFactors(2,2,2);
243  shrinker->Update();
244  }
245 
246  // Find countour
247  vtkMarchingCubesPtr convert = vtkMarchingCubesPtr::New();
248  if(reduceResolution)
249  convert->SetInputConnection(shrinker->GetOutputPort());
250  else
251  convert->SetInputData(input);
252 
253  convert->SetValue(0, threshold);
254  convert->Update();
255 
256  vtkPolyDataPtr cubesPolyData = convert->GetOutput();
257 // vtkPolyDataPtr cubesPolyData = vtkPolyDataPtr::New();
258 // cubesPolyData = convert->GetOutput();
259 // std::cout << "convert->GetOutput(); " << cubesPolyData.GetPointer() << std::endl;
260 
261  // Smooth surface model
262  vtkWindowedSincPolyDataFilterPtr smoother = vtkWindowedSincPolyDataFilterPtr::New();
263  if(smoothing)
264  {
265  smoother->SetInputData(cubesPolyData);
266  smoother->SetNumberOfIterations(15);// Higher number = more smoothing
267  smoother->SetBoundarySmoothing(false);
268  smoother->SetFeatureEdgeSmoothing(false);
269  smoother->SetNormalizeCoordinates(true);
270  smoother->SetFeatureAngle(120);
271  smoother->SetPassBand(0.3);//Lower number = more smoothing
272  smoother->Update();
273  cubesPolyData = smoother->GetOutput();
274  }
275 
276  //Create a surface of triangles
277 
278  //Decimate surface model (remove a percentage of the polygons)
279  vtkTriangleFilterPtr trifilt = vtkTriangleFilterPtr::New();
280  vtkDecimateProPtr deci = vtkDecimateProPtr::New();
281  vtkPolyDataNormalsPtr normals = vtkPolyDataNormalsPtr::New();
282  if (decimation > 0.000001)
283  {
284  trifilt->SetInputData(cubesPolyData);
285  trifilt->Update();
286  deci->SetInputConnection(trifilt->GetOutputPort());
287  deci->SetTargetReduction(decimation);
288  deci->SetPreserveTopology(preserveTopology);
289  // deci->PreserveTopologyOn();
290  deci->Update();
291  cubesPolyData = deci->GetOutput();
292  }
293 
294  normals->SetInputData(cubesPolyData);
295  normals->Update();
296 
297  cubesPolyData->DeepCopy(normals->GetOutput());
298 
299  return cubesPolyData;
300 }
301 
303 {
304  if (!mRawResult)
305  return false;
306 
307  ImagePtr input = this->getCopiedInputImage();
308 
309  if (!input)
310  return false;
311 
312  ColorPropertyPtr colorOption = this->getColorOption(mOptions);
313  MeshPtr output = this->postProcess(mServices->patient(), mRawResult, input, colorOption->getValue());
314  mRawResult = NULL;
315 
316  if (output)
317  mOutputTypes.front()->setValue(output->getUid());
318 
319  return true;
320 }
321 
323 {
324  if (!contour || !base)
325  return MeshPtr();
326 
327  QString uid = base->getUid() + ContourFilter::getNameSuffix() + "%1";
328  QString name = base->getName()+ ContourFilter::getNameSuffix() + "%1";
329  MeshPtr output = patient->createSpecificData<Mesh>(uid, name);
330  output->setVtkPolyData(contour);
331  if (!output)
332  return MeshPtr();
333 
334  output->get_rMd_History()->setRegistration(base->get_rMd());
335  output->get_rMd_History()->setParentSpace(base->getUid());
336 
337  output->setColor(color);
338 
339  patient->insertData(output);
340 
341  return output;
342 }
343 
344 } // 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
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()
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.