]> Creatis software - creaMaracasVisu.git/blob - lib/maracasVisuLib/src/interface/wxWindows/widgets/include/vtkImagePolyDataSeedConnectivity.cxx
Support #1768 CREATIS Licence insertion
[creaMaracasVisu.git] / lib / maracasVisuLib / src / interface / wxWindows / widgets / include / vtkImagePolyDataSeedConnectivity.cxx
1 /*# ---------------------------------------------------------------------
2 #
3 # Copyright (c) CREATIS (Centre de Recherche en Acquisition et Traitement de l'Image
4 #                        pour la Sant�)
5 # Authors : Eduardo Davila, Frederic Cervenansky, Claire Mouton
6 # Previous Authors : Laurent Guigues, Jean-Pierre Roux
7 # CreaTools website : www.creatis.insa-lyon.fr/site/fr/creatools_accueil
8 #
9 #  This software is governed by the CeCILL-B license under French law and
10 #  abiding by the rules of distribution of free software. You can  use,
11 #  modify and/ or redistribute the software under the terms of the CeCILL-B
12 #  license as circulated by CEA, CNRS and INRIA at the following URL
13 #  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
14 #  or in the file LICENSE.txt.
15 #
16 #  As a counterpart to the access to the source code and  rights to copy,
17 #  modify and redistribute granted by the license, users are provided only
18 #  with a limited warranty  and the software's author,  the holder of the
19 #  economic rights,  and the successive licensors  have only  limited
20 #  liability.
21 #
22 #  The fact that you are presently reading this means that you have had
23 #  knowledge of the CeCILL-B license and that you accept its terms.
24 # ------------------------------------------------------------------------ */
25
26 /*=========================================================================
27
28   Program:   wxMaracas
29   Module:    $RCSfile: vtkImagePolyDataSeedConnectivity.cxx,v $
30   Language:  C++
31   Date:      $Date: 2012/11/15 14:15:18 $
32   Version:   $Revision: 1.2 $
33
34   Copyright: (c) 2002, 2003
35   License:
36   
37      This software is distributed WITHOUT ANY WARRANTY; without even 
38      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
39      PURPOSE.  See the above copyright notice for more information.
40
41 =========================================================================*/
42 #include "vtkImagePolyDataSeedConnectivity.h"
43
44 #include <vtkPolyData.h>
45 #include <vtkImageData.h>
46 #include <vtkMath.h>
47 #include <vtkPlane.h>
48 #include <vtkSphere.h>
49 #include <vtkImplicitBoolean.h>
50 #include <vtkImplicitFunctionToImageStencil.h>
51 #include <vtkImageStencil.h>
52 #include <vtkObjectFactory.h>
53 #include <vtkImageSeedConnectivity.h>
54 #include <vtkImageIterator.h>
55 #include <vtkImageGaussianSmooth.h>
56 #include <vtkMarchingCubes.h>
57 #include <vtkPolyDataConnectivityFilter.h>
58 #include <vtkCleanPolyData.h>
59 #include <vtkTriangleFilter.h>
60 #include <vtkPointData.h>
61 #include <vtkImageContinuousDilate3D.h>
62
63 vtkCxxRevisionMacro(vtkImagePolyDataSeedConnectivity, "$Revision: 1.2 $");
64 vtkStandardNewMacro(vtkImagePolyDataSeedConnectivity);
65 vtkCxxSetObjectMacro(vtkImagePolyDataSeedConnectivity, Axis,vtkPolyData);
66
67 //----------------------------------------------------------------------------
68 vtkImagePolyDataSeedConnectivity::vtkImagePolyDataSeedConnectivity()
69 {
70   this->Axis = NULL;
71   this->ClipImageData = vtkImageData::New();
72   this->OuterMold = vtkPolyData::New();
73   this->ThresholdRatio = 0.45;
74 }
75 //----------------------------------------------------------------------------
76 vtkImagePolyDataSeedConnectivity::~vtkImagePolyDataSeedConnectivity()
77 {
78   if(this->Axis)
79   {
80     this->Axis->Delete();
81   }
82   this->ClipImageData->Delete();
83   this->ClipImageData = NULL;
84   this->OuterMold->Delete();
85   this->OuterMold = NULL;
86 }
87  /**
88   * Utilisation de static pour accel�rer l'execution ?
89   * cf: vtkDividingCubes.cxx
90   */
91
92 //Macros definitions
93 #define PSC_MIN(x,y)      ((x)<(y) ? (x) : (y))
94 #define PSC_MAX(x,y)      ((x)>(y) ? (x) : (y))
95 //----------------------------------------------------------------------------
96 void vtkImagePolyDataSeedConnectivity::Execute()
97 {
98   vtkImageData *input = this->GetInput();
99   vtkPolyData *output = this->GetOutput();
100   int intervalle = 2;
101   int dim[3];
102
103   vtkDebugMacro(<< "Executing polydata seed connectivity...");
104
105   if (input == NULL)
106     {
107     vtkErrorMacro(<<"Input is NULL");
108     return;
109     }
110
111   // just deal with volumes
112   if ( input->GetDataDimension() != 3 )
113     {
114     vtkErrorMacro("Bad input: only treats 3D structured point datasets");
115     return;
116     }
117
118   input->GetDimensions(dim);
119
120   //Allocate a temporary image that will be filled in b&w
121   //this is the base of the algothm, from a graysccale input + an axis
122   //we produce a b&w image the the marching cubes can contour:
123
124   vtkImageData *imagedata = vtkImageData::New();
125   imagedata->SetOrigin( input->GetOrigin() );
126   imagedata->SetSpacing( input->GetSpacing() );
127   imagedata->SetExtent( input->GetExtent() );
128   imagedata->SetScalarTypeToUnsignedChar (); //for connectityseed + reduce mem usage
129   imagedata->AllocateScalars();
130
131   /*
132   Result: piece of crap !!
133   There was some leakage from another vessel that was near enough from the heart.
134   */
135
136   vtkImageSeedConnectivity *seed = vtkImageSeedConnectivity::New();
137   seed->SetInput( imagedata );
138   seed->SetInputConnectValue( 255 ); //If 0 -> inverse video, 255 -> rien !
139
140   // Before starting the loop we should make sure the image has been clipped
141   // with the polydata:
142   this->ClipImageWithAxis();
143
144   vtkPoints *points = this->Axis->GetPoints();
145   vtkDataArray* scalars = this->Axis->GetPointData()->GetScalars();
146   
147   int numPts = points->GetNumberOfPoints();
148   double p1[3], p2[3], p3[3], tuple[2];
149   double radius, signalvalue;
150   int region[6];
151   int i_p2; //allow saving of p2 index
152   unsigned short* inSI, *inSIEnd;
153   unsigned char*  outSI,*outSIEnd;
154     
155   for(int i=0; i<numPts; i+=intervalle)
156   {
157     i_p2 = i+intervalle/2;
158     points->GetPoint(i, p1);
159     if(numPts <= i+2*intervalle)
160     {
161       i_p2 = i+intervalle;
162       points->GetPoint(numPts - 1, p3);
163       i = numPts;       //no more iteration
164     }
165     else
166     {
167       points->GetPoint(i+intervalle, p3);
168     }
169     points->GetPoint(i_p2, p2);
170     
171     //Now we can add p2 point to the see list:
172     //seed->AddSeed( 3, p2); //too bad :(
173     seed->AddSeed( (int)p2[0], (int)p2[1], (int)p2[2] );
174
175     //this parameter seems to be ok for most image
176     //furthermore it doesn't make any change to make it bigger.
177     scalars->GetTuple(i_p2, tuple);
178     radius              = tuple[0]; 
179         signalvalue = tuple[1];
180     radius *= 4;                //multiply for a factor 4 for aneurism case
181
182     region[0] = (int)PSC_MAX(p2[0] - radius, 0);
183     region[2] = (int)PSC_MAX(p2[1] - radius, 0);
184     region[4] = (int)PSC_MAX(p2[2] - radius, 0);
185
186     region[1] = (int)PSC_MIN(p2[0] + radius, dim[0] - 1);
187     region[3] = (int)PSC_MIN(p2[1] + radius, dim[1] - 1);
188     region[5] = (int)PSC_MIN(p2[2] + radius, dim[2] - 1);
189
190     // multiply signal by a factor this allow user to accuratley find the best
191     // range
192     signalvalue *= ThresholdRatio;  
193
194       /**
195        * Instead of working on the real image, we might take advantage of
196        * class Stencil made by dgobbi
197        *
198        * Steps:
199        *  - Create a fake disk (black) of radius ~ Optimale sphere and normal
200        *    orientation = last point first eigen vector
201        * See: Disk-Stencil.py for more info
202        */
203       //Solution: this is too much a trouble: rather do it once on the entry (=first  axis point)
204
205       vtkImageIterator<unsigned short> inIt(this->ClipImageData, region);//image_woheart
206       vtkImageIterator<unsigned char> outIt(imagedata, region);
207       // vtkImageProgressIterator should be nicer in conjonction with a wxGauge
208
209       // Loop through ouput pixels
210       while (!inIt.IsAtEnd())
211       {
212         inSI = inIt.BeginSpan(); inSIEnd = inIt.EndSpan();
213         outSI = outIt.BeginSpan(); outSIEnd = outIt.EndSpan();
214
215         // Pixel operation
216         while (inSI != inSIEnd)
217           *outSI++ = (signalvalue <= *inSI++) ? 255 : 0;
218
219         inIt.NextSpan(); outIt.NextSpan();
220       }
221    }
222    
223   //NEW: introduced by Maciek/Marcela: use a vtkImageGaussian to get rid 
224   //of the crenelage/steps introduced by MarchingCubes
225   
226   vtkImageGaussianSmooth *gauss = vtkImageGaussianSmooth::New();
227   gauss->SetInput( seed->GetOutput() );
228   gauss->SetDimensionality(3);
229   //gauss->SetStandardDeviation(1.0);
230   //gauss->SetRadiusFactor(3.);
231
232   vtkMarchingCubes *contour = vtkMarchingCubes::New();
233   contour->SetInput ( gauss->GetOutput() );
234   contour->SetNumberOfContours( 1 );
235   contour->SetValue(0, 128);
236   contour->ComputeGradientsOn ();
237   contour->ComputeScalarsOn ();
238
239   vtkCleanPolyData *clean = vtkCleanPolyData::New();
240   clean->SetInput ( contour->GetOutput() );
241
242   vtkTriangleFilter *tf = vtkTriangleFilter::New();
243   tf->SetInput( clean->GetOutput() );
244   tf->Update();
245
246   //Need to do a deep copy as we'll do a ShallowCopy afterwards
247   output->DeepCopy( tf->GetOutput() );
248
249   //output->Squeeze();  //usefull ?
250
251   // Now we do a dilatation of seed->GetOutput() in order to have inner (=output)
252   // and outer mold (=OuterMold):
253  /**
254   * Internal function that take an input of a b&w or grayscale image (should be 
255   * unsigned short/ unsigned char and apply a dilatation (see math morpho for
256   * info).
257   * NB: dilation o gaussian = gaussian o dilatation 
258   */
259
260   //First apply a morpho math: dilatation of 2mm:
261   vtkImageContinuousDilate3D *dilate = vtkImageContinuousDilate3D ::New();
262   dilate->SetInput ( seed->GetOutput() );
263   //TODO: determine real kernel based on scale = cm/pixel
264   //See: http://public.kitware.com/pipermail/vtkusers/2000-September/004210.html
265   //[Spacing is x and y is the distance between the samples. The units of measure
266   //are your choice. Often medical images use millimeters. Let's say your medical
267   //data resolution is 512 x 512 and your study has a 250 mm field of view. Then
268   //the spacing in x and y would be 250/512 or .488 mm.]
269   //therefore we have here:
270
271   int kernelsize[3];    
272   double spacing[3];
273   seed->GetOutput()->GetSpacing( spacing );  //beware input should be Update()
274   for(int j=0; j<3; j++)
275   {
276     //2mm thickness, we assume spacing units is in mm !!
277     kernelsize[j] = 3; //(int) (2. / spacing[i]);
278     //std::cout << "kernel=" << kernelsize[i] << ",et:" << (int)(2./0.3334) << ",";
279     //beware if spacing=0.3334 -> 2/0.3334 = 5 !!!! bad bad +/-0.5 to see
280   }
281   dilate->SetKernelSize (kernelsize[0], kernelsize[1], kernelsize[2]);
282
283   //VTK filters rules !!!
284   gauss->SetInput( dilate->GetOutput());
285   tf->Update();
286   // shallow copy result into output.
287   // Could be re-written, as we know there is no vert neither strips...
288   OuterMold->ShallowCopy( tf->GetOutput() );
289   
290   //OuterMold->Squeeze();  //usefull ?
291
292   //Delete temp stuff:
293   imagedata             -> Delete();
294   seed                  -> Delete();
295   gauss                 -> Delete();
296   contour               -> Delete();
297   clean                 -> Delete();
298   tf                    -> Delete();
299   dilate                -> Delete();
300   
301   //TODO: if this filter is used many times when working on different image size
302   //I guess we need to find something brighter for this->ClipImageData
303 }
304 //end of macros use
305 #undef PSC_MAX
306 #undef PSC_MIN
307 //----------------------------------------------------------------------------
308 //We need to add a function that from the raw data is able to clip with
309 //the begining and ending of polydata to produce sharp edge.
310 void vtkImagePolyDataSeedConnectivity::ClipImageWithAxis()
311 {
312   //no input need we'll work with member data:
313   //Now we only need the region growing part:
314  /**
315   * Instead of working directly on leo we might remove the heart by a  trick:
316   * If we could cut with a disk at place where the last(first) axis point is
317   * we could avoid the flood fill algo to spread in the heart...smart  !
318   */
319
320  /**
321   * First we construct a virtual disk (=half sphere for now)
322   * See: Disk-Stencil.py
323   */
324   double normal[3];
325
326  /**
327   * What we do here:
328   * Get first point (p2) and second point (p1) : assume it exists !
329   * Then calculate vector p2->p1
330   * normalize it
331   * use it to define intersection plane between sphere and plane
332   */
333   this->Axis->Update();
334   vtkPoints *points = this->Axis->GetPoints();
335   int numPts = points->GetNumberOfPoints();
336   double p1[3];
337   double p2[3];
338   
339   points->GetPoint( 0, p2 );
340   points->GetPoint( 1, p1 );
341   
342   normal[0] = p1[0] - p2[0];
343   normal[1] = p1[1] - p2[1];
344   normal[2] = p1[2] - p2[2];
345   vtkMath::Normalize( normal );
346
347   vtkPlane *plane1 = vtkPlane::New();
348   plane1->SetOrigin( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
349   plane1->SetNormal( normal[0], normal[1], normal[2]);
350
351   vtkDataArray* scalars = this->Axis->GetPointData()->GetScalars();
352   double tuple[2];
353   scalars->GetTuple(0, tuple);
354   double radius = tuple[0];
355
356    vtkSphere *sphere1 = vtkSphere::New();
357    sphere1->SetCenter( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
358    sphere1->SetRadius( radius + 1);
359
360    vtkImplicitBoolean *disk1 = vtkImplicitBoolean::New();
361    disk1->SetOperationTypeToIntersection();
362    disk1->AddFunction(sphere1);
363    disk1->AddFunction(plane1);
364
365  /**
366   * What we do here:
367   * Get last point (p2) and before last point (p1) : assume it exists !
368   * Then calculate vector p2->p1
369   * normalize it
370   * use it to define intersection plane between sphere and plane
371   */
372
373   //ax->getControlPoint(numPts - 1, p2, p2+3);
374   points->GetPoint( numPts - 1, p2);
375   //ax->getControlPoint(numPts - 2, p1, p1+3); //assume it exists
376   points->GetPoint( numPts - 2, p1);
377   normal[0] = p1[0] - p2[0];
378   normal[1] = p1[1] - p2[1];
379   normal[2] = p1[2] - p2[2];
380   vtkMath::Normalize( normal );
381
382   vtkPlane *plane2 = vtkPlane::New();
383   plane2->SetOrigin( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
384   plane2->SetNormal( normal[0], normal[1], normal[2]);
385
386   vtkSphere *sphere2 = vtkSphere::New();
387   sphere2->SetCenter( p2[ 0 ], p2[ 1 ], p2[ 2 ] );
388   sphere2->SetRadius( radius + 1 );
389
390   vtkImplicitBoolean *disk2 = vtkImplicitBoolean::New();
391   disk2->SetOperationTypeToIntersection();
392   disk2->AddFunction(sphere2);
393   disk2->AddFunction(plane2);
394
395   vtkImplicitBoolean *disk = vtkImplicitBoolean::New();
396   disk->SetOperationTypeToUnion();
397   disk->AddFunction(disk1);
398   disk->AddFunction(disk2);
399
400  /**
401   * Now use dgobbi class: Stencil stuff to remove the 'bad' heart part
402   */
403
404   vtkImplicitFunctionToImageStencil *functionToStencil = vtkImplicitFunctionToImageStencil::New ();
405   functionToStencil->SetInput ( disk );
406
407   vtkImageStencil *stencil = vtkImageStencil::New();
408   stencil->SetInput( this->GetInput() );
409   stencil->SetStencil( functionToStencil->GetOutput() );
410   stencil->ReverseStencilOn();
411   stencil->SetBackgroundValue (0);
412   //don't forget to call ->Update before using an iterator !!!
413   stencil->Update();
414   
415   this->ClipImageData->ShallowCopy( stencil->GetOutput() );
416   
417  /**
418   * Should I do my own vtkCappedCylinder ??
419   * solution: no , cause ... I can't remember why... :p
420   */
421   
422   //Delete stuff:
423   plane1                        -> Delete();
424   sphere1                       -> Delete();
425   disk1                         -> Delete();
426   plane2                        -> Delete();
427   sphere2                       -> Delete();
428   disk2                         -> Delete();
429   disk                          -> Delete();
430   functionToStencil     -> Delete();
431   stencil                       -> Delete();
432 }
433 //----------------------------------------------------------------------------
434 void vtkImagePolyDataSeedConnectivity::PrintSelf(ostream& os, vtkIndent indent)
435 {
436   this->Superclass::PrintSelf(os,indent);
437   if ( this->Axis )
438     {
439     os << indent << "Axis of " << this->Axis->GetNumberOfPoints()
440        << "points defined\n";
441     }
442   else
443     {
444     os << indent << "Axis not defined\n";
445     }
446 }
447
448