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://www.centreleonberard.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 ===========================================================================**/
19 #ifndef CLITKEXTRACTLUNGSFILTER_TXX
20 #define CLITKEXTRACTLUNGSFILTER_TXX
23 #include "clitkImageCommon.h"
24 #include "clitkSetBackgroundImageFilter.h"
25 #include "clitkSegmentationUtils.h"
26 #include "clitkAutoCropFilter.h"
27 #include "clitkCropLikeImageFilter.h"
28 #include "clitkFillMaskFilter.h"
29 #include "clitkMemoryUsage.h"
32 #include "itkBinaryThresholdImageFilter.h"
33 #include "itkConnectedComponentImageFilter.h"
34 #include "itkRelabelComponentImageFilter.h"
35 #include "itkOtsuThresholdImageFilter.h"
36 #include "itkBinaryThinningImageFilter3D.h"
37 #include "itkImageIteratorWithIndex.h"
38 #include "itkBinaryMorphologicalOpeningImageFilter.h"
39 #include "itkBinaryMorphologicalClosingImageFilter.h"
41 //--------------------------------------------------------------------
42 template <class ImageType>
43 clitk::ExtractLungFilter<ImageType>::
46 clitk::FilterWithAnatomicalFeatureDatabaseManagement(),
47 itk::ImageToImageFilter<ImageType, MaskImageType>()
50 m_MaxSeedNumber = 500;
52 // Default global options
53 this->SetNumberOfRequiredInputs(1);
54 SetPatientMaskBackgroundValue(0);
55 SetBackgroundValue(0); // Must be zero
56 SetForegroundValue(1);
57 SetMinimalComponentSize(100);
58 VerboseRegionGrowingFlagOff();
60 // Step 1 default values
61 SetUpperThreshold(-300);
62 SetLowerThreshold(-1000);
63 UseLowerThresholdOff();
64 LabelParamType * p1 = new LabelParamType;
67 p1->AddLabelToRemove(2);
68 SetLabelizeParameters1(p1);
70 // Step 2 default values
71 SetUpperThresholdForTrachea(-900);
72 SetMultiplierForTrachea(5);
73 SetThresholdStepSizeForTrachea(64);
74 SetNumberOfSlicesToSkipBeforeSearchingSeed(0);
75 TracheaVolumeMustBeCheckedFlagOn();
77 // Step 3 default values
78 SetNumberOfHistogramBins(500);
79 LabelParamType * p2 = new LabelParamType;
82 // p->AddLabelToRemove(2); // No label to remove by default
83 SetLabelizeParameters2(p2);
85 // Step 4 default values
86 SetRadiusForTrachea(1);
87 LabelParamType * p3 = new LabelParamType;
91 SetLabelizeParameters3(p3);
95 SetOpenCloseRadius(1);
101 //--------------------------------------------------------------------
104 //--------------------------------------------------------------------
105 template <class ImageType>
107 clitk::ExtractLungFilter<ImageType>::
108 SetInput(const ImageType * image)
110 this->SetNthInput(0, const_cast<ImageType *>(image));
112 //--------------------------------------------------------------------
115 //--------------------------------------------------------------------
116 template <class ImageType>
118 clitk::ExtractLungFilter<ImageType>::
119 AddSeed(InternalIndexType s)
121 m_Seeds.push_back(s);
123 //--------------------------------------------------------------------
126 //--------------------------------------------------------------------
127 template <class ImageType>
129 clitk::ExtractLungFilter<ImageType>::
130 GenerateOutputInformation()
132 clitk::PrintMemory(GetVerboseMemoryFlag(), "Initial memory"); // OK
133 Superclass::GenerateOutputInformation();
138 // Get input pointers
139 input = dynamic_cast<const ImageType*>(itk::ProcessObject::GetInput(0));
140 patient = GetAFDB()->template GetImage <MaskImageType>("Patient");
141 PrintMemory(GetVerboseMemoryFlag(), "After reading patient"); // OK
143 //--------------------------------------------------------------------
144 //--------------------------------------------------------------------
145 // Crop input like patient image (must have the same spacing)
146 // It copy the input if the same are the same
147 StartNewStep("Copy and crop input image to 'patient' extends");
148 typedef clitk::CropLikeImageFilter<ImageType> CropImageFilter;
149 typename CropImageFilter::Pointer cropFilter = CropImageFilter::New();
150 // cropFilter->ReleaseDataFlagOn(); // NO=seg fault !!
151 cropFilter->SetInput(input);
152 cropFilter->SetCropLikeImage(patient);
153 cropFilter->Update();
154 working_input = cropFilter->GetOutput();
155 // cropFilter->Delete(); // NO !!!! if yes, sg fault, Cropfilter is buggy !??
156 StopCurrentStep<ImageType>(working_input);
157 PrintMemory(GetVerboseMemoryFlag(), "After crop"); // OK, slightly more than a copy of input
159 //--------------------------------------------------------------------
160 //--------------------------------------------------------------------
161 StartNewStep("Set background to initial image");
162 working_input = SetBackground<ImageType, MaskImageType>
163 (working_input, patient, GetPatientMaskBackgroundValue(), -1000, true);
164 StopCurrentStep<ImageType>(working_input);
165 PrintMemory(GetVerboseMemoryFlag(), "After set bg"); // OK, additional mem = 0
168 // We do not need patient mask anymore, release memory
169 GetAFDB()->template ReleaseImage<MaskImageType>("Patient");
170 DD(patient->GetReferenceCount());
171 PrintMemory(GetVerboseMemoryFlag(), "After delete patient"); // OK, additional mem = 0
173 PrintMemory(GetVerboseMemoryFlag(), "After delete patient"); // OK, additional mem = 0
176 //--------------------------------------------------------------------
177 //--------------------------------------------------------------------
178 StartNewStep("Remove Air");
180 if (m_UseLowerThreshold) {
181 if (m_LowerThreshold > m_UpperThreshold) {
182 clitkExceptionMacro("lower threshold cannot be greater than upper threshold.");
185 // Threshold to get air
186 typedef itk::BinaryThresholdImageFilter<ImageType, MaskImageType> InputBinarizeFilterType;
187 typename InputBinarizeFilterType::Pointer binarizeFilter = InputBinarizeFilterType::New();
188 binarizeFilter->SetInput(working_input);
189 if (m_UseLowerThreshold) binarizeFilter->SetLowerThreshold(m_LowerThreshold);
190 // binarizeFilter->CanRunInPlace() is false
191 binarizeFilter->SetUpperThreshold(m_UpperThreshold);
192 binarizeFilter->SetInsideValue(this->GetForegroundValue());
193 binarizeFilter->SetOutsideValue(this->GetBackgroundValue());
194 binarizeFilter->Update();
195 working_mask = binarizeFilter->GetOutput();
196 PrintMemory(GetVerboseMemoryFlag(), "After Binarizefilter"); // OK, additional mem is one mask image
198 // Labelize and keep right labels
200 Labelize<MaskImageType>
201 (working_mask, GetBackgroundValue(), true, GetMinimalComponentSize());
202 PrintMemory(GetVerboseMemoryFlag(), "After Labelize"); // BUG ? additional mem around 1 time the input ?
204 working_mask = RemoveLabels<MaskImageType>
205 (working_mask, GetBackgroundValue(), GetLabelizeParameters1()->GetLabelsToRemove());
206 PrintMemory(GetVerboseMemoryFlag(), "After RemoveLabels"); // OK additional mem = 0
208 working_mask = KeepLabels<MaskImageType>
210 GetBackgroundValue(),
211 GetForegroundValue(),
212 GetLabelizeParameters1()->GetFirstKeep(),
213 GetLabelizeParameters1()->GetLastKeep(),
214 GetLabelizeParameters1()->GetUseLastKeep());
215 PrintMemory(GetVerboseMemoryFlag(), "After KeepLabels to create the 'air'");
218 working_input = SetBackground<ImageType, MaskImageType>
219 (working_input, working_mask, this->GetForegroundValue(), this->GetBackgroundValue(), true);
220 PrintMemory(GetVerboseMemoryFlag(), "After SetBackground");
223 StopCurrentStep<ImageType>(working_input);
225 //--------------------------------------------------------------------
226 //--------------------------------------------------------------------
227 StartNewStep("Search for the trachea");
229 PrintMemory(GetVerboseMemoryFlag(), "After SearchForTrachea");
231 //--------------------------------------------------------------------
232 //--------------------------------------------------------------------
233 StartNewStep("Extract the lung with Otsu filter");
234 // Automated Otsu thresholding and relabeling
235 typedef itk::OtsuThresholdImageFilter<ImageType,MaskImageType> OtsuThresholdImageFilterType;
236 typename OtsuThresholdImageFilterType::Pointer otsuFilter=OtsuThresholdImageFilterType::New();
237 otsuFilter->SetInput(working_input);
238 otsuFilter->SetNumberOfHistogramBins(GetNumberOfHistogramBins());
239 otsuFilter->SetInsideValue(this->GetForegroundValue());
240 otsuFilter->SetOutsideValue(this->GetBackgroundValue());
241 otsuFilter->Update();
242 working_mask = otsuFilter->GetOutput();
245 StopCurrentStep<MaskImageType>(working_mask);
246 PrintMemory(GetVerboseMemoryFlag(), "After Otsufilter");
248 //--------------------------------------------------------------------
249 //--------------------------------------------------------------------
250 StartNewStep("Select labels");
252 working_mask = LabelizeAndSelectLabels<MaskImageType>
254 GetBackgroundValue(),
255 GetForegroundValue(),
257 GetMinimalComponentSize(),
258 GetLabelizeParameters2());
261 StopCurrentStep<MaskImageType>(working_mask);
262 PrintMemory(GetVerboseMemoryFlag(), "After LabelizeAndSelectLabels");
264 //--------------------------------------------------------------------
265 //--------------------------------------------------------------------
266 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
267 StartNewStep("Remove the trachea");
269 working_mask = SetBackground<MaskImageType, MaskImageType>
270 (working_mask, trachea, 1, -1, true);
271 PrintMemory(GetVerboseMemoryFlag(), "After SetBackground");
273 // Dilate the trachea
274 static const unsigned int Dim = ImageType::ImageDimension;
275 typedef itk::BinaryBallStructuringElement<InternalPixelType, Dim> KernelType;
276 KernelType structuringElement;
277 structuringElement.SetRadius(GetRadiusForTrachea());
278 structuringElement.CreateStructuringElement();
279 typedef clitk::ConditionalBinaryDilateImageFilter<MaskImageType, MaskImageType, KernelType> ConditionalBinaryDilateImageFilterType;
280 typename ConditionalBinaryDilateImageFilterType::Pointer dilateFilter = ConditionalBinaryDilateImageFilterType::New();
281 dilateFilter->SetBoundaryToForeground(false);
282 dilateFilter->SetKernel(structuringElement);
283 dilateFilter->SetBackgroundValue (1);
284 dilateFilter->SetForegroundValue (-1);
285 dilateFilter->SetInput (working_mask);
286 dilateFilter->Update();
287 working_mask = dilateFilter->GetOutput();
288 PrintMemory(GetVerboseMemoryFlag(), "After dilate");
290 // Set trachea with dilatation
291 trachea = SetBackground<MaskImageType, MaskImageType>
292 (trachea, working_mask, -1, this->GetForegroundValue(), true);
294 // Remove the trachea
295 working_mask = SetBackground<MaskImageType, MaskImageType>
296 (working_mask, working_mask, -1, this->GetBackgroundValue(), true);
299 working_mask = LabelizeAndSelectLabels<MaskImageType>
301 GetBackgroundValue(),
302 GetForegroundValue(),
304 GetMinimalComponentSize(),
305 GetLabelizeParameters3());
308 StopCurrentStep<MaskImageType>(working_mask);
311 //--------------------------------------------------------------------
312 //--------------------------------------------------------------------
313 PrintMemory(GetVerboseMemoryFlag(), "before autocropfilter");
314 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
316 trachea = clitk::AutoCrop<MaskImageType>(trachea, GetBackgroundValue());
317 StopCurrentStep<MaskImageType>(trachea);
318 PrintMemory(GetVerboseMemoryFlag(), "after delete trachea");
320 PrintMemory(GetVerboseMemoryFlag(), "after delete trachea");
322 //--------------------------------------------------------------------
323 //--------------------------------------------------------------------
324 StartNewStep("Cropping lung");
325 PrintMemory(GetVerboseMemoryFlag(), "Before Autocropfilter");
327 working_mask = clitk::AutoCrop<MaskImageType>(working_mask, GetBackgroundValue());
328 StopCurrentStep<MaskImageType>(working_mask);
330 //--------------------------------------------------------------------
331 //--------------------------------------------------------------------
333 if (GetOpenCloseFlag()) {
334 StartNewStep("Open/Close");
335 PrintMemory(GetVerboseMemoryFlag(), "Before OpenClose");
337 // Structuring element
338 typedef itk::BinaryBallStructuringElement<InternalPixelType, ImageDimension> KernelType;
339 KernelType structuringElement;
340 structuringElement.SetRadius(GetOpenCloseRadius());
341 structuringElement.CreateStructuringElement();
344 typedef itk::BinaryMorphologicalOpeningImageFilter<MaskImageType, InternalImageType, KernelType> OpenFilterType;
345 typename OpenFilterType::Pointer openFilter = OpenFilterType::New();
346 openFilter->SetInput(working_mask);
347 openFilter->SetBackgroundValue(GetBackgroundValue());
348 openFilter->SetForegroundValue(GetForegroundValue());
349 openFilter->SetKernel(structuringElement);
352 typedef itk::BinaryMorphologicalClosingImageFilter<MaskImageType, MaskImageType, KernelType> CloseFilterType;
353 typename CloseFilterType::Pointer closeFilter = CloseFilterType::New();
354 closeFilter->SetInput(openFilter->GetOutput());
355 closeFilter->SetSafeBorder(true);
356 closeFilter->SetForegroundValue(GetForegroundValue());
357 closeFilter->SetKernel(structuringElement);
358 closeFilter->Update();
359 working_mask = closeFilter->GetOutput();
362 //--------------------------------------------------------------------
363 //--------------------------------------------------------------------
365 if (GetFillHolesFlag()) {
366 StartNewStep("Fill Holes");
367 PrintMemory(GetVerboseMemoryFlag(), "Before Fill Holes");
368 typedef clitk::FillMaskFilter<MaskImageType> FillMaskFilterType;
369 typename FillMaskFilterType::Pointer fillMaskFilter = FillMaskFilterType::New();
370 fillMaskFilter->SetInput(working_mask);
371 fillMaskFilter->AddDirection(2);
372 //fillMaskFilter->AddDirection(1);
373 fillMaskFilter->Update();
374 working_mask = fillMaskFilter->GetOutput();
375 StopCurrentStep<MaskImageType>(working_mask);
378 //--------------------------------------------------------------------
379 //--------------------------------------------------------------------
380 StartNewStep("Separate Left/Right lungs");
381 PrintMemory(GetVerboseMemoryFlag(), "Before Separate");
383 working_mask = Labelize<MaskImageType>(working_mask,
384 GetBackgroundValue(),
386 GetMinimalComponentSize());
388 PrintMemory(GetVerboseMemoryFlag(), "After Labelize");
391 typedef itk::StatisticsImageFilter<MaskImageType> StatisticsImageFilterType;
392 typename StatisticsImageFilterType::Pointer statisticsImageFilter=StatisticsImageFilterType::New();
393 statisticsImageFilter->SetInput(working_mask);
394 statisticsImageFilter->Update();
395 unsigned int initialNumberOfLabels = statisticsImageFilter->GetMaximum();
396 working_mask = statisticsImageFilter->GetOutput();
398 PrintMemory(GetVerboseMemoryFlag(), "After count label");
400 // Decompose the first label
401 static const unsigned int Dim = ImageType::ImageDimension;
402 if (initialNumberOfLabels<2) {
403 // Structuring element radius
404 typename ImageType::SizeType radius;
405 for (unsigned int i=0;i<Dim;i++) radius[i]=1;
406 typedef clitk::DecomposeAndReconstructImageFilter<MaskImageType,MaskImageType> DecomposeAndReconstructFilterType;
407 typename DecomposeAndReconstructFilterType::Pointer decomposeAndReconstructFilter=DecomposeAndReconstructFilterType::New();
408 decomposeAndReconstructFilter->SetInput(working_mask);
409 decomposeAndReconstructFilter->SetVerbose(false);
410 decomposeAndReconstructFilter->SetRadius(radius);
411 decomposeAndReconstructFilter->SetMaximumNumberOfLabels(2);
412 decomposeAndReconstructFilter->SetMinimumObjectSize(this->GetMinimalComponentSize());
413 decomposeAndReconstructFilter->SetMinimumNumberOfIterations(1);
414 decomposeAndReconstructFilter->SetBackgroundValue(this->GetBackgroundValue());
415 decomposeAndReconstructFilter->SetForegroundValue(this->GetForegroundValue());
416 decomposeAndReconstructFilter->SetFullyConnected(true);
417 decomposeAndReconstructFilter->SetNumberOfNewLabels(1);
418 decomposeAndReconstructFilter->Update();
419 working_mask = decomposeAndReconstructFilter->GetOutput();
421 PrintMemory(GetVerboseMemoryFlag(), "After decomposeAndReconstructFilter");
423 // Retain labels ('1' is largset lung, so right. '2' is left)
424 typedef itk::ThresholdImageFilter<MaskImageType> ThresholdImageFilterType;
425 typename ThresholdImageFilterType::Pointer thresholdFilter = ThresholdImageFilterType::New();
426 thresholdFilter->SetInput(working_mask);
427 thresholdFilter->ThresholdAbove(2);
428 thresholdFilter->SetOutsideValue(this->GetBackgroundValue());
429 thresholdFilter->Update();
430 working_mask = thresholdFilter->GetOutput();
431 StopCurrentStep<MaskImageType> (working_mask);
432 PrintMemory(GetVerboseMemoryFlag(), "After Thresholdfilter");
434 // Update output info
435 // output = working_mask;
436 //this->GetOutput(0)->SetRegions(output->GetLargestPossibleRegion());
438 // this->GetOutput(0)->SetRegions(working_mask->GetLargestPossibleRegion());
441 //--------------------------------------------------------------------
444 //--------------------------------------------------------------------
445 template <class TImageType>
447 clitk::ExtractLungFilter<TImageType>::
448 GenerateInputRequestedRegion() {
449 // DD("GenerateInputRequestedRegion (nothing?)");
451 //--------------------------------------------------------------------
454 //--------------------------------------------------------------------
455 template <class ImageType>
457 clitk::ExtractLungFilter<ImageType>::
461 // this->GraftOutput(output); // not SetNthOutput
462 this->GraftOutput(working_mask); // not SetNthOutput
463 // Store image filenames into AFDB
464 GetAFDB()->SetImageFilename("Lungs", this->GetOutputLungFilename());
465 GetAFDB()->SetImageFilename("Trachea", this->GetOutputTracheaFilename());
468 //--------------------------------------------------------------------
471 //--------------------------------------------------------------------
472 template <class ImageType>
474 clitk::ExtractLungFilter<ImageType>::
475 SearchForTracheaSeed(int skip)
477 if (m_Seeds.size() == 0) { // try to find seed (if not zero, it is given by user)
478 // Restart the search until a seed is found, skipping more and more slices
481 // Search seed (parameters = UpperThresholdForTrachea)
482 static const unsigned int Dim = ImageType::ImageDimension;
483 typename InternalImageType::RegionType sliceRegion = working_input->GetLargestPossibleRegion();
484 typename InternalImageType::SizeType sliceRegionSize = sliceRegion.GetSize();
485 typename InternalImageType::IndexType sliceRegionIndex = sliceRegion.GetIndex();
486 sliceRegionIndex[Dim-1]=sliceRegionSize[Dim-1]-skip-5;
487 sliceRegionSize[Dim-1]=5;
488 sliceRegion.SetSize(sliceRegionSize);
489 sliceRegion.SetIndex(sliceRegionIndex);
490 typedef itk::ImageRegionConstIterator<ImageType> IteratorType;
491 IteratorType it(working_input, sliceRegion);
493 while (!it.IsAtEnd()) {
494 if(it.Get() < GetUpperThresholdForTrachea() ) {
495 AddSeed(it.GetIndex());
496 // DD(it.GetIndex());
501 // if we do not found : restart
502 stop = (m_Seeds.size() != 0);
504 if (GetVerboseStepFlag()) {
505 std::cout << "\t No seed found this time. I skip some slices and restart." << std::endl;
507 if (skip > 0.5 * working_input->GetLargestPossibleRegion().GetSize()[2]) {
508 // we want to skip more than a half of the image, it is probably a bug
509 std::cerr << "Number of slices to skip to find trachea to high = " << skip << std::endl;
516 // DD(m_Seeds.size());
520 return (m_Seeds.size() != 0);
522 //--------------------------------------------------------------------
525 //--------------------------------------------------------------------
526 template <class ImageType>
528 clitk::ExtractLungFilter<ImageType>::
529 TracheaRegionGrowing()
531 // Explosion controlled region growing
532 PrintMemory(GetVerboseMemoryFlag(), "Before ExplosionControlledThresholdConnectedImageFilter");
533 typedef clitk::ExplosionControlledThresholdConnectedImageFilter<ImageType, MaskImageType> ImageFilterType;
534 typename ImageFilterType::Pointer f= ImageFilterType::New();
535 f->SetInput(working_input);
537 f->SetUpper(GetUpperThresholdForTrachea());
538 f->SetMinimumLowerThreshold(-2000);
539 // f->SetMaximumUpperThreshold(0); // MAYBE TO CHANGE ???
540 f->SetMaximumUpperThreshold(-800); // MAYBE TO CHANGE ???
541 f->SetAdaptLowerBorder(false);
542 f->SetAdaptUpperBorder(true);
543 f->SetMinimumSize(5000);
544 f->SetReplaceValue(1);
545 f->SetMultiplier(GetMultiplierForTrachea());
546 f->SetThresholdStepSize(GetThresholdStepSizeForTrachea());
547 f->SetMinimumThresholdStepSize(1);
548 f->SetVerbose(GetVerboseRegionGrowingFlag());
549 for(unsigned int i=0; i<m_Seeds.size();i++) {
550 f->AddSeed(m_Seeds[i]);
554 PrintMemory(GetVerboseMemoryFlag(), "After RG update");
556 // take first (main) connected component
557 trachea = Labelize<MaskImageType>(f->GetOutput(),
558 GetBackgroundValue(),
560 1000);//GetMinimalComponentSize());
561 PrintMemory(GetVerboseMemoryFlag(), "After Labelize");
562 trachea = KeepLabels<MaskImageType>(trachea,
563 GetBackgroundValue(),
564 GetForegroundValue(),
566 PrintMemory(GetVerboseMemoryFlag(), "After KeepLabels");
568 //--------------------------------------------------------------------
571 //--------------------------------------------------------------------
572 template <class ImageType>
574 clitk::ExtractLungFilter<ImageType>::
575 ComputeTracheaVolume()
577 typedef itk::ImageRegionConstIterator<InternalImageType> IteratorType;
578 IteratorType iter(trachea, trachea->GetLargestPossibleRegion());
581 while (!iter.IsAtEnd()) {
582 if (iter.Get() == this->GetForegroundValue()) volume++;
586 double voxelsize = trachea->GetSpacing()[0]*trachea->GetSpacing()[1]*trachea->GetSpacing()[2];
587 return volume*voxelsize;
589 //--------------------------------------------------------------------
592 //--------------------------------------------------------------------
593 template <class ImageType>
595 clitk::ExtractLungFilter<ImageType>::
598 // Search for seed among n slices, skip some slices before starting
599 // if not found -> skip more and restart
601 // when seed found : perform region growing
602 // compute trachea volume
603 // if volume not plausible -> skip more slices and restart
607 int skip = GetNumberOfSlicesToSkipBeforeSearchingSeed();
610 if (SearchForTracheaSeed(skip)) {
611 TracheaRegionGrowing();
612 volume = ComputeTracheaVolume()/1000; // assume mm3, so divide by 1000 to get cc
613 if (GetWriteStepFlag()) {
614 writeImage<MaskImageType>(trachea, "step-trachea-"+toString(skip)+".mhd");
616 if (GetTracheaVolumeMustBeCheckedFlag()) {
617 if ((volume > 10) && (volume < 65 )) { // depend on image size ...
618 // Typical volume 22.59 cm 3 (± 7.69 cm 3 ) [ Leader 2004 ]
619 if (GetVerboseStepFlag()) {
620 std::cout << "\t Found trachea with volume " << volume << " cc." << std::endl;
624 if (GetVerboseStepFlag()) {
625 std::cout << "\t The volume of the trachea (" << volume
626 << " cc) seems not correct. I skip some slices (" << skip << ") and restart to find seeds."
631 // empty the list of seed
634 if (skip > 0.5 * working_input->GetLargestPossibleRegion().GetSize()[2]) {
635 // we want to skip more than a half of the image, it is probably a bug
636 std::cerr << "2 : Number of slices to skip to find trachea too high = " << skip << std::endl;
648 StopCurrentStep<MaskImageType>(trachea);
650 else { // Trachea not found
651 this->SetWarning("* WARNING * No seed found for trachea.");
655 //--------------------------------------------------------------------
657 #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX