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