]> 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 }
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        = " << (int)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     // Compute union of bounding boxes in X and Y
163     static const unsigned int dim = ImageType::ImageDimension;
164     typedef itk::BoundingBox<unsigned long, dim> BBType;
165     typename BBType::Pointer bb1 = BBType::New();
166     ComputeBBFromImageRegion<ImageType>(m_working_object, m_working_object->GetLargestPossibleRegion(), bb1);
167     typename BBType::Pointer bb2 = BBType::New();
168     ComputeBBFromImageRegion<ImageType>(input, input->GetLargestPossibleRegion(), bb2);
169     typename BBType::Pointer bbo = BBType::New();
170     ComputeBBUnion<dim>(bbo, bb1, bb2);
171
172     //We set Z BB like input
173     typename ImageType::PointType maxs = bbo->GetMaximum();
174     typename ImageType::PointType mins = bbo->GetMinimum();
175     maxs[2] = bb2->GetMaximum()[2];
176     mins[2] = bb2->GetMinimum()[2];
177     bbo->SetMaximum(maxs);
178     bbo->SetMinimum(mins);
179
180     // Crop
181     m_working_input = clitk::ResizeImageLike<ImageType>(input, bbo, this->GetBackgroundValue());    
182     m_working_object = clitk::ResizeImageLike<ImageType>(m_working_object, 
183                                                          m_working_input, 
184                                                          this->GetObjectBackgroundValue());
185     
186     // Index can be negative in some cases, and lead to problem with
187     // some filter. So we correct it.
188     m_working_input = clitk::RemoveNegativeIndexFromRegion<ImageType>(m_working_input);
189     m_working_object = clitk::RemoveNegativeIndexFromRegion<ImageType>(m_working_object);
190
191     // End
192     this->template StopCurrentStep<ImageType>(m_working_input);  
193   }
194   
195   //--------------------------------------------------------------------
196   /* Steps : 
197     - extract vector of slices in input, in object
198     - slice by slice rel position
199     - joint result
200     - post process
201   */
202
203   //--------------------------------------------------------------------
204   // Extract input slices
205   this->StartNewStep("Extract input slices");
206   typedef clitk::ExtractSliceFilter<ImageType> ExtractSliceFilterType;
207   typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
208   extractSliceFilter->SetInput(m_working_input);
209   extractSliceFilter->SetDirection(GetDirection());
210   extractSliceFilter->Update();
211   typedef typename ExtractSliceFilterType::SliceType SliceType;
212   std::vector<typename SliceType::Pointer> mInputSlices;
213   extractSliceFilter->GetOutputSlices(mInputSlices);
214   this->template StopCurrentStep<SliceType>(mInputSlices[0]);
215   
216   //--------------------------------------------------------------------
217   // Extract object slices
218   this->StartNewStep("Extract object slices");
219   extractSliceFilter = ExtractSliceFilterType::New();
220   extractSliceFilter->SetInput(m_working_object);
221   extractSliceFilter->SetDirection(GetDirection());
222   extractSliceFilter->Update();
223   std::vector<typename SliceType::Pointer> mObjectSlices;
224   extractSliceFilter->GetOutputSlices(mObjectSlices);
225   this->template StopCurrentStep<SliceType>(mObjectSlices[0]);
226
227   //--------------------------------------------------------------------
228   // Prepare fuzzy slices (if needed)
229   std::vector<typename FloatSliceType::Pointer> mFuzzyMapSlices;
230   mFuzzyMapSlices.resize(mInputSlices.size());
231
232   //--------------------------------------------------------------------
233   // Perform slice by slice relative position
234   this->StartNewStep("Perform slice by slice relative position ("+toString(mInputSlices.size())+")");
235   for(unsigned int i=0; i<mInputSlices.size(); i++) {
236     
237     // Count the number of CCL (allow to ignore empty slice)
238     int nb=0;
239     mObjectSlices[i] = LabelizeAndCountNumberOfObjects<SliceType>(mObjectSlices[i], 0, true, 1, nb);
240
241     // If no object and empty slices and if we need the full fuzzy map, create a dummy one.
242     if ((nb==0) && (this->GetFuzzyMapOnlyFlag())) {
243       typename FloatSliceType::Pointer one = FloatSliceType::New();
244       one->CopyInformation(mObjectSlices[0]);
245       one->SetRegions(mObjectSlices[0]->GetLargestPossibleRegion());
246       one->Allocate();
247       one->FillBuffer(2.0);
248       mFuzzyMapSlices[i] = one;
249     } // End nb==0 && GetComputeFuzzyMapFlag
250     else {
251       if ((!GetIgnoreEmptySliceObjectFlag()) || (nb!=0)) {
252         
253         // Select or not a single CCL ?
254         if (GetUseTheLargestObjectCCLFlag()) {
255           mObjectSlices[i] = KeepLabels<SliceType>(mObjectSlices[i], 0, 1, 1, 1, true);
256         }
257
258         // Select a single according to a position if more than one CCL
259         if (GetObjectCCLSelectionFlag()) {
260           // if several CCL, choose the most extrema according a direction, 
261           // if not -> should we consider this slice ? 
262           if (nb<2) {
263             if (GetObjectCCLSelectionIgnoreSingleCCLFlag()) {
264               mObjectSlices[i] = SetBackground<SliceType, SliceType>(mObjectSlices[i], mObjectSlices[i], 
265                                                                      1, this->GetBackgroundValue(), 
266                                                                      true);
267             }
268           }
269           int dim = GetObjectCCLSelectionDimension();
270           int direction = GetObjectCCLSelectionDirection();
271           std::vector<typename SliceType::PointType> centroids;
272           ComputeCentroids<SliceType>(mObjectSlices[i], this->GetBackgroundValue(), centroids);
273           uint index=1;
274           for(uint j=1; j<centroids.size(); j++) {
275             if (direction == 1) {
276               if (centroids[j][dim] > centroids[index][dim]) index = j;
277             }
278             else {
279               if (centroids[j][dim] < centroids[index][dim]) index = j;
280             }
281           }
282           for(uint v=1; v<centroids.size(); v++) {
283             if (v != index) {
284               mObjectSlices[i] = SetBackground<SliceType, SliceType>(mObjectSlices[i], mObjectSlices[i], 
285                                                                      (char)v, this->GetBackgroundValue(), 
286                                                                      true);
287             }
288           }
289         } // end GetbjectCCLSelectionFlag = true
290
291         // Relative position
292         typedef clitk::AddRelativePositionConstraintToLabelImageFilter<SliceType> RelPosFilterType;
293         typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
294         relPosFilter->VerboseStepFlagOff();
295         if (GetVerboseSlicesFlag()) {
296           std::cout << "Slice " << i << std::endl;
297           relPosFilter->VerboseStepFlagOn();
298         }
299         relPosFilter->WriteStepFlagOff();
300         // relPosFilter->VerboseMemoryFlagOn();
301         relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()+"-"+toString(i));        
302         relPosFilter->SetBackgroundValue(this->GetBackgroundValue());
303         relPosFilter->SetInput(mInputSlices[i]); 
304         relPosFilter->SetInputObject(mObjectSlices[i]); 
305         relPosFilter->SetRemoveObjectFlag(this->GetRemoveObjectFlag());        
306         // This flag (InverseOrientation) *must* be set before
307         // AddOrientation because AddOrientation can change it.
308         relPosFilter->SetInverseOrientationFlag(this->GetInverseOrientationFlag());
309         for(int j=0; j<this->GetNumberOfAngles(); j++) {
310           relPosFilter->AddAnglesInRad(this->GetAngle1InRad(j), this->GetAngle2InRad(j));
311         }
312         relPosFilter->SetIntermediateSpacing(this->GetIntermediateSpacing());
313         relPosFilter->SetIntermediateSpacingFlag(this->GetIntermediateSpacingFlag());
314         relPosFilter->SetFuzzyThreshold(this->GetFuzzyThreshold());
315         relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
316         relPosFilter->SetCombineWithOrFlag(this->GetCombineWithOrFlag()); 
317         // should we stop after fuzzy map ?
318         relPosFilter->SetFuzzyMapOnlyFlag(this->GetFuzzyMapOnlyFlag());
319         //        relPosFilter->SetComputeFuzzyMapFlag(this->GetComputeFuzzyMapFlag());      
320         relPosFilter->SetFastFlag(this->GetFastFlag());
321         relPosFilter->SetRadius(this->GetRadius());
322
323         // Go !
324         relPosFilter->Update();
325
326         // If we stop after the fuzzy map, store the fuzzy slices
327         if (this->GetFuzzyMapOnlyFlag()) {
328           mFuzzyMapSlices[i] = relPosFilter->GetFuzzyMap();
329           // writeImage<FloatSliceType>(mFuzzyMapSlices[i], "slice_"+toString(i)+".mha");
330         }
331
332         // Set input slices
333         if (!this->GetFuzzyMapOnlyFlag())  {
334           mInputSlices[i] = relPosFilter->GetOutput();
335           // Select main CC if needed
336           if (GetUniqueConnectedComponentBySliceFlag()) {
337             mInputSlices[i] = Labelize<SliceType>(mInputSlices[i], 0, true, 1);
338             mInputSlices[i] = KeepLabels<SliceType>(mInputSlices[i], 0, 1, 1, 1, true);
339           }          
340         }
341
342       }
343
344       /*
345       // Select unique CC according to the most in a given direction
346       if (GetUniqueConnectedComponentBySliceAccordingToADirection()) {
347       int nb;
348       mInputSlices[i] = LabelizeAndCountNumberOfObjects<SliceType>(mInputSlices[i], 0, true, 1, nb);
349       std::vector<typename ImageType::PointType> & centroids;
350       ComputeCentroids
351       }
352       */
353
354     } // End nb!=0 || GetComputeFuzzyMapFlagOFF
355
356   } // end for i mInputSlices
357
358   // Join the slices
359   m_working_input = clitk::JoinSlices<ImageType>(mInputSlices, m_working_input, GetDirection());
360   this->template StopCurrentStep<ImageType>(m_working_input);
361
362   // Join the fuzzy map if needed
363   if (this->GetFuzzyMapOnlyFlag()) {
364     this->m_FuzzyMap = clitk::JoinSlices<FloatImageType>(mFuzzyMapSlices, m_working_input, GetDirection());
365     this->template StopCurrentStep<FloatImageType>(this->m_FuzzyMap);
366     if (this->GetFuzzyMapOnlyFlag()) return;
367   }
368
369   //--------------------------------------------------------------------
370   // Step 7: autocrop
371   if (this->GetAutoCropFlag()) {
372     this->StartNewStep("Final AutoCrop");
373     typedef clitk::AutoCropFilter<ImageType> CropFilterType;
374     typename CropFilterType::Pointer cropFilter = CropFilterType::New();
375     cropFilter->SetInput(m_working_input);
376     cropFilter->ReleaseDataFlagOff();
377     cropFilter->Update();   
378     m_working_input = cropFilter->GetOutput();
379     this->template StopCurrentStep<ImageType>(m_working_input);    
380   }
381
382   // Update output info
383   this->GetOutput(0)->SetRegions(m_working_input->GetLargestPossibleRegion());  
384 }
385 //--------------------------------------------------------------------
386
387
388 //--------------------------------------------------------------------
389 template <class ImageType>
390 void 
391 clitk::SliceBySliceRelativePositionFilter<ImageType>::
392 GenerateData() 
393 {
394   // Get input pointer
395   //--------------------------------------------------------------------
396   //--------------------------------------------------------------------  
397   // Final Step -> set output
398   //this->SetNthOutput(0, m_working_input);
399   if (this->GetFuzzyMapOnlyFlag()) return; // no output in this case
400   this->GraftOutput(m_working_input);
401   return;
402 }
403 //--------------------------------------------------------------------
404