]> Creatis software - clitk.git/blob - itk/clitkSliceBySliceRelativePositionFilter.txx
Add VerboseSlicesFlag
[clitk.git] / itk / clitkSliceBySliceRelativePositionFilter.txx
1 /*=========================================================================
2   Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
3
4   Authors belong to: 
5   - University of LYON              http://www.universite-lyon.fr/
6   - Léon Bérard cancer center       http://oncora1.lyon.fnclcc.fr
7   - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
8
9   This software is distributed WITHOUT ANY WARRANTY; without even
10   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11   PURPOSE.  See the copyright notices for more information.
12
13   It is distributed under dual licence
14
15   - BSD        See included LICENSE.txt file
16   - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17   ======================================================================-====*/
18
19 // clitk
20 #include "clitkCropLikeImageFilter.h"
21 #include "clitkSegmentationUtils.h"
22 #include "clitkExtractSliceFilter.h"
23 #include "clitkResampleImageWithOptionsFilter.h"
24
25 // itk
26 #include <itkJoinSeriesImageFilter.h>
27
28 //--------------------------------------------------------------------
29 template <class ImageType>
30 clitk::SliceBySliceRelativePositionFilter<ImageType>::
31 SliceBySliceRelativePositionFilter():
32   clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>()
33 {
34   SetDirection(2);
35   UniqueConnectedComponentBySliceFlagOff();
36   SetIgnoreEmptySliceObjectFlag(false);
37   UseTheLargestObjectCCLFlagOff();
38   this->VerboseStepFlagOff();
39   this->WriteStepFlagOff();
40   this->SetCombineWithOrFlag(false);
41   ObjectCCLSelectionFlagOff();
42   SetObjectCCLSelectionDimension(0);
43   SetObjectCCLSelectionDirection(1);
44   ObjectCCLSelectionIgnoreSingleCCLFlagOff();
45   VerboseSlicesFlagOff();
46 }
47 //--------------------------------------------------------------------
48
49
50 //--------------------------------------------------------------------
51 template <class ImageType>
52 void 
53 clitk::SliceBySliceRelativePositionFilter<ImageType>::
54 SetInput(const ImageType * image) 
55 {
56   // Process object is not const-correct so the const casting is required.
57   this->SetNthInput(0, const_cast<ImageType *>(image));
58 }
59 //--------------------------------------------------------------------
60   
61
62 //--------------------------------------------------------------------
63 template <class ImageType>
64 void 
65 clitk::SliceBySliceRelativePositionFilter<ImageType>::
66 SetInputObject(const ImageType * image) 
67 {
68   // Process object is not const-correct so the const casting is required.
69   this->SetNthInput(1, const_cast<ImageType *>(image));
70 }
71 //--------------------------------------------------------------------
72   
73
74 //--------------------------------------------------------------------
75 template <class ImageType>
76 void 
77 clitk::SliceBySliceRelativePositionFilter<ImageType>::
78 PrintOptions(std::ostream & os) 
79 {
80   os << "Slice direction = " << this->GetDirection() << std::endl
81      << "BG value        = " << this->GetBackgroundValue() << std::endl;
82   for(int i=0; i<this->GetNumberOfAngles(); i++) {
83     os << "Orientation     = " << this->GetOrientationTypeString()[i] << std::endl;
84     os << "Angles     = " << clitk::rad2deg(this->GetAngle1InRad(i)) 
85        << " " << clitk::rad2deg(this->GetAngle2InRad(i)) << std::endl;
86   }
87   os << "InverseOrientationFlag  = " << this->GetInverseOrientationFlag() << std::endl        
88      << "SpacingFlag     = " << this->GetIntermediateSpacingFlag() << std::endl
89      << "Spacing         = " << this->GetIntermediateSpacing() << std::endl
90      << "FuzzyThreshold  = " << this->GetFuzzyThreshold() << std::endl
91      << "UniqueConnectedComponentBySliceFlag  = " << this->GetUniqueConnectedComponentBySliceFlag() << std::endl
92      << "AutoCropFlag    = " << this->GetAutoCropFlag() << std::endl    
93      << "RemoveObjectFlag= " << this->GetRemoveObjectFlag() << std::endl    
94      << "CombineWithOrFlag = " << this->GetCombineWithOrFlag() << std::endl    
95      << "UseTheLargestObjectCCLFlag = " << this->GetUseTheLargestObjectCCLFlag() << std::endl    
96      << "ObjectCCLSelectionFlag = " << this->GetObjectCCLSelectionFlag() << std::endl    
97      << "ObjectCCLSelectionDimension = " << this->GetObjectCCLSelectionDimension() << std::endl    
98      << "ObjectCCLSelectionIgnoreSingleCCLFlag = " << this->GetObjectCCLSelectionIgnoreSingleCCLFlag() << std::endl    
99      << "IgnoreEmptySliceObjectFlag = " << this->GetIgnoreEmptySliceObjectFlag() << std::endl
100      << "(RP) FastFlag              = " << this->GetFastFlag() << std::endl
101      << "(RP) Radius                = " << this->GetRadius() << std::endl;
102 }
103 //--------------------------------------------------------------------
104
105
106 //--------------------------------------------------------------------
107 template <class ImageType>
108 void 
109 clitk::SliceBySliceRelativePositionFilter<ImageType>::
110 GenerateInputRequestedRegion() 
111 {
112   // Call default
113   itk::ImageToImageFilter<ImageType, ImageType>::GenerateInputRequestedRegion();
114   // Get input pointers and set requested region to common region
115   ImagePointer input1 = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
116   ImagePointer input2 = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
117   input1->SetRequestedRegion(input1->GetLargestPossibleRegion());
118   input2->SetRequestedRegion(input2->GetLargestPossibleRegion());
119 }
120 //--------------------------------------------------------------------
121
122
123 //--------------------------------------------------------------------
124 template <class ImageType>
125 void 
126 clitk::SliceBySliceRelativePositionFilter<ImageType>::
127 GenerateOutputInformation() 
128 {
129   if (this->GetVerboseOptionFlag()) {
130     PrintOptions();
131   }
132
133   //  if (this->GetFuzzyMapOnlyFlag()) this->ComputeFuzzyMapFlagOn();
134
135   // Get input pointer
136   input = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
137   object = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
138   m_working_object = object;
139   m_working_input = input;
140
141   //--------------------------------------------------------------------
142   // Resample object to the same spacing than input
143   if (!clitk::HaveSameSpacing<ImageType, ImageType>(object, input)) {
144     this->StartNewStep("Resample object to the same spacing than input");
145     m_working_object = clitk::ResampleImageSpacing<ImageType>(object, input->GetSpacing());
146     this->template StopCurrentStep<ImageType>(m_working_object);
147   }
148   
149   //--------------------------------------------------------------------
150   // Resize image according to common area (except in Z)
151   if (!clitk::HaveSameSizeAndSpacing<ImageType, ImageType>(m_working_object, input)) {
152     this->StartNewStep("Resize images (union in XY and like input in Z)");
153     
154     /* OLD STUFF
155     this->StartNewStep("Pad object to the same size than input");
156     m_working_object = clitk::ResizeImageLike<ImageType>(m_working_object, 
157     input, 
158     this->GetObjectBackgroundValue());
159     this->template StopCurrentStep<ImageType>(m_working_object);
160     */
161
162     DD(input->GetLargestPossibleRegion());
163     DD(m_working_object->GetLargestPossibleRegion());
164
165     // Compute union of bounding boxes in X and Y
166     static const unsigned int dim = ImageType::ImageDimension;
167     typedef itk::BoundingBox<unsigned long, dim> BBType;
168     typename BBType::Pointer bb1 = BBType::New();
169     ComputeBBFromImageRegion<ImageType>(m_working_object, m_working_object->GetLargestPossibleRegion(), bb1);
170     typename BBType::Pointer bb2 = BBType::New();
171     ComputeBBFromImageRegion<ImageType>(input, input->GetLargestPossibleRegion(), bb2);
172     typename BBType::Pointer bbo = BBType::New();
173     ComputeBBUnion<dim>(bbo, bb1, bb2);
174
175     //We set Z BB like input
176     typename ImageType::PointType maxs = bbo->GetMaximum();
177     typename ImageType::PointType mins = bbo->GetMinimum();
178     maxs[2] = bb2->GetMaximum()[2];
179     mins[2] = bb2->GetMinimum()[2];
180     bbo->SetMaximum(maxs);
181     bbo->SetMinimum(mins);
182
183     // Crop
184     m_working_input = clitk::ResizeImageLike<ImageType>(input, bbo, this->GetBackgroundValue());    
185     m_working_object = clitk::ResizeImageLike<ImageType>(m_working_object, 
186                                                          m_working_input, 
187                                                          this->GetObjectBackgroundValue());
188     
189     // Index can be negative in some cases, and lead to problem with
190     // some filter. So we correct it.
191     m_working_input = clitk::RemoveNegativeIndexFromRegion<ImageType>(m_working_input);
192     m_working_object = clitk::RemoveNegativeIndexFromRegion<ImageType>(m_working_object);
193
194     // End
195     this->template StopCurrentStep<ImageType>(m_working_input);  
196   }
197   
198   //--------------------------------------------------------------------
199   /* Steps : 
200     - extract vector of slices in input, in object
201     - slice by slice rel position
202     - joint result
203     - post process
204   */
205
206   //--------------------------------------------------------------------
207   // Extract input slices
208   this->StartNewStep("Extract input slices");
209   typedef clitk::ExtractSliceFilter<ImageType> ExtractSliceFilterType;
210   typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
211   extractSliceFilter->SetInput(m_working_input);
212   extractSliceFilter->SetDirection(GetDirection());
213   extractSliceFilter->Update();
214   typedef typename ExtractSliceFilterType::SliceType SliceType;
215   std::vector<typename SliceType::Pointer> mInputSlices;
216   extractSliceFilter->GetOutputSlices(mInputSlices);
217   this->template StopCurrentStep<SliceType>(mInputSlices[0]);
218   
219   //--------------------------------------------------------------------
220   // Extract object slices
221   this->StartNewStep("Extract object slices");
222   extractSliceFilter = ExtractSliceFilterType::New();
223   extractSliceFilter->SetInput(m_working_object);
224   extractSliceFilter->SetDirection(GetDirection());
225   extractSliceFilter->Update();
226   std::vector<typename SliceType::Pointer> mObjectSlices;
227   extractSliceFilter->GetOutputSlices(mObjectSlices);
228   this->template StopCurrentStep<SliceType>(mObjectSlices[0]);
229
230   //--------------------------------------------------------------------
231   // Prepare fuzzy slices (if needed)
232   std::vector<typename FloatSliceType::Pointer> mFuzzyMapSlices;
233   mFuzzyMapSlices.resize(mInputSlices.size());
234
235   //--------------------------------------------------------------------
236   // Perform slice by slice relative position
237   this->StartNewStep("Perform slice by slice relative position ("+toString(mInputSlices.size())+")");
238   for(unsigned int i=0; i<mInputSlices.size(); i++) {
239     
240     // Count the number of CCL (allow to ignore empty slice)
241     int nb=0;
242     mObjectSlices[i] = LabelizeAndCountNumberOfObjects<SliceType>(mObjectSlices[i], 0, true, 1, nb);
243
244     // If no object and empty slices and if we need the full fuzzy map, create a dummy one.
245     if ((nb==0) && (this->GetFuzzyMapOnlyFlag())) {
246       typename FloatSliceType::Pointer one = FloatSliceType::New();
247       one->CopyInformation(mObjectSlices[0]);
248       one->SetRegions(mObjectSlices[0]->GetLargestPossibleRegion());
249       one->Allocate();
250       one->FillBuffer(2.0);
251       mFuzzyMapSlices[i] = one;
252     } // End nb==0 && GetComputeFuzzyMapFlag
253     else {
254       if ((!GetIgnoreEmptySliceObjectFlag()) || (nb!=0)) {
255         
256         // Select or not a single CCL ?
257         if (GetUseTheLargestObjectCCLFlag()) {
258           mObjectSlices[i] = KeepLabels<SliceType>(mObjectSlices[i], 0, 1, 1, 1, true);
259         }
260
261         // Select a single according to a position if more than one CCL
262         if (GetObjectCCLSelectionFlag()) {
263           // if several CCL, choose the most extrema according a direction, 
264           // if not -> should we consider this slice ? 
265           if (nb<2) {
266             if (GetObjectCCLSelectionIgnoreSingleCCLFlag()) {
267               mObjectSlices[i] = SetBackground<SliceType, SliceType>(mObjectSlices[i], mObjectSlices[i], 
268                                                                      1, this->GetBackgroundValue(), 
269                                                                      true);
270             }
271           }
272           int dim = GetObjectCCLSelectionDimension();
273           int direction = GetObjectCCLSelectionDirection();
274           std::vector<typename SliceType::PointType> centroids;
275           ComputeCentroids<SliceType>(mObjectSlices[i], this->GetBackgroundValue(), centroids);
276           uint index=1;
277           for(uint j=1; j<centroids.size(); j++) {
278             if (direction == 1) {
279               if (centroids[j][dim] > centroids[index][dim]) index = j;
280             }
281             else {
282               if (centroids[j][dim] < centroids[index][dim]) index = j;
283             }
284           }
285           for(uint v=1; v<centroids.size(); v++) {
286             if (v != index) {
287               mObjectSlices[i] = SetBackground<SliceType, SliceType>(mObjectSlices[i], mObjectSlices[i], 
288                                                                      (char)v, this->GetBackgroundValue(), 
289                                                                      true);
290             }
291           }
292         } // end GetbjectCCLSelectionFlag = true
293
294         // Relative position
295         typedef clitk::AddRelativePositionConstraintToLabelImageFilter<SliceType> RelPosFilterType;
296         typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
297         relPosFilter->VerboseStepFlagOff();
298         if (GetVerboseSlicesFlag()) {
299           std::cout << "Slice " << i << std::endl;
300           relPosFilter->VerboseStepFlagOn();
301         }
302         relPosFilter->WriteStepFlagOff();
303         // relPosFilter->VerboseMemoryFlagOn();
304         relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()+"-"+toString(i));        
305         relPosFilter->SetBackgroundValue(this->GetBackgroundValue());
306         relPosFilter->SetInput(mInputSlices[i]); 
307         relPosFilter->SetInputObject(mObjectSlices[i]); 
308         relPosFilter->SetRemoveObjectFlag(this->GetRemoveObjectFlag());        
309         // This flag (InverseOrientation) *must* be set before
310         // AddOrientation because AddOrientation can change it.
311         relPosFilter->SetInverseOrientationFlag(this->GetInverseOrientationFlag());
312         for(int j=0; j<this->GetNumberOfAngles(); j++) {
313           relPosFilter->AddAnglesInRad(this->GetAngle1InRad(j), this->GetAngle2InRad(j));
314         }
315         relPosFilter->SetIntermediateSpacing(this->GetIntermediateSpacing());
316         relPosFilter->SetIntermediateSpacingFlag(this->GetIntermediateSpacingFlag());
317         relPosFilter->SetFuzzyThreshold(this->GetFuzzyThreshold());
318         relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
319         relPosFilter->SetCombineWithOrFlag(this->GetCombineWithOrFlag()); 
320         // should we stop after fuzzy map ?
321         relPosFilter->SetFuzzyMapOnlyFlag(this->GetFuzzyMapOnlyFlag());
322         //        relPosFilter->SetComputeFuzzyMapFlag(this->GetComputeFuzzyMapFlag());      
323         relPosFilter->SetFastFlag(this->GetFastFlag());
324         relPosFilter->SetRadius(this->GetRadius());
325
326         // Go !
327         relPosFilter->Update();
328
329         // If we stop after the fuzzy map, store the fuzzy slices
330         if (this->GetFuzzyMapOnlyFlag()) {
331           mFuzzyMapSlices[i] = relPosFilter->GetFuzzyMap();
332           // writeImage<FloatSliceType>(mFuzzyMapSlices[i], "slice_"+toString(i)+".mha");
333         }
334
335         // Set input slices
336         if (!this->GetFuzzyMapOnlyFlag())  {
337           mInputSlices[i] = relPosFilter->GetOutput();
338           // Select main CC if needed
339           if (GetUniqueConnectedComponentBySliceFlag()) {
340             mInputSlices[i] = Labelize<SliceType>(mInputSlices[i], 0, true, 1);
341             mInputSlices[i] = KeepLabels<SliceType>(mInputSlices[i], 0, 1, 1, 1, true);
342           }          
343         }
344
345       }
346
347       /*
348       // Select unique CC according to the most in a given direction
349       if (GetUniqueConnectedComponentBySliceAccordingToADirection()) {
350       int nb;
351       mInputSlices[i] = LabelizeAndCountNumberOfObjects<SliceType>(mInputSlices[i], 0, true, 1, nb);
352       std::vector<typename ImageType::PointType> & centroids;
353       ComputeCentroids
354       }
355       */
356
357     } // End nb!=0 || GetComputeFuzzyMapFlagOFF
358
359   } // end for i mInputSlices
360
361   // Join the slices
362   m_working_input = clitk::JoinSlices<ImageType>(mInputSlices, m_working_input, GetDirection());
363   this->template StopCurrentStep<ImageType>(m_working_input);
364
365   // Join the fuzzy map if needed
366   if (this->GetFuzzyMapOnlyFlag()) {
367     this->m_FuzzyMap = clitk::JoinSlices<FloatImageType>(mFuzzyMapSlices, m_working_input, GetDirection());
368     this->template StopCurrentStep<FloatImageType>(this->m_FuzzyMap);
369     if (this->GetFuzzyMapOnlyFlag()) return;
370   }
371
372   //--------------------------------------------------------------------
373   // Step 7: autocrop
374   if (this->GetAutoCropFlag()) {
375     this->StartNewStep("Final AutoCrop");
376     typedef clitk::AutoCropFilter<ImageType> CropFilterType;
377     typename CropFilterType::Pointer cropFilter = CropFilterType::New();
378     cropFilter->SetInput(m_working_input);
379     cropFilter->ReleaseDataFlagOff();
380     cropFilter->Update();   
381     m_working_input = cropFilter->GetOutput();
382     this->template StopCurrentStep<ImageType>(m_working_input);    
383   }
384
385   // Update output info
386   this->GetOutput(0)->SetRegions(m_working_input->GetLargestPossibleRegion());  
387 }
388 //--------------------------------------------------------------------
389
390
391 //--------------------------------------------------------------------
392 template <class ImageType>
393 void 
394 clitk::SliceBySliceRelativePositionFilter<ImageType>::
395 GenerateData() 
396 {
397   // Get input pointer
398   //--------------------------------------------------------------------
399   //--------------------------------------------------------------------  
400   // Final Step -> set output
401   //this->SetNthOutput(0, m_working_input);
402   if (this->GetFuzzyMapOnlyFlag()) return; // no output in this case
403   this->GraftOutput(m_working_input);
404   return;
405 }
406 //--------------------------------------------------------------------
407