]> Creatis software - clitk.git/blob - itk/clitkSliceBySliceRelativePositionFilter.txx
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[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   this->SetK1(vcl_acos(-1.0)/2);
47 }
48 //--------------------------------------------------------------------
49
50
51 //--------------------------------------------------------------------
52 template <class ImageType>
53 void 
54 clitk::SliceBySliceRelativePositionFilter<ImageType>::
55 SetInput(const ImageType * image) 
56 {
57   // Process object is not const-correct so the const casting is required.
58   this->SetNthInput(0, const_cast<ImageType *>(image));
59 }
60 //--------------------------------------------------------------------
61   
62
63 //--------------------------------------------------------------------
64 template <class ImageType>
65 void 
66 clitk::SliceBySliceRelativePositionFilter<ImageType>::
67 SetInputObject(const ImageType * image) 
68 {
69   // Process object is not const-correct so the const casting is required.
70   this->SetNthInput(1, const_cast<ImageType *>(image));
71 }
72 //--------------------------------------------------------------------
73   
74
75 //--------------------------------------------------------------------
76 template <class ImageType>
77 void 
78 clitk::SliceBySliceRelativePositionFilter<ImageType>::
79 PrintOptions(std::ostream & os) 
80 {
81   os << "Slice direction = " << this->GetDirection() << std::endl
82      << "BG value        = " << (int)this->GetBackgroundValue() << std::endl;
83   for(int i=0; i<this->GetNumberOfAngles(); i++) {
84     os << "Orientation     = " << this->GetOrientationTypeString()[i] << std::endl;
85     os << "Angles     = " << clitk::rad2deg(this->GetAngle1InRad(i)) 
86        << " " << clitk::rad2deg(this->GetAngle2InRad(i)) << std::endl;
87   }
88   os << "InverseOrientationFlag  = " << this->GetInverseOrientationFlag() << std::endl        
89      << "SpacingFlag     = " << this->GetIntermediateSpacingFlag() << std::endl
90      << "Spacing         = " << this->GetIntermediateSpacing() << std::endl
91      << "FuzzyThreshold  = " << this->GetFuzzyThreshold() << std::endl
92      << "UniqueConnectedComponentBySliceFlag  = " << this->GetUniqueConnectedComponentBySliceFlag() << std::endl
93      << "AutoCropFlag    = " << this->GetAutoCropFlag() << std::endl    
94      << "RemoveObjectFlag= " << this->GetRemoveObjectFlag() << std::endl    
95      << "CombineWithOrFlag = " << this->GetCombineWithOrFlag() << std::endl    
96      << "UseTheLargestObjectCCLFlag = " << this->GetUseTheLargestObjectCCLFlag() << std::endl    
97      << "ObjectCCLSelectionFlag = " << this->GetObjectCCLSelectionFlag() << std::endl    
98      << "ObjectCCLSelectionDimension = " << this->GetObjectCCLSelectionDimension() << std::endl    
99      << "ObjectCCLSelectionIgnoreSingleCCLFlag = " << this->GetObjectCCLSelectionIgnoreSingleCCLFlag() << std::endl    
100      << "IgnoreEmptySliceObjectFlag = " << this->GetIgnoreEmptySliceObjectFlag() << std::endl
101      << "(RP) FastFlag              = " << this->GetFastFlag() << std::endl
102      << "(RP) Radius                = " << this->GetRadius() << std::endl
103      << "(RP) K1                    = " << this->GetK1() << std::endl;
104 }
105 //--------------------------------------------------------------------
106
107
108 //--------------------------------------------------------------------
109 template <class ImageType>
110 void 
111 clitk::SliceBySliceRelativePositionFilter<ImageType>::
112 GenerateInputRequestedRegion() 
113 {
114   // Call default
115   itk::ImageToImageFilter<ImageType, ImageType>::GenerateInputRequestedRegion();
116   // Get input pointers and set requested region to common region
117   ImagePointer input1 = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
118   ImagePointer input2 = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
119   input1->SetRequestedRegion(input1->GetLargestPossibleRegion());
120   input2->SetRequestedRegion(input2->GetLargestPossibleRegion());
121 }
122 //--------------------------------------------------------------------
123
124
125 //--------------------------------------------------------------------
126 template <class ImageType>
127 void 
128 clitk::SliceBySliceRelativePositionFilter<ImageType>::
129 GenerateOutputInformation() 
130 {
131   if (this->GetVerboseOptionFlag()) {
132     PrintOptions();
133   }
134
135   //  if (this->GetFuzzyMapOnlyFlag()) this->ComputeFuzzyMapFlagOn();
136
137   // Get input pointer
138   input = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
139   object = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
140   m_working_object = object;
141   m_working_input = input;
142
143   //--------------------------------------------------------------------
144   // Resample object to the same spacing than input
145   if (!clitk::HaveSameSpacing<ImageType, ImageType>(object, input)) {
146     this->StartNewStep("Resample object to the same spacing than input");
147     m_working_object = clitk::ResampleImageSpacing<ImageType>(object, input->GetSpacing());
148     this->template StopCurrentStep<ImageType>(m_working_object);
149   }
150   
151   //--------------------------------------------------------------------
152   // Resize image according to common area (except in Z)
153   if (!clitk::HaveSameSizeAndSpacing<ImageType, ImageType>(m_working_object, input)) {
154     this->StartNewStep("Resize images (union in XY and like input in Z)");
155     
156     /* OLD STUFF
157     this->StartNewStep("Pad object to the same size than input");
158     m_working_object = clitk::ResizeImageLike<ImageType>(m_working_object, 
159     input, 
160     this->GetObjectBackgroundValue());
161     this->template StopCurrentStep<ImageType>(m_working_object);
162     */
163
164     // Compute union of bounding boxes in X and Y
165     static const unsigned int dim = ImageType::ImageDimension;
166     typedef itk::BoundingBox<unsigned long, dim> BBType;
167     typename BBType::Pointer bb1 = BBType::New();
168     ComputeBBFromImageRegion<ImageType>(m_working_object, m_working_object->GetLargestPossibleRegion(), bb1);
169     typename BBType::Pointer bb2 = BBType::New();
170     ComputeBBFromImageRegion<ImageType>(input, input->GetLargestPossibleRegion(), bb2);
171     typename BBType::Pointer bbo = BBType::New();
172     ComputeBBUnion<dim>(bbo, bb1, bb2);
173
174     //We set Z BB like input
175     typename ImageType::PointType maxs = bbo->GetMaximum();
176     typename ImageType::PointType mins = bbo->GetMinimum();
177     maxs[2] = bb2->GetMaximum()[2];
178     mins[2] = bb2->GetMinimum()[2];
179     bbo->SetMaximum(maxs);
180     bbo->SetMinimum(mins);
181
182     // Crop
183     m_working_input = clitk::ResizeImageLike<ImageType>(input, bbo, this->GetBackgroundValue());    
184     m_working_object = clitk::ResizeImageLike<ImageType>(m_working_object, 
185                                                          m_working_input, 
186                                                          this->GetObjectBackgroundValue());
187     
188     // Index can be negative in some cases, and lead to problem with
189     // some filter. So we correct it.
190     m_working_input = clitk::RemoveNegativeIndexFromRegion<ImageType>(m_working_input);
191     m_working_object = clitk::RemoveNegativeIndexFromRegion<ImageType>(m_working_object);
192
193     // End
194     this->template StopCurrentStep<ImageType>(m_working_input);  
195   }
196   
197   //--------------------------------------------------------------------
198   /* Steps : 
199     - extract vector of slices in input, in object
200     - slice by slice rel position
201     - joint result
202     - post process
203   */
204
205   //--------------------------------------------------------------------
206   // Extract input slices
207   this->StartNewStep("Extract input slices");
208   typedef clitk::ExtractSliceFilter<ImageType> ExtractSliceFilterType;
209   typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
210   extractSliceFilter->SetInput(m_working_input);
211   extractSliceFilter->SetDirection(GetDirection());
212   extractSliceFilter->Update();
213   typedef typename ExtractSliceFilterType::SliceType SliceType;
214   std::vector<typename SliceType::Pointer> mInputSlices;
215   extractSliceFilter->GetOutputSlices(mInputSlices);
216   this->template StopCurrentStep<SliceType>(mInputSlices[0]);
217   
218   //--------------------------------------------------------------------
219   // Extract object slices
220   this->StartNewStep("Extract object slices");
221   extractSliceFilter = ExtractSliceFilterType::New();
222   extractSliceFilter->SetInput(m_working_object);
223   extractSliceFilter->SetDirection(GetDirection());
224   extractSliceFilter->Update();
225   std::vector<typename SliceType::Pointer> mObjectSlices;
226   extractSliceFilter->GetOutputSlices(mObjectSlices);
227   this->template StopCurrentStep<SliceType>(mObjectSlices[0]);
228
229   //--------------------------------------------------------------------
230   // Prepare fuzzy slices (if needed)
231   std::vector<typename FloatSliceType::Pointer> mFuzzyMapSlices;
232   mFuzzyMapSlices.resize(mInputSlices.size());
233
234   //--------------------------------------------------------------------
235   // Perform slice by slice relative position
236   this->StartNewStep("Perform slice by slice relative position ("+toString(mInputSlices.size())+")");
237   for(unsigned int i=0; i<mInputSlices.size(); i++) {
238     
239     // Count the number of CCL (allow to ignore empty slice)
240     int nb=0;
241     mObjectSlices[i] = LabelizeAndCountNumberOfObjects<SliceType>(mObjectSlices[i], 0, true, 1, nb);
242
243     // If no object and empty slices and if we need the full fuzzy map, create a dummy one.
244     if ((nb==0) && (this->GetFuzzyMapOnlyFlag())) {
245       typename FloatSliceType::Pointer one = FloatSliceType::New();
246       one->CopyInformation(mObjectSlices[0]);
247       one->SetRegions(mObjectSlices[0]->GetLargestPossibleRegion());
248       one->Allocate();
249       one->FillBuffer(2.0);
250       mFuzzyMapSlices[i] = one;
251     } // End nb==0 && GetComputeFuzzyMapFlag
252     else {
253       if ((!GetIgnoreEmptySliceObjectFlag()) || (nb!=0)) {
254         
255         // Select or not a single CCL ?
256         if (GetUseTheLargestObjectCCLFlag()) {
257           mObjectSlices[i] = KeepLabels<SliceType>(mObjectSlices[i], 0, 1, 1, 1, true);
258         }
259
260         // Select a single according to a position if more than one CCL
261         if (GetObjectCCLSelectionFlag()) {
262           // if several CCL, choose the most extrema according a direction, 
263           // if not -> should we consider this slice ? 
264           if (nb<2) {
265             if (GetObjectCCLSelectionIgnoreSingleCCLFlag()) {
266               mObjectSlices[i] = SetBackground<SliceType, SliceType>(mObjectSlices[i], mObjectSlices[i], 
267                                                                      1, this->GetBackgroundValue(), 
268                                                                      true);
269             }
270           }
271           int dim = GetObjectCCLSelectionDimension();
272           int direction = GetObjectCCLSelectionDirection();
273           std::vector<typename SliceType::PointType> centroids;
274           ComputeCentroids<SliceType>(mObjectSlices[i], this->GetBackgroundValue(), centroids);
275           uint index=1;
276           for(uint j=1; j<centroids.size(); j++) {
277             if (direction == 1) {
278               if (centroids[j][dim] > centroids[index][dim]) index = j;
279             }
280             else {
281               if (centroids[j][dim] < centroids[index][dim]) index = j;
282             }
283           }
284           for(uint v=1; v<centroids.size(); v++) {
285             if (v != index) {
286               mObjectSlices[i] = SetBackground<SliceType, SliceType>(mObjectSlices[i], mObjectSlices[i], 
287                                                                      (char)v, this->GetBackgroundValue(), 
288                                                                      true);
289             }
290           }
291         } // end GetbjectCCLSelectionFlag = true
292
293         // Relative position
294         typedef clitk::AddRelativePositionConstraintToLabelImageFilter<SliceType> RelPosFilterType;
295         typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
296         relPosFilter->VerboseStepFlagOff();
297         if (GetVerboseSlicesFlag()) {
298           std::cout << "Slice " << i << std::endl;
299           relPosFilter->VerboseStepFlagOn();
300         }
301         relPosFilter->WriteStepFlagOff();
302         // relPosFilter->VerboseMemoryFlagOn();
303         relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()+"-"+toString(i));        
304         relPosFilter->SetBackgroundValue(this->GetBackgroundValue());
305         relPosFilter->SetInput(mInputSlices[i]); 
306         relPosFilter->SetInputObject(mObjectSlices[i]); 
307         relPosFilter->SetRemoveObjectFlag(this->GetRemoveObjectFlag());        
308         // This flag (InverseOrientation) *must* be set before
309         // AddOrientation because AddOrientation can change it.
310         relPosFilter->SetInverseOrientationFlag(this->GetInverseOrientationFlag());
311         for(int j=0; j<this->GetNumberOfAngles(); j++) {
312           relPosFilter->AddAnglesInRad(this->GetAngle1InRad(j), this->GetAngle2InRad(j));
313         }
314         relPosFilter->SetIntermediateSpacing(this->GetIntermediateSpacing());
315         relPosFilter->SetIntermediateSpacingFlag(this->GetIntermediateSpacingFlag());
316         relPosFilter->SetFuzzyThreshold(this->GetFuzzyThreshold());
317         relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
318         relPosFilter->SetCombineWithOrFlag(this->GetCombineWithOrFlag()); 
319         // should we stop after fuzzy map ?
320         relPosFilter->SetFuzzyMapOnlyFlag(this->GetFuzzyMapOnlyFlag());
321         //        relPosFilter->SetComputeFuzzyMapFlag(this->GetComputeFuzzyMapFlag());      
322         relPosFilter->SetFastFlag(this->GetFastFlag());
323         relPosFilter->SetRadius(this->GetRadius());
324         relPosFilter->SetK1(this->GetK1());
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