1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
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
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.
13 It is distributed under dual licence
15 - BSD See included LICENSE.txt file
16 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ======================================================================-====*/
20 #include "clitkCommon.h"
21 #include "clitkBooleanOperatorLabelImageFilter.h"
22 #include "clitkAutoCropFilter.h"
23 #include "clitkResampleImageWithOptionsFilter.h"
24 #include "clitkBooleanOperatorLabelImageFilter.h"
28 #include "itkStatisticsLabelMapFilter.h"
29 #include "itkLabelImageToStatisticsLabelMapFilter.h"
30 #include "itkRegionOfInterestImageFilter.h"
31 #include "itkBinaryThresholdImageFilter.h"
32 #include "itkBinaryErodeImageFilter.h"
33 #include "itkBinaryBallStructuringElement.h"
36 #include "RelativePositionPropImageFilter.h"
38 //--------------------------------------------------------------------
39 template <class ImageType>
40 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
41 AddRelativePositionConstraintToLabelImageFilter():
43 itk::ImageToImageFilter<ImageType, ImageType>()
45 this->SetNumberOfRequiredInputs(2);
46 SetFuzzyThreshold(0.6);
47 SetBackgroundValue(0);
48 SetObjectBackgroundValue(0);
49 SetOrientationType(LeftTo);
50 ResampleBeforeRelativePositionFilterOn();
51 SetIntermediateSpacing(10);
55 //--------------------------------------------------------------------
58 //--------------------------------------------------------------------
59 template <class ImageType>
61 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
62 SetInput(const ImageType * image)
64 // Process object is not const-correct so the const casting is required.
65 this->SetNthInput(0, const_cast<ImageType *>(image));
67 //--------------------------------------------------------------------
70 //--------------------------------------------------------------------
71 template <class ImageType>
73 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
74 SetInputObject(const ImageType * image)
76 // Process object is not const-correct so the const casting is required.
77 this->SetNthInput(1, const_cast<ImageType *>(image));
79 //--------------------------------------------------------------------
82 //--------------------------------------------------------------------
83 template <class ImageType>
85 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
86 SetOrientationTypeString(std::string t)
88 SetOrientationType(Angle);
89 if (t[0] == 'L') SetOrientationType(LeftTo);
90 if (t[0] == 'R') SetOrientationType(RightTo);
91 if (t[0] == 'A') SetOrientationType(AntTo);
92 if (t[0] == 'P') SetOrientationType(PostTo);
93 if (t[0] == 'S') SetOrientationType(SupTo);
94 if (t[0] == 'I') SetOrientationType(InfTo);
96 //--------------------------------------------------------------------
99 //--------------------------------------------------------------------
100 template <class ImageType>
102 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
103 GenerateOutputInformation()
105 ImagePointer input = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
106 ImagePointer outputImage = this->GetOutput(0);
107 outputImage->SetRegions(outputImage->GetLargestPossibleRegion());
109 //--------------------------------------------------------------------
112 //--------------------------------------------------------------------
113 template <class ImageType>
115 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
116 GenerateInputRequestedRegion()
119 itk::ImageToImageFilter<ImageType, ImageType>::GenerateInputRequestedRegion();
120 // Get input pointers and set requested region to common region
121 ImagePointer input1 = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
122 ImagePointer input2 = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
123 input1->SetRequestedRegion(input1->GetLargestPossibleRegion());
124 input2->SetRequestedRegion(input2->GetLargestPossibleRegion());
126 //--------------------------------------------------------------------
129 //--------------------------------------------------------------------
130 template <class ImageType>
132 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
135 SetOrientationType(Angle);
138 //--------------------------------------------------------------------
141 //--------------------------------------------------------------------
142 template <class ImageType>
144 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
147 SetOrientationType(Angle);
150 //--------------------------------------------------------------------
153 //--------------------------------------------------------------------
154 template <class ImageType>
156 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
157 SetOrientationType(OrientationTypeEnumeration orientation)
159 m_OrientationType = orientation;
160 switch (m_OrientationType) {
161 case LeftTo: m_Angle1 = clitk::deg2rad(0); m_Angle2 = clitk::deg2rad(0); break;
162 case RightTo: m_Angle1 = clitk::deg2rad(180); m_Angle2 = clitk::deg2rad(0); break;
163 case AntTo: m_Angle1 = clitk::deg2rad(90); m_Angle2 = clitk::deg2rad(0); break;
164 case PostTo: m_Angle1 = clitk::deg2rad(-90); m_Angle2 = clitk::deg2rad(0); break;
165 case InfTo: m_Angle1 = clitk::deg2rad(0); m_Angle2 = clitk::deg2rad(90); break;
166 case SupTo: m_Angle1 = clitk::deg2rad(0); m_Angle2 = clitk::deg2rad(-90); break;
178 //--------------------------------------------------------------------
181 //--------------------------------------------------------------------
182 template <class ImageType>
184 clitk::AddRelativePositionConstraintToLabelImageFilter<ImageType>::
189 DD(GetFuzzyThreshold());
190 DD((int)GetBackgroundValue());
191 DD((int)GetObjectBackgroundValue());
192 DD(GetOrientationType());
193 DD(GetResampleBeforeRelativePositionFilter());
194 DD(GetIntermediateSpacing());
195 DD(GetAutoCropFlag());
200 input = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
201 object = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
203 //--------------------------------------------------------------------
204 //--------------------------------------------------------------------
205 static const unsigned int dim = ImageType::ImageDimension;
206 StartNewStep("Initial resample");
208 if (m_ResampleBeforeRelativePositionFilter) {
209 typedef clitk::ResampleImageWithOptionsFilter<ImageType> ResampleFilterType;
210 typename ResampleFilterType::Pointer resampleFilter = ResampleFilterType::New();
211 resampleFilter->SetInput(object);
212 resampleFilter->SetOutputIsoSpacing(m_IntermediateSpacing);
213 resampleFilter->SetGaussianFilteringEnabled(false);
214 // resampleFilter->SetVerboseOptions(true);
215 resampleFilter->Update();
216 working_image = resampleFilter->GetOutput();
219 working_image = object;
221 StopCurrentStep<ImageType>(working_image);
223 // Step 2: object pad to input image -> we want to compute the
224 // relative position for each point belonging to the input image
225 // domain, so we have to extend (pad) the object image to fit the
227 if (!clitk::HaveSameSizeAndSpacing<ImageType, ImageType>(input, working_image)) {
228 StartNewStep("Pad object to image size");
229 typename ImageType::Pointer output = ImageType::New();
231 for(unsigned int i=0; i<dim; i++) {
232 size[i] = lrint((input->GetLargestPossibleRegion().GetSize()[i]*input->GetSpacing()[i])/(double)working_image->GetSpacing()[i]);
235 // The index of the input is not necessarily zero, so we have to
236 // take it into account (not done)
238 IndexType index = input->GetLargestPossibleRegion().GetIndex();
239 region.SetSize(size);
240 for(unsigned int i=0; i<dim; i++) {
242 std::cerr << "Index diff from zero : " << index << ". not done yet !" << std::endl;
246 // output->SetLargestPossibleRegion(region);
247 output->SetRegions(region);
248 output->SetSpacing(working_image->GetSpacing());
249 PointType origin = input->GetOrigin();
250 for(unsigned int i=0; i<dim; i++) {
251 origin[i] = index[i]*input->GetSpacing()[i] + input->GetOrigin()[i];
253 output->SetOrigin(origin);
254 // output->SetOrigin(input->GetOrigin());
257 output->FillBuffer(m_BackgroundValue);
258 typename PadFilterType::Pointer padFilter = PadFilterType::New();
259 // typename PadFilterType::InputImageIndexType index;
260 for(unsigned int i=0; i<dim; i++) {
261 index[i] = -index[i]*input->GetSpacing()[i]/(double)working_image->GetSpacing()[i]
262 + lrint((working_image->GetOrigin()[i] - input->GetOrigin()[i])/working_image->GetSpacing()[i]);
264 padFilter->SetSourceImage(working_image);
265 padFilter->SetDestinationImage(output);
266 padFilter->SetDestinationIndex(index);
267 padFilter->SetSourceRegion(working_image->GetLargestPossibleRegion());
269 working_image = padFilter->GetOutput();
270 StopCurrentStep<ImageType>(working_image);
273 // DD("[debug] RelPos : same size and spacing : no padding");
275 // Keep object image (with resampline and pad)
276 object_resampled = working_image;
277 // StopCurrentStep<ImageType>(working_image);
279 // Step 3: compute rel pos in object
280 StartNewStep("Relative Position Map");
281 typedef itk::RelativePositionPropImageFilter<ImageType, FloatImageType> RelPosFilterType;
282 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
283 relPosFilter->SetInput(working_image);
284 relPosFilter->SetAlpha1(m_Angle1); // xy plane
285 relPosFilter->SetAlpha2(m_Angle2);
286 relPosFilter->SetK1(M_PI/2.0); // Opening parameter, default = pi/2
287 relPosFilter->SetFast(true);
288 relPosFilter->SetRadius(1); // seems sufficient in this cas
289 // relPosFilter->SetVerboseProgress(true);
290 relPosFilter->Update();
291 relPos = relPosFilter->GetOutput();
292 StopCurrentStep<FloatImageType>(relPos);
294 //--------------------------------------------------------------------
295 //--------------------------------------------------------------------
296 StartNewStep("Map Threshold");
298 typedef itk::BinaryThresholdImageFilter<FloatImageType, ImageType> BinaryThresholdImageFilterType;
299 typename BinaryThresholdImageFilterType::Pointer thresholdFilter = BinaryThresholdImageFilterType::New();
300 thresholdFilter->SetInput(relPos);
301 thresholdFilter->SetOutsideValue(m_BackgroundValue);
302 thresholdFilter->SetInsideValue(m_BackgroundValue+1);
303 thresholdFilter->SetLowerThreshold(m_FuzzyThreshold);
304 thresholdFilter->Update();
305 working_image = thresholdFilter->GetOutput();
306 StopCurrentStep<ImageType>(working_image);
308 //--------------------------------------------------------------------
309 //--------------------------------------------------------------------
310 StartNewStep("Post Processing: erosion with initial mask");
311 // Step 2 : erosion with initial mask to exclude pixels that were
312 // inside the resampled version and outside the original mask
313 typedef itk::BinaryBallStructuringElement<unsigned int, ImageDimension> StructuringElementType;
314 StructuringElementType kernel;
316 kernel.CreateStructuringElement();
317 typedef itk::BinaryErodeImageFilter<ImageType, ImageType, StructuringElementType> ErodeFilterType;
318 typename ErodeFilterType::Pointer erodeFilter = ErodeFilterType::New();
319 erodeFilter->SetInput(working_image);
320 erodeFilter->SetKernel(kernel);
321 erodeFilter->SetBackgroundValue(m_BackgroundValue);
322 erodeFilter->SetErodeValue(m_BackgroundValue+1);
323 erodeFilter->Update();
324 working_image = erodeFilter->GetOutput();
325 StopCurrentStep<ImageType>(working_image);
327 //--------------------------------------------------------------------
328 //--------------------------------------------------------------------
329 // Step 5: resample to initial spacing
330 if (m_ResampleBeforeRelativePositionFilter) {
331 StartNewStep("Resample to get the same sampling than input");
332 typedef clitk::ResampleImageWithOptionsFilter<ImageType> ResampleFilterType;
333 typename ResampleFilterType::Pointer resampleFilter = ResampleFilterType::New();
334 resampleFilter->SetDefaultPixelValue(m_BackgroundValue);
335 resampleFilter->SetInput(working_image);
336 resampleFilter->SetOutputSpacing(input->GetSpacing());
337 resampleFilter->SetGaussianFilteringEnabled(false);
338 // resampleFilter->SetVerboseOptions(true);
339 resampleFilter->Update();
340 working_image = resampleFilter->GetOutput();
341 StopCurrentStep<ImageType>(working_image);
344 //--------------------------------------------------------------------
345 //--------------------------------------------------------------------
346 // Pre Step 6: pad if not the same size : it can occur when downsample and upsample
347 // DD(working_image->GetLargestPossibleRegion());
348 // DD(input->GetLargestPossibleRegion());
349 //if (!HaveSameSizeAndSpacing(working_image, input)) {
350 if (working_image->GetLargestPossibleRegion() != input->GetLargestPossibleRegion()) {
351 StartNewStep("Pad to get the same size than input");
352 typename ImageType::Pointer temp = ImageType::New();
353 temp->CopyInformation(input);
354 temp->SetRegions(input->GetLargestPossibleRegion()); // Do not forget !!
356 temp->FillBuffer(m_BackgroundValue);
357 typename PadFilterType::Pointer padFilter2 = PadFilterType::New();
358 padFilter2->SetSourceImage(working_image);
359 padFilter2->SetDestinationImage(temp);
360 // DD(input->GetLargestPossibleRegion().GetIndex());
361 padFilter2->SetDestinationIndex(input->GetLargestPossibleRegion().GetIndex());
362 padFilter2->SetSourceRegion(working_image->GetLargestPossibleRegion());
363 padFilter2->Update();
364 working_image = padFilter2->GetOutput();
365 StopCurrentStep<ImageType>(working_image);
368 //DD("[debug] Rel Pos : no padding after");
371 //--------------------------------------------------------------------
372 //--------------------------------------------------------------------
373 // Step 6: combine input+thresholded relpos
374 StartNewStep("Combine with initial input (boolean And)");
375 typedef clitk::BooleanOperatorLabelImageFilter<ImageType> BoolFilterType;
376 typename BoolFilterType::Pointer combineFilter = BoolFilterType::New();
377 combineFilter->SetBackgroundValue(m_BackgroundValue);
378 combineFilter->SetBackgroundValue1(m_BackgroundValue);
379 combineFilter->SetBackgroundValue2(m_BackgroundValue);
380 combineFilter->SetForegroundValue(m_BackgroundValue+1);
381 combineFilter->SetInput1(input);
382 combineFilter->SetInput2(working_image);
384 combineFilter->SetOperationType(BoolFilterType::AndNot);
386 combineFilter->SetOperationType(BoolFilterType::And);
387 combineFilter->InPlaceOn();
388 combineFilter->Update();
389 working_image = combineFilter->GetOutput();
391 combineFilter = BoolFilterType::New();
392 combineFilter->SetInput1(working_image);
393 combineFilter->SetInput2(object);
394 combineFilter->SetOperationType(BoolFilterType::AndNot);
395 combineFilter->InPlaceOn();
396 combineFilter->Update();
398 working_image = combineFilter->GetOutput();
399 StopCurrentStep<ImageType>(working_image);
401 //--------------------------------------------------------------------
402 //--------------------------------------------------------------------
404 if (GetAutoCropFlag()) {
405 StartNewStep("Final AutoCrop");
406 typedef clitk::AutoCropFilter<ImageType> CropFilterType;
407 typename CropFilterType::Pointer cropFilter = CropFilterType::New();
408 cropFilter->SetInput(working_image);
409 cropFilter->ReleaseDataFlagOff();
410 cropFilter->Update();
411 working_image = cropFilter->GetOutput();
412 StopCurrentStep<ImageType>(working_image);
415 //--------------------------------------------------------------------
416 //--------------------------------------------------------------------
418 // Final Step -> set output
419 this->SetNthOutput(0, working_image);
420 // this->GraftOutput(working_image);
423 //--------------------------------------------------------------------