1 /*=========================================================================
4 Module: $RCSfile: vtkImagePolyDataSeedConnectivity.cxx,v $
6 Date: $Date: 2009/05/14 13:54:57 $
7 Version: $Revision: 1.1 $
9 Copyright: (c) 2002, 2003
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notice for more information.
16 =========================================================================*/
17 #include "vtkImagePolyDataSeedConnectivity.h"
19 #include <vtkPolyData.h>
20 #include <vtkImageData.h>
23 #include <vtkSphere.h>
24 #include <vtkImplicitBoolean.h>
25 #include <vtkImplicitFunctionToImageStencil.h>
26 #include <vtkImageStencil.h>
27 #include <vtkObjectFactory.h>
28 #include <vtkImageSeedConnectivity.h>
29 #include <vtkImageIterator.h>
30 #include <vtkImageGaussianSmooth.h>
31 #include <vtkMarchingCubes.h>
32 #include <vtkPolyDataConnectivityFilter.h>
33 #include <vtkCleanPolyData.h>
34 #include <vtkTriangleFilter.h>
35 #include <vtkPointData.h>
36 #include <vtkImageContinuousDilate3D.h>
38 vtkCxxRevisionMacro(vtkImagePolyDataSeedConnectivity, "$Revision: 1.1 $");
39 vtkStandardNewMacro(vtkImagePolyDataSeedConnectivity);
40 vtkCxxSetObjectMacro(vtkImagePolyDataSeedConnectivity, Axis,vtkPolyData);
42 //----------------------------------------------------------------------------
43 vtkImagePolyDataSeedConnectivity::vtkImagePolyDataSeedConnectivity()
46 this->ClipImageData = vtkImageData::New();
47 this->OuterMold = vtkPolyData::New();
48 this->ThresholdRatio = 0.45;
50 //----------------------------------------------------------------------------
51 vtkImagePolyDataSeedConnectivity::~vtkImagePolyDataSeedConnectivity()
57 this->ClipImageData->Delete();
58 this->ClipImageData = NULL;
59 this->OuterMold->Delete();
60 this->OuterMold = NULL;
63 * Utilisation de static pour accelérer l'execution ?
64 * cf: vtkDividingCubes.cxx
68 #define PSC_MIN(x,y) ((x)<(y) ? (x) : (y))
69 #define PSC_MAX(x,y) ((x)>(y) ? (x) : (y))
70 //----------------------------------------------------------------------------
71 void vtkImagePolyDataSeedConnectivity::Execute()
73 vtkImageData *input = this->GetInput();
74 vtkPolyData *output = this->GetOutput();
78 vtkDebugMacro(<< "Executing polydata seed connectivity...");
82 vtkErrorMacro(<<"Input is NULL");
86 // just deal with volumes
87 if ( input->GetDataDimension() != 3 )
89 vtkErrorMacro("Bad input: only treats 3D structured point datasets");
93 input->GetDimensions(dim);
95 //Allocate a temporary image that will be filled in b&w
96 //this is the base of the algothm, from a graysccale input + an axis
97 //we produce a b&w image the the marching cubes can contour:
99 vtkImageData *imagedata = vtkImageData::New();
100 imagedata->SetOrigin( input->GetOrigin() );
101 imagedata->SetSpacing( input->GetSpacing() );
102 imagedata->SetExtent( input->GetExtent() );
103 imagedata->SetScalarTypeToUnsignedChar (); //for connectityseed + reduce mem usage
104 imagedata->AllocateScalars();
107 Result: piece of crap !!
108 There was some leakage from another vessel that was near enough from the heart.
111 vtkImageSeedConnectivity *seed = vtkImageSeedConnectivity::New();
112 seed->SetInput( imagedata );
113 seed->SetInputConnectValue( 255 ); //If 0 -> inverse video, 255 -> rien !
115 // Before starting the loop we should make sure the image has been clipped
116 // with the polydata:
117 this->ClipImageWithAxis();
119 vtkPoints *points = this->Axis->GetPoints();
120 vtkDataArray* scalars = this->Axis->GetPointData()->GetScalars();
122 int numPts = points->GetNumberOfPoints();
123 double p1[3], p2[3], p3[3], tuple[2];
124 double radius, signalvalue;
126 int i_p2; //allow saving of p2 index
127 unsigned short* inSI, *inSIEnd;
128 unsigned char* outSI,*outSIEnd;
130 for(int i=0; i<numPts; i+=intervalle)
132 i_p2 = i+intervalle/2;
133 points->GetPoint(i, p1);
134 if(numPts <= i+2*intervalle)
137 points->GetPoint(numPts - 1, p3);
138 i = numPts; //no more iteration
142 points->GetPoint(i+intervalle, p3);
144 points->GetPoint(i_p2, p2);
146 //Now we can add p2 point to the see list:
147 //seed->AddSeed( 3, p2); //too bad :(
148 seed->AddSeed( (int)p2[0], (int)p2[1], (int)p2[2] );
150 //this parameter seems to be ok for most image
151 //furthermore it doesn't make any change to make it bigger.
152 scalars->GetTuple(i_p2, tuple);
154 signalvalue = tuple[1];
155 radius *= 4; //multiply for a factor 4 for aneurism case
157 region[0] = (int)PSC_MAX(p2[0] - radius, 0);
158 region[2] = (int)PSC_MAX(p2[1] - radius, 0);
159 region[4] = (int)PSC_MAX(p2[2] - radius, 0);
161 region[1] = (int)PSC_MIN(p2[0] + radius, dim[0] - 1);
162 region[3] = (int)PSC_MIN(p2[1] + radius, dim[1] - 1);
163 region[5] = (int)PSC_MIN(p2[2] + radius, dim[2] - 1);
165 // multiply signal by a factor this allow user to accuratley find the best
167 signalvalue *= ThresholdRatio;
170 * Instead of working on the real image, we might take advantage of
171 * class Stencil made by dgobbi
174 * - Create a fake disk (black) of radius ~ Optimale sphere and normal
175 * orientation = last point first eigen vector
176 * See: Disk-Stencil.py for more info
178 //Solution: this is too much a trouble: rather do it once on the entry (=first axis point)
180 vtkImageIterator<unsigned short> inIt(this->ClipImageData, region);//image_woheart
181 vtkImageIterator<unsigned char> outIt(imagedata, region);
182 // vtkImageProgressIterator should be nicer in conjonction with a wxGauge
184 // Loop through ouput pixels
185 while (!inIt.IsAtEnd())
187 inSI = inIt.BeginSpan(); inSIEnd = inIt.EndSpan();
188 outSI = outIt.BeginSpan(); outSIEnd = outIt.EndSpan();
191 while (inSI != inSIEnd)
192 *outSI++ = (signalvalue <= *inSI++) ? 255 : 0;
194 inIt.NextSpan(); outIt.NextSpan();
198 //NEW: introduced by Maciek/Marcela: use a vtkImageGaussian to get rid
199 //of the crenelage/steps introduced by MarchingCubes
201 vtkImageGaussianSmooth *gauss = vtkImageGaussianSmooth::New();
202 gauss->SetInput( seed->GetOutput() );
203 gauss->SetDimensionality(3);
204 //gauss->SetStandardDeviation(1.0);
205 //gauss->SetRadiusFactor(3.);
207 vtkMarchingCubes *contour = vtkMarchingCubes::New();
208 contour->SetInput ( gauss->GetOutput() );
209 contour->SetNumberOfContours( 1 );
210 contour->SetValue(0, 128);
211 contour->ComputeGradientsOn ();
212 contour->ComputeScalarsOn ();
214 vtkCleanPolyData *clean = vtkCleanPolyData::New();
215 clean->SetInput ( contour->GetOutput() );
217 vtkTriangleFilter *tf = vtkTriangleFilter::New();
218 tf->SetInput( clean->GetOutput() );
221 //Need to do a deep copy as we'll do a ShallowCopy afterwards
222 output->DeepCopy( tf->GetOutput() );
224 //output->Squeeze(); //usefull ?
226 // Now we do a dilatation of seed->GetOutput() in order to have inner (=output)
227 // and outer mold (=OuterMold):
229 * Internal function that take an input of a b&w or grayscale image (should be
230 * unsigned short/ unsigned char and apply a dilatation (see math morpho for
232 * NB: dilation o gaussian = gaussian o dilatation
235 //First apply a morpho math: dilatation of 2mm:
236 vtkImageContinuousDilate3D *dilate = vtkImageContinuousDilate3D ::New();
237 dilate->SetInput ( seed->GetOutput() );
238 //TODO: determine real kernel based on scale = cm/pixel
239 //See: http://public.kitware.com/pipermail/vtkusers/2000-September/004210.html
240 //[Spacing is x and y is the distance between the samples. The units of measure
241 //are your choice. Often medical images use millimeters. Let's say your medical
242 //data resolution is 512 x 512 and your study has a 250 mm field of view. Then
243 //the spacing in x and y would be 250/512 or .488 mm.]
244 //therefore we have here:
248 seed->GetOutput()->GetSpacing( spacing ); //beware input should be Update()
249 for(int j=0; j<3; j++)
251 //2mm thickness, we assume spacing units is in mm !!
252 kernelsize[j] = 3; //(int) (2. / spacing[i]);
253 //std::cout << "kernel=" << kernelsize[i] << ",et:" << (int)(2./0.3334) << ",";
254 //beware if spacing=0.3334 -> 2/0.3334 = 5 !!!! bad bad +/-0.5 to see
256 dilate->SetKernelSize (kernelsize[0], kernelsize[1], kernelsize[2]);
258 //VTK filters rules !!!
259 gauss->SetInput( dilate->GetOutput());
261 // shallow copy result into output.
262 // Could be re-written, as we know there is no vert neither strips...
263 OuterMold->ShallowCopy( tf->GetOutput() );
265 //OuterMold->Squeeze(); //usefull ?
268 imagedata -> Delete();
276 //TODO: if this filter is used many times when working on different image size
277 //I guess we need to find something brighter for this->ClipImageData
282 //----------------------------------------------------------------------------
283 //We need to add a function that from the raw data is able to clip with
284 //the begining and ending of polydata to produce sharp edge.
285 void vtkImagePolyDataSeedConnectivity::ClipImageWithAxis()
287 //no input need we'll work with member data:
288 //Now we only need the region growing part:
290 * Instead of working directly on leo we might remove the heart by a trick:
291 * If we could cut with a disk at place where the last(first) axis point is
292 * we could avoid the flood fill algo to spread in the heart...smart !
296 * First we construct a virtual disk (=half sphere for now)
297 * See: Disk-Stencil.py
303 * Get first point (p2) and second point (p1) : assume it exists !
304 * Then calculate vector p2->p1
306 * use it to define intersection plane between sphere and plane
308 this->Axis->Update();
309 vtkPoints *points = this->Axis->GetPoints();
310 int numPts = points->GetNumberOfPoints();
314 points->GetPoint( 0, p2 );
315 points->GetPoint( 1, p1 );
317 normal[0] = p1[0] - p2[0];
318 normal[1] = p1[1] - p2[1];
319 normal[2] = p1[2] - p2[2];
320 vtkMath::Normalize( normal );
322 vtkPlane *plane1 = vtkPlane::New();
323 plane1->SetOrigin( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
324 plane1->SetNormal( normal[0], normal[1], normal[2]);
326 vtkDataArray* scalars = this->Axis->GetPointData()->GetScalars();
328 scalars->GetTuple(0, tuple);
329 double radius = tuple[0];
331 vtkSphere *sphere1 = vtkSphere::New();
332 sphere1->SetCenter( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
333 sphere1->SetRadius( radius + 1);
335 vtkImplicitBoolean *disk1 = vtkImplicitBoolean::New();
336 disk1->SetOperationTypeToIntersection();
337 disk1->AddFunction(sphere1);
338 disk1->AddFunction(plane1);
342 * Get last point (p2) and before last point (p1) : assume it exists !
343 * Then calculate vector p2->p1
345 * use it to define intersection plane between sphere and plane
348 //ax->getControlPoint(numPts - 1, p2, p2+3);
349 points->GetPoint( numPts - 1, p2);
350 //ax->getControlPoint(numPts - 2, p1, p1+3); //assume it exists
351 points->GetPoint( numPts - 2, p1);
352 normal[0] = p1[0] - p2[0];
353 normal[1] = p1[1] - p2[1];
354 normal[2] = p1[2] - p2[2];
355 vtkMath::Normalize( normal );
357 vtkPlane *plane2 = vtkPlane::New();
358 plane2->SetOrigin( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
359 plane2->SetNormal( normal[0], normal[1], normal[2]);
361 vtkSphere *sphere2 = vtkSphere::New();
362 sphere2->SetCenter( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
363 sphere2->SetRadius( radius + 1 );
365 vtkImplicitBoolean *disk2 = vtkImplicitBoolean::New();
366 disk2->SetOperationTypeToIntersection();
367 disk2->AddFunction(sphere2);
368 disk2->AddFunction(plane2);
370 vtkImplicitBoolean *disk = vtkImplicitBoolean::New();
371 disk->SetOperationTypeToUnion();
372 disk->AddFunction(disk1);
373 disk->AddFunction(disk2);
376 * Now use dgobbi class: Stencil stuff to remove the 'bad' heart part
379 vtkImplicitFunctionToImageStencil *functionToStencil = vtkImplicitFunctionToImageStencil::New ();
380 functionToStencil->SetInput ( disk );
382 vtkImageStencil *stencil = vtkImageStencil::New();
383 stencil->SetInput( this->GetInput() );
384 stencil->SetStencil( functionToStencil->GetOutput() );
385 stencil->ReverseStencilOn();
386 stencil->SetBackgroundValue (0);
387 //don't forget to call ->Update before using an iterator !!!
390 this->ClipImageData->ShallowCopy( stencil->GetOutput() );
393 * Should I do my own vtkCappedCylinder ??
394 * solution: no , cause ... I can't remember why... :p
405 functionToStencil -> Delete();
408 //----------------------------------------------------------------------------
409 void vtkImagePolyDataSeedConnectivity::PrintSelf(ostream& os, vtkIndent indent)
411 this->Superclass::PrintSelf(os,indent);
414 os << indent << "Axis of " << this->Axis->GetNumberOfPoints()
415 << "points defined\n";
419 os << indent << "Axis not defined\n";