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 ======================================================================-====*/
19 #ifndef CLITKEXTRACTLUNGSFILTER_TXX
20 #define CLITKEXTRACTLUNGSFILTER_TXX
23 #include "clitkImageCommon.h"
24 #include "clitkSetBackgroundImageFilter.h"
25 #include "clitkSegmentationUtils.h"
26 #include "clitkAutoCropFilter.h"
29 #include "itkBinaryThresholdImageFilter.h"
30 #include "itkConnectedComponentImageFilter.h"
31 #include "itkRelabelComponentImageFilter.h"
32 #include "itkOtsuThresholdImageFilter.h"
33 #include "itkBinaryThinningImageFilter3D.h"
34 #include "itkImageIteratorWithIndex.h"
37 //--------------------------------------------------------------------
38 template <class ImageType, class MaskImageType>
39 clitk::ExtractLungFilter<ImageType, MaskImageType>::
42 itk::ImageToImageFilter<ImageType, MaskImageType>()
45 m_MaxSeedNumber = 500;
47 // Default global options
48 this->SetNumberOfRequiredInputs(2);
49 SetPatientMaskBackgroundValue(0);
50 SetBackgroundValue(0); // Must be zero
51 SetForegroundValue(1);
52 SetMinimalComponentSize(100);
54 // Step 1 default values
55 SetUpperThreshold(-300);
56 SetLowerThreshold(-1000);
57 UseLowerThresholdOff();
58 LabelParamType * p1 = new LabelParamType;
61 p1->AddLabelToRemove(2);
62 SetLabelizeParameters1(p1);
64 // Step 2 default values
65 SetUpperThresholdForTrachea(-900);
66 SetMultiplierForTrachea(5);
67 SetThresholdStepSizeForTrachea(64);
68 SetNumberOfSlicesToSkipBeforeSearchingSeed(0);
70 // Step 3 default values
71 SetNumberOfHistogramBins(500);
72 LabelParamType * p2 = new LabelParamType;
75 // p->AddLabelToRemove(2); // No label to remove by default
76 SetLabelizeParameters2(p2);
78 // Step 4 default values
79 SetRadiusForTrachea(1);
80 LabelParamType * p3 = new LabelParamType;
84 SetLabelizeParameters3(p3);
86 // Step 5 : find bronchial bifurcations
87 FindBronchialBifurcationsOn();
89 //--------------------------------------------------------------------
92 //--------------------------------------------------------------------
93 template <class ImageType, class MaskImageType>
95 clitk::ExtractLungFilter<ImageType, MaskImageType>::
96 SetInput(const ImageType * image)
98 this->SetNthInput(0, const_cast<ImageType *>(image));
100 //--------------------------------------------------------------------
103 //--------------------------------------------------------------------
104 template <class ImageType, class MaskImageType>
106 clitk::ExtractLungFilter<ImageType, MaskImageType>::
107 SetInputPatientMask(MaskImageType * image, MaskImagePixelType bg )
109 this->SetNthInput(1, const_cast<MaskImageType *>(image));
110 SetPatientMaskBackgroundValue(bg);
112 //--------------------------------------------------------------------
115 //--------------------------------------------------------------------
116 template <class ImageType, class MaskImageType>
118 clitk::ExtractLungFilter<ImageType, MaskImageType>::
119 AddSeed(InternalIndexType s)
121 m_Seeds.push_back(s);
123 //--------------------------------------------------------------------
126 //--------------------------------------------------------------------
127 template <class ImageType, class MaskImageType>
128 template<class ArgsInfoType>
130 clitk::ExtractLungFilter<ImageType, MaskImageType>::
131 SetArgsInfo(ArgsInfoType mArgsInfo)
133 SetVerboseOption_GGO(mArgsInfo);
134 SetVerboseStep_GGO(mArgsInfo);
135 SetWriteStep_GGO(mArgsInfo);
136 SetVerboseWarningOff_GGO(mArgsInfo);
138 SetUpperThreshold_GGO(mArgsInfo);
139 SetLowerThreshold_GGO(mArgsInfo);
140 SetNumberOfSlicesToSkipBeforeSearchingSeed_GGO(mArgsInfo);
141 SetLabelizeParameters1_GGO(mArgsInfo);
142 if (!mArgsInfo.remove1_given) {
143 GetLabelizeParameters1()->AddLabelToRemove(2);
144 if (GetVerboseOption()) VerboseOption("remove1", 2);
147 SetUpperThresholdForTrachea_GGO(mArgsInfo);
148 SetMultiplierForTrachea_GGO(mArgsInfo);
149 SetThresholdStepSizeForTrachea_GGO(mArgsInfo);
150 AddSeed_GGO(mArgsInfo);
152 SetMinimalComponentSize_GGO(mArgsInfo);
154 SetNumberOfHistogramBins_GGO(mArgsInfo);
155 SetLabelizeParameters2_GGO(mArgsInfo);
157 SetRadiusForTrachea_GGO(mArgsInfo);
158 SetLabelizeParameters3_GGO(mArgsInfo);
160 //--------------------------------------------------------------------
163 //--------------------------------------------------------------------
164 template <class ImageType, class MaskImageType>
166 clitk::ExtractLungFilter<ImageType, MaskImageType>::
167 GenerateOutputInformation()
169 Superclass::GenerateOutputInformation();
170 //this->GetOutput(0)->SetRequestedRegion(this->GetOutput(0)->GetLargestPossibleRegion());
172 // Get input pointers
173 patient = dynamic_cast<const MaskImageType*>(itk::ProcessObject::GetInput(1));
174 input = dynamic_cast<const ImageType*>(itk::ProcessObject::GetInput(0));
177 if (!HaveSameSizeAndSpacing<ImageType, MaskImageType>(input, patient)) {
178 this->SetLastError("* ERROR * the images (input and patient mask) must have the same size & spacing");
182 //--------------------------------------------------------------------
183 //--------------------------------------------------------------------
184 StartNewStepOrStop("Set background to initial image");
185 working_input = SetBackground<ImageType, MaskImageType>
186 (input, patient, GetPatientMaskBackgroundValue(), -1000);
187 StopCurrentStep<ImageType>(working_input);
189 //--------------------------------------------------------------------
190 //--------------------------------------------------------------------
191 StartNewStepOrStop("Remove Air");
193 if (m_UseLowerThreshold) {
194 if (m_LowerThreshold > m_UpperThreshold) {
195 this->SetLastError("ERROR: lower threshold cannot be greater than upper threshold.");
199 // Threshold to get air
200 typedef itk::BinaryThresholdImageFilter<ImageType, InternalImageType> InputBinarizeFilterType;
201 typename InputBinarizeFilterType::Pointer binarizeFilter=InputBinarizeFilterType::New();
202 binarizeFilter->SetInput(working_input);
203 if (m_UseLowerThreshold) binarizeFilter->SetLowerThreshold(m_LowerThreshold);
204 binarizeFilter->SetUpperThreshold(m_UpperThreshold);
205 binarizeFilter ->SetInsideValue(this->GetForegroundValue());
206 binarizeFilter ->SetOutsideValue(this->GetBackgroundValue());
207 binarizeFilter->Update();
208 working_image = binarizeFilter->GetOutput();
210 // Labelize and keep right labels
211 working_image = Labelize<InternalImageType>(working_image, GetBackgroundValue(), true, GetMinimalComponentSize());
213 working_image = RemoveLabels<InternalImageType>
214 (working_image, GetBackgroundValue(), GetLabelizeParameters1()->GetLabelsToRemove());
216 typename InternalImageType::Pointer air = KeepLabels<InternalImageType>
218 GetBackgroundValue(),
219 GetForegroundValue(),
220 GetLabelizeParameters1()->GetFirstKeep(),
221 GetLabelizeParameters1()->GetLastKeep(),
222 GetLabelizeParameters1()->GetUseLastKeep());
225 working_input = SetBackground<ImageType, InternalImageType>
226 (working_input, air, this->GetForegroundValue(), this->GetBackgroundValue());
229 StopCurrentStep<ImageType>(working_input);
231 //--------------------------------------------------------------------
232 //--------------------------------------------------------------------
233 StartNewStepOrStop("Search for the trachea");
236 //--------------------------------------------------------------------
237 //--------------------------------------------------------------------
238 StartNewStepOrStop("Extract the lung with Otsu filter");
239 // Automated Otsu thresholding and relabeling
240 typedef itk::OtsuThresholdImageFilter<ImageType,InternalImageType> OtsuThresholdImageFilterType;
241 typename OtsuThresholdImageFilterType::Pointer otsuFilter=OtsuThresholdImageFilterType::New();
242 otsuFilter->SetInput(working_input);
243 otsuFilter->SetNumberOfHistogramBins(GetNumberOfHistogramBins());
244 otsuFilter->SetInsideValue(this->GetForegroundValue());
245 otsuFilter->SetOutsideValue(this->GetBackgroundValue());
246 otsuFilter->Update();
247 working_image = otsuFilter->GetOutput();
250 StopCurrentStep<InternalImageType>(working_image);
252 //--------------------------------------------------------------------
253 //--------------------------------------------------------------------
254 StartNewStepOrStop("Select labels");
256 working_image = LabelizeAndSelectLabels<InternalImageType>
258 GetBackgroundValue(),
259 GetForegroundValue(),
261 GetMinimalComponentSize(),
262 GetLabelizeParameters2());
265 StopCurrentStep<InternalImageType>(working_image);
267 //--------------------------------------------------------------------
268 //--------------------------------------------------------------------
269 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
270 StartNewStepOrStop("Remove the trachea");
272 working_image = SetBackground<InternalImageType, InternalImageType>
273 (working_image, trachea_tmp, 1, -1);
275 // Dilate the trachea
276 static const unsigned int Dim = ImageType::ImageDimension;
277 typedef itk::BinaryBallStructuringElement<InternalPixelType, Dim> KernelType;
278 KernelType structuringElement;
279 structuringElement.SetRadius(GetRadiusForTrachea());
280 structuringElement.CreateStructuringElement();
281 typedef clitk::ConditionalBinaryDilateImageFilter<InternalImageType, InternalImageType, KernelType> ConditionalBinaryDilateImageFilterType;
282 typename ConditionalBinaryDilateImageFilterType::Pointer dilateFilter = ConditionalBinaryDilateImageFilterType::New();
283 dilateFilter->SetBoundaryToForeground(false);
284 dilateFilter->SetKernel(structuringElement);
285 dilateFilter->SetBackgroundValue (1);
286 dilateFilter->SetForegroundValue (-1);
287 dilateFilter->SetInput (working_image);
288 dilateFilter->Update();
289 working_image = dilateFilter->GetOutput();
291 // Set trachea with dilatation
292 trachea_tmp = SetBackground<InternalImageType, InternalImageType>
293 (trachea_tmp, working_image, -1, this->GetForegroundValue());
295 // Remove the trachea
296 working_image = SetBackground<InternalImageType, InternalImageType>
297 (working_image, working_image, -1, this->GetBackgroundValue());
300 working_image = LabelizeAndSelectLabels<InternalImageType>
302 GetBackgroundValue(),
303 GetForegroundValue(),
305 GetMinimalComponentSize(),
306 GetLabelizeParameters3());
309 StopCurrentStep<InternalImageType>(working_image);
312 //--------------------------------------------------------------------
313 //--------------------------------------------------------------------
314 typedef clitk::AutoCropFilter<InternalImageType> CropFilterType;
315 typename CropFilterType::Pointer cropFilter = CropFilterType::New();
316 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
317 StartNewStepOrStop("Croping trachea");
318 cropFilter->SetInput(trachea_tmp);
319 cropFilter->Update(); // Needed
320 typedef itk::CastImageFilter<InternalImageType, MaskImageType> CastImageFilterType;
321 typename CastImageFilterType::Pointer caster= CastImageFilterType::New();
322 caster->SetInput(cropFilter->GetOutput());
324 trachea = caster->GetOutput();
325 StopCurrentStep<MaskImageType>(trachea);
328 //--------------------------------------------------------------------
329 //--------------------------------------------------------------------
330 StartNewStepOrStop("Croping lung");
331 typename CropFilterType::Pointer cropFilter2 = CropFilterType::New(); // Needed to reset pipeline
332 cropFilter2->SetInput(working_image);
333 cropFilter2->Update();
334 working_image = cropFilter2->GetOutput();
335 StopCurrentStep<InternalImageType>(working_image);
337 //--------------------------------------------------------------------
338 //--------------------------------------------------------------------
339 StartNewStepOrStop("Separate Left/Right lungs");
341 working_image = Labelize<InternalImageType>(working_image,
342 GetBackgroundValue(),
344 GetMinimalComponentSize());
347 typedef itk::StatisticsImageFilter<InternalImageType> StatisticsImageFilterType;
348 typename StatisticsImageFilterType::Pointer statisticsImageFilter=StatisticsImageFilterType::New();
349 statisticsImageFilter->SetInput(working_image);
350 statisticsImageFilter->Update();
351 unsigned int initialNumberOfLabels = statisticsImageFilter->GetMaximum();
352 working_image = statisticsImageFilter->GetOutput();
354 // Decompose the first label
355 static const unsigned int Dim = ImageType::ImageDimension;
356 if (initialNumberOfLabels<2) {
357 // Structuring element radius
358 typename ImageType::SizeType radius;
359 for (unsigned int i=0;i<Dim;i++) radius[i]=1;
360 typedef clitk::DecomposeAndReconstructImageFilter<InternalImageType,InternalImageType> DecomposeAndReconstructFilterType;
361 typename DecomposeAndReconstructFilterType::Pointer decomposeAndReconstructFilter=DecomposeAndReconstructFilterType::New();
362 decomposeAndReconstructFilter->SetInput(working_image);
363 decomposeAndReconstructFilter->SetVerbose(false);
364 decomposeAndReconstructFilter->SetRadius(radius);
365 decomposeAndReconstructFilter->SetMaximumNumberOfLabels(2);
366 decomposeAndReconstructFilter->SetMinimumObjectSize(this->GetMinimalComponentSize());
367 decomposeAndReconstructFilter->SetMinimumNumberOfIterations(1);
368 decomposeAndReconstructFilter->SetBackgroundValue(this->GetBackgroundValue());
369 decomposeAndReconstructFilter->SetForegroundValue(this->GetForegroundValue());
370 decomposeAndReconstructFilter->SetFullyConnected(true);
371 decomposeAndReconstructFilter->SetNumberOfNewLabels(1);
372 decomposeAndReconstructFilter->Update();
373 working_image = decomposeAndReconstructFilter->GetOutput();
376 // Retain labels ('1' is largset lung, so right. '2' is left)
377 typedef itk::ThresholdImageFilter<InternalImageType> ThresholdImageFilterType;
378 typename ThresholdImageFilterType::Pointer thresholdFilter = ThresholdImageFilterType::New();
379 thresholdFilter->SetInput(working_image);
380 thresholdFilter->ThresholdAbove(2);
381 thresholdFilter->SetOutsideValue(this->GetBackgroundValue());
382 thresholdFilter->Update();
383 working_image = thresholdFilter->GetOutput();
384 StopCurrentStep<InternalImageType> (working_image);
387 StartNewStepOrStop("Final cast");
388 typedef itk::CastImageFilter<InternalImageType, MaskImageType> CastImageFilterType;
389 typename CastImageFilterType::Pointer caster= CastImageFilterType::New();
390 caster->SetInput(working_image);
392 output = caster->GetOutput();
394 // Update output info
395 this->GetOutput(0)->SetRegions(output->GetLargestPossibleRegion());
397 // Try to extract bifurcation in the trachea (bronchi)
398 // STILL EXPERIMENTAL
399 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
401 if (GetFindBronchialBifurcations()) {
402 StartNewStepOrStop("Find bronchial bifurcations");
403 // Step 1 : extract skeleton
404 // Define the thinning filter
405 typedef itk::BinaryThinningImageFilter3D<MaskImageType, MaskImageType> ThinningFilterType;
406 typename ThinningFilterType::Pointer thinningFilter = ThinningFilterType::New();
407 thinningFilter->SetInput(trachea);
408 thinningFilter->Update();
409 typename MaskImageType::Pointer skeleton = thinningFilter->GetOutput();
410 writeImage<MaskImageType>(skeleton, "skeleton.mhd");
415 // Step 2.1 : find first point for tracking
416 typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> IteratorType;
417 IteratorType it(skeleton, skeleton->GetLargestPossibleRegion());
418 it.GoToReverseBegin();
419 while ((!it.IsAtEnd()) && (it.Get() == GetBackgroundValue())) {
423 this->SetLastError("ERROR: first point in the skeleton not found ! Abort");
426 DD(skeleton->GetLargestPossibleRegion().GetIndex());
427 typename MaskImageType::IndexType index = it.GetIndex();
430 // Step 2.2 : initialize neighborhooditerator
431 typedef itk::NeighborhoodIterator<MaskImageType> NeighborhoodIteratorType;
432 typename NeighborhoodIteratorType::SizeType radius;
434 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
438 // Find first label number (must be different from BG and FG)
439 typename MaskImageType::PixelType label = GetForegroundValue()+1;
440 while ((label == GetBackgroundValue()) || (label == GetForegroundValue())) { label++; }
443 // Track from the first point
444 std::vector<BifurcationType> listOfBifurcations;
445 TrackFromThisIndex(listOfBifurcations, skeleton, index, label);
447 DD(listOfBifurcations.size());
448 writeImage<MaskImageType>(skeleton, "skeleton2.mhd");
450 for(unsigned int i=0; i<listOfBifurcations.size(); i++) {
451 typename MaskImageType::PointType p;
452 skeleton->TransformIndexToPhysicalPoint(listOfBifurcations[i].index, p);
459 //--------------------------------------------------------------------
462 //--------------------------------------------------------------------
463 template <class ImageType, class MaskImageType>
465 clitk::ExtractLungFilter<ImageType, MaskImageType>::
468 // Do not put some "startnewstep" here, because the object if
469 // modified and the filter's pipeline it do two times. But it is
470 // required to quit if MustStop was set before.
471 if (GetMustStop()) return;
473 // If everything goes well, set the output
474 this->GraftOutput(output); // not SetNthOutput
476 //--------------------------------------------------------------------
479 //--------------------------------------------------------------------
480 template <class ImageType, class MaskImageType>
482 clitk::ExtractLungFilter<ImageType, MaskImageType>::
483 TrackFromThisIndex(std::vector<BifurcationType> & listOfBifurcations,
484 MaskImagePointer skeleton,
485 MaskImageIndexType index,
486 MaskImagePixelType label)
488 DD("TrackFromThisIndex");
491 // Create NeighborhoodIterator
492 typedef itk::NeighborhoodIterator<MaskImageType> NeighborhoodIteratorType;
493 typename NeighborhoodIteratorType::SizeType radius;
495 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
498 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
501 nit.SetLocation(index);
502 // DD((int)nit.GetCenterPixel());
503 nit.SetCenterPixel(label);
504 listOfTrackedPoint.clear();
505 for(unsigned int i=0; i<nit.Size(); i++) {
506 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
507 // DD(nit.GetIndex(i));
508 if (nit.GetPixel(i) == GetForegroundValue()) { // if this is foreground, we continue the tracking
509 // DD(nit.GetIndex(i));
510 listOfTrackedPoint.push_back(nit.GetIndex(i));
514 // DD(listOfTrackedPoint.size());
515 if (listOfTrackedPoint.size() == 1) {
516 index = listOfTrackedPoint[0];
519 if (listOfTrackedPoint.size() == 2) {
520 BifurcationType bif(index, label, label+1, label+2);
521 listOfBifurcations.push_back(bif);
522 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1);
523 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2);
526 if (listOfTrackedPoint.size() > 2) {
527 std::cerr << "too much bifurcation points ... ?" << std::endl;
530 // Else this it the end of the tracking
536 //--------------------------------------------------------------------
539 //--------------------------------------------------------------------
540 template <class ImageType, class MaskImageType>
542 clitk::ExtractLungFilter<ImageType, MaskImageType>::
543 SearchForTracheaSeed(int skip)
545 if (m_Seeds.size() == 0) { // try to find seed (if not zero, it is given by user)
546 // Restart the search until a seed is found, skipping more and more slices
549 // Search seed (parameters = UpperThresholdForTrachea)
550 static const unsigned int Dim = ImageType::ImageDimension;
551 typename InternalImageType::RegionType sliceRegion = working_input->GetLargestPossibleRegion();
552 typename InternalImageType::SizeType sliceRegionSize = sliceRegion.GetSize();
553 typename InternalImageType::IndexType sliceRegionIndex = sliceRegion.GetIndex();
554 sliceRegionIndex[Dim-1]=sliceRegionSize[Dim-1]-skip-5;
555 sliceRegionSize[Dim-1]=5;
556 sliceRegion.SetSize(sliceRegionSize);
557 sliceRegion.SetIndex(sliceRegionIndex);
558 typedef itk::ImageRegionConstIterator<ImageType> IteratorType;
559 IteratorType it(working_input, sliceRegion);
561 while (!it.IsAtEnd()) {
562 if(it.Get() < GetUpperThresholdForTrachea() ) {
563 AddSeed(it.GetIndex());
564 // DD(it.GetIndex());
569 // if we do not found : restart
570 stop = (m_Seeds.size() != 0);
572 if (GetVerboseStep()) {
573 std::cout << "\t No seed found this time. I skip some slices and restart." << std::endl;
575 if (skip > 0.5 * working_input->GetLargestPossibleRegion().GetSize()[2]) {
576 // we want to skip more than a half of the image, it is probably a bug
577 std::cerr << "Number of slices to skip to find trachea to high = " << skip << std::endl;
584 return (m_Seeds.size() != 0);
586 //--------------------------------------------------------------------
589 //--------------------------------------------------------------------
590 template <class ImageType, class MaskImageType>
592 clitk::ExtractLungFilter<ImageType, MaskImageType>::
593 TracheaRegionGrowing()
595 // Explosion controlled region growing
596 typedef clitk::ExplosionControlledThresholdConnectedImageFilter<ImageType, InternalImageType> ImageFilterType;
597 typename ImageFilterType::Pointer f= ImageFilterType::New();
598 f->SetInput(working_input);
599 f->SetVerbose(false);
601 f->SetUpper(GetUpperThresholdForTrachea());
602 f->SetMinimumLowerThreshold(-2000);
603 f->SetMaximumUpperThreshold(0);
604 f->SetAdaptLowerBorder(false);
605 f->SetAdaptUpperBorder(true);
606 f->SetMinimumSize(5000);
607 f->SetReplaceValue(1);
608 f->SetMultiplier(GetMultiplierForTrachea());
609 f->SetThresholdStepSize(GetThresholdStepSizeForTrachea());
610 f->SetMinimumThresholdStepSize(1);
611 for(unsigned int i=0; i<m_Seeds.size();i++) {
612 f->AddSeed(m_Seeds[i]);
617 // take first (main) connected component
618 writeImage<InternalImageType>(f->GetOutput(), "t1.mhd");
619 trachea_tmp = Labelize<InternalImageType>(f->GetOutput(),
620 GetBackgroundValue(),
622 GetMinimalComponentSize());
623 writeImage<InternalImageType>(trachea_tmp, "t2.mhd");
624 trachea_tmp = KeepLabels<InternalImageType>(trachea_tmp,
625 GetBackgroundValue(),
626 GetForegroundValue(),
628 writeImage<InternalImageType>(trachea_tmp, "t3.mhd");
630 //--------------------------------------------------------------------
633 //--------------------------------------------------------------------
634 template <class ImageType, class MaskImageType>
636 clitk::ExtractLungFilter<ImageType, MaskImageType>::
637 ComputeTracheaVolume()
639 typedef itk::ImageRegionConstIterator<InternalImageType> IteratorType;
640 IteratorType iter(trachea_tmp, trachea_tmp->GetLargestPossibleRegion());
643 while (!iter.IsAtEnd()) {
644 if (iter.Get() == this->GetForegroundValue()) volume++;
648 double voxelsize = trachea_tmp->GetSpacing()[0]*trachea_tmp->GetSpacing()[1]*trachea_tmp->GetSpacing()[2];
649 return volume*voxelsize;
651 //--------------------------------------------------------------------
654 //--------------------------------------------------------------------
655 template <class ImageType, class MaskImageType>
657 clitk::ExtractLungFilter<ImageType, MaskImageType>::
660 // Search for seed among n slices, skip some slices before starting
661 // if not found -> skip more and restart
663 // when seed found : perform region growing
664 // compute trachea volume
665 // if volume not plausible -> skip more slices and restart
669 int skip = GetNumberOfSlicesToSkipBeforeSearchingSeed();
671 stop = SearchForTracheaSeed(skip);
673 TracheaRegionGrowing();
674 volume = ComputeTracheaVolume(); // assume mm3
675 if ((volume > 10000) && (volume < 55000 )) { // it is ok
676 // Typical volume 22.59 cm 3 (± 7.69 cm 3 ) [ Leader 2004 ]
677 if (GetVerboseStep()) {
678 std::cout << "\t Found trachea with volume " << volume << " cc." << std::endl;
683 if (GetVerboseStep()) {
684 std::cout << "\t The volume of the trachea (" << volume
685 << " cc) seems not correct. I skip some slices (" << skip << ") and restart to find seeds."
690 // empty the list of seed
698 StopCurrentStep<InternalImageType>(trachea_tmp);
700 else { // Trachea not found
701 this->SetWarning("* WARNING * No seed found for trachea.");
705 //--------------------------------------------------------------------
707 #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX