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