]> Creatis software - creaMaracasVisu.git/blob - lib/maracasVisuLib/src/interface/wxWindows/widgets/include/vtkImagePolyDataSeedConnectivity.cxx
DFCH
[creaMaracasVisu.git] / lib / maracasVisuLib / src / interface / wxWindows / widgets / include / vtkImagePolyDataSeedConnectivity.cxx
1 /*=========================================================================
2
3   Program:   wxMaracas
4   Module:    $RCSfile: vtkImagePolyDataSeedConnectivity.cxx,v $
5   Language:  C++
6   Date:      $Date: 2009/05/14 13:54:57 $
7   Version:   $Revision: 1.1 $
8
9   Copyright: (c) 2002, 2003
10   License:
11   
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.
15
16 =========================================================================*/
17 #include "vtkImagePolyDataSeedConnectivity.h"
18
19 #include <vtkPolyData.h>
20 #include <vtkImageData.h>
21 #include <vtkMath.h>
22 #include <vtkPlane.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>
37
38 vtkCxxRevisionMacro(vtkImagePolyDataSeedConnectivity, "$Revision: 1.1 $");
39 vtkStandardNewMacro(vtkImagePolyDataSeedConnectivity);
40 vtkCxxSetObjectMacro(vtkImagePolyDataSeedConnectivity, Axis,vtkPolyData);
41
42 //----------------------------------------------------------------------------
43 vtkImagePolyDataSeedConnectivity::vtkImagePolyDataSeedConnectivity()
44 {
45   this->Axis = NULL;
46   this->ClipImageData = vtkImageData::New();
47   this->OuterMold = vtkPolyData::New();
48   this->ThresholdRatio = 0.45;
49 }
50 //----------------------------------------------------------------------------
51 vtkImagePolyDataSeedConnectivity::~vtkImagePolyDataSeedConnectivity()
52 {
53   if(this->Axis)
54   {
55     this->Axis->Delete();
56   }
57   this->ClipImageData->Delete();
58   this->ClipImageData = NULL;
59   this->OuterMold->Delete();
60   this->OuterMold = NULL;
61 }
62  /**
63   * Utilisation de static pour accelĂ©rer l'execution ?
64   * cf: vtkDividingCubes.cxx
65   */
66
67 //Macros definitions
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()
72 {
73   vtkImageData *input = this->GetInput();
74   vtkPolyData *output = this->GetOutput();
75   int intervalle = 2;
76   int dim[3];
77
78   vtkDebugMacro(<< "Executing polydata seed connectivity...");
79
80   if (input == NULL)
81     {
82     vtkErrorMacro(<<"Input is NULL");
83     return;
84     }
85
86   // just deal with volumes
87   if ( input->GetDataDimension() != 3 )
88     {
89     vtkErrorMacro("Bad input: only treats 3D structured point datasets");
90     return;
91     }
92
93   input->GetDimensions(dim);
94
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:
98
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();
105
106   /*
107   Result: piece of crap !!
108   There was some leakage from another vessel that was near enough from the heart.
109   */
110
111   vtkImageSeedConnectivity *seed = vtkImageSeedConnectivity::New();
112   seed->SetInput( imagedata );
113   seed->SetInputConnectValue( 255 ); //If 0 -> inverse video, 255 -> rien !
114
115   // Before starting the loop we should make sure the image has been clipped
116   // with the polydata:
117   this->ClipImageWithAxis();
118
119   vtkPoints *points = this->Axis->GetPoints();
120   vtkDataArray* scalars = this->Axis->GetPointData()->GetScalars();
121   
122   int numPts = points->GetNumberOfPoints();
123   double p1[3], p2[3], p3[3], tuple[2];
124   double radius, signalvalue;
125   int region[6];
126   int i_p2; //allow saving of p2 index
127   unsigned short* inSI, *inSIEnd;
128   unsigned char*  outSI,*outSIEnd;
129     
130   for(int i=0; i<numPts; i+=intervalle)
131   {
132     i_p2 = i+intervalle/2;
133     points->GetPoint(i, p1);
134     if(numPts <= i+2*intervalle)
135     {
136       i_p2 = i+intervalle;
137       points->GetPoint(numPts - 1, p3);
138       i = numPts;       //no more iteration
139     }
140     else
141     {
142       points->GetPoint(i+intervalle, p3);
143     }
144     points->GetPoint(i_p2, p2);
145     
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] );
149
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);
153     radius              = tuple[0]; 
154         signalvalue = tuple[1];
155     radius *= 4;                //multiply for a factor 4 for aneurism case
156
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);
160
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);
164
165     // multiply signal by a factor this allow user to accuratley find the best
166     // range
167     signalvalue *= ThresholdRatio;  
168
169       /**
170        * Instead of working on the real image, we might take advantage of
171        * class Stencil made by dgobbi
172        *
173        * Steps:
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
177        */
178       //Solution: this is too much a trouble: rather do it once on the entry (=first  axis point)
179
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
183
184       // Loop through ouput pixels
185       while (!inIt.IsAtEnd())
186       {
187         inSI = inIt.BeginSpan(); inSIEnd = inIt.EndSpan();
188         outSI = outIt.BeginSpan(); outSIEnd = outIt.EndSpan();
189
190         // Pixel operation
191         while (inSI != inSIEnd)
192           *outSI++ = (signalvalue <= *inSI++) ? 255 : 0;
193
194         inIt.NextSpan(); outIt.NextSpan();
195       }
196    }
197    
198   //NEW: introduced by Maciek/Marcela: use a vtkImageGaussian to get rid 
199   //of the crenelage/steps introduced by MarchingCubes
200   
201   vtkImageGaussianSmooth *gauss = vtkImageGaussianSmooth::New();
202   gauss->SetInput( seed->GetOutput() );
203   gauss->SetDimensionality(3);
204   //gauss->SetStandardDeviation(1.0);
205   //gauss->SetRadiusFactor(3.);
206
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 ();
213
214   vtkCleanPolyData *clean = vtkCleanPolyData::New();
215   clean->SetInput ( contour->GetOutput() );
216
217   vtkTriangleFilter *tf = vtkTriangleFilter::New();
218   tf->SetInput( clean->GetOutput() );
219   tf->Update();
220
221   //Need to do a deep copy as we'll do a ShallowCopy afterwards
222   output->DeepCopy( tf->GetOutput() );
223
224   //output->Squeeze();  //usefull ?
225
226   // Now we do a dilatation of seed->GetOutput() in order to have inner (=output)
227   // and outer mold (=OuterMold):
228  /**
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
231   * info).
232   * NB: dilation o gaussian = gaussian o dilatation 
233   */
234
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:
245
246   int kernelsize[3];    
247   double spacing[3];
248   seed->GetOutput()->GetSpacing( spacing );  //beware input should be Update()
249   for(int j=0; j<3; j++)
250   {
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
255   }
256   dilate->SetKernelSize (kernelsize[0], kernelsize[1], kernelsize[2]);
257
258   //VTK filters rules !!!
259   gauss->SetInput( dilate->GetOutput());
260   tf->Update();
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() );
264   
265   //OuterMold->Squeeze();  //usefull ?
266
267   //Delete temp stuff:
268   imagedata             -> Delete();
269   seed                  -> Delete();
270   gauss                 -> Delete();
271   contour               -> Delete();
272   clean                 -> Delete();
273   tf                    -> Delete();
274   dilate                -> Delete();
275   
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
278 }
279 //end of macros use
280 #undef PSC_MAX
281 #undef PSC_MIN
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()
286 {
287   //no input need we'll work with member data:
288   //Now we only need the region growing part:
289  /**
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  !
293   */
294
295  /**
296   * First we construct a virtual disk (=half sphere for now)
297   * See: Disk-Stencil.py
298   */
299   double normal[3];
300
301  /**
302   * What we do here:
303   * Get first point (p2) and second point (p1) : assume it exists !
304   * Then calculate vector p2->p1
305   * normalize it
306   * use it to define intersection plane between sphere and plane
307   */
308   this->Axis->Update();
309   vtkPoints *points = this->Axis->GetPoints();
310   int numPts = points->GetNumberOfPoints();
311   double p1[3];
312   double p2[3];
313   
314   points->GetPoint( 0, p2 );
315   points->GetPoint( 1, p1 );
316   
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 );
321
322   vtkPlane *plane1 = vtkPlane::New();
323   plane1->SetOrigin( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
324   plane1->SetNormal( normal[0], normal[1], normal[2]);
325
326   vtkDataArray* scalars = this->Axis->GetPointData()->GetScalars();
327   double tuple[2];
328   scalars->GetTuple(0, tuple);
329   double radius = tuple[0];
330
331    vtkSphere *sphere1 = vtkSphere::New();
332    sphere1->SetCenter( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
333    sphere1->SetRadius( radius + 1);
334
335    vtkImplicitBoolean *disk1 = vtkImplicitBoolean::New();
336    disk1->SetOperationTypeToIntersection();
337    disk1->AddFunction(sphere1);
338    disk1->AddFunction(plane1);
339
340  /**
341   * What we do here:
342   * Get last point (p2) and before last point (p1) : assume it exists !
343   * Then calculate vector p2->p1
344   * normalize it
345   * use it to define intersection plane between sphere and plane
346   */
347
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 );
356
357   vtkPlane *plane2 = vtkPlane::New();
358   plane2->SetOrigin( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
359   plane2->SetNormal( normal[0], normal[1], normal[2]);
360
361   vtkSphere *sphere2 = vtkSphere::New();
362   sphere2->SetCenter( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
363   sphere2->SetRadius( radius + 1 );
364
365   vtkImplicitBoolean *disk2 = vtkImplicitBoolean::New();
366   disk2->SetOperationTypeToIntersection();
367   disk2->AddFunction(sphere2);
368   disk2->AddFunction(plane2);
369
370   vtkImplicitBoolean *disk = vtkImplicitBoolean::New();
371   disk->SetOperationTypeToUnion();
372   disk->AddFunction(disk1);
373   disk->AddFunction(disk2);
374
375  /**
376   * Now use dgobbi class: Stencil stuff to remove the 'bad' heart part
377   */
378
379   vtkImplicitFunctionToImageStencil *functionToStencil = vtkImplicitFunctionToImageStencil::New ();
380   functionToStencil->SetInput ( disk );
381
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 !!!
388   stencil->Update();
389   
390   this->ClipImageData->ShallowCopy( stencil->GetOutput() );
391   
392  /**
393   * Should I do my own vtkCappedCylinder ??
394   * solution: no , cause ... I can't remember why... :p
395   */
396   
397   //Delete stuff:
398   plane1                        -> Delete();
399   sphere1                       -> Delete();
400   disk1                         -> Delete();
401   plane2                        -> Delete();
402   sphere2                       -> Delete();
403   disk2                         -> Delete();
404   disk                          -> Delete();
405   functionToStencil     -> Delete();
406   stencil                       -> Delete();
407 }
408 //----------------------------------------------------------------------------
409 void vtkImagePolyDataSeedConnectivity::PrintSelf(ostream& os, vtkIndent indent)
410 {
411   this->Superclass::PrintSelf(os,indent);
412   if ( this->Axis )
413     {
414     os << indent << "Axis of " << this->Axis->GetNumberOfPoints()
415        << "points defined\n";
416     }
417   else
418     {
419     os << indent << "Axis not defined\n";
420     }
421 }
422
423