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"
27 #include "clitkExtractSliceFilter.h"
30 #include "itkBinaryThresholdImageFilter.h"
31 #include "itkConnectedComponentImageFilter.h"
32 #include "itkRelabelComponentImageFilter.h"
33 #include "itkOtsuThresholdImageFilter.h"
34 #include "itkBinaryThinningImageFilter3D.h"
35 #include "itkImageIteratorWithIndex.h"
37 //--------------------------------------------------------------------
38 template <class ImageType, class MaskImageType>
39 clitk::ExtractLungFilter<ImageType, MaskImageType>::
42 clitk::FilterWithAnatomicalFeatureDatabaseManagement(),
43 itk::ImageToImageFilter<ImageType, MaskImageType>()
46 m_MaxSeedNumber = 500;
48 // Default global options
49 this->SetNumberOfRequiredInputs(2);
50 SetPatientMaskBackgroundValue(0);
51 SetBackgroundValue(0); // Must be zero
52 SetForegroundValue(1);
53 SetMinimalComponentSize(100);
55 // Step 1 default values
56 SetUpperThreshold(-300);
57 SetLowerThreshold(-1000);
58 UseLowerThresholdOff();
59 LabelParamType * p1 = new LabelParamType;
62 p1->AddLabelToRemove(2);
63 SetLabelizeParameters1(p1);
65 // Step 2 default values
66 SetUpperThresholdForTrachea(-900);
67 SetMultiplierForTrachea(5);
68 SetThresholdStepSizeForTrachea(64);
69 SetNumberOfSlicesToSkipBeforeSearchingSeed(0);
71 // Step 3 default values
72 SetNumberOfHistogramBins(500);
73 LabelParamType * p2 = new LabelParamType;
76 // p->AddLabelToRemove(2); // No label to remove by default
77 SetLabelizeParameters2(p2);
79 // Step 4 default values
80 SetRadiusForTrachea(1);
81 LabelParamType * p3 = new LabelParamType;
85 SetLabelizeParameters3(p3);
87 // Step 5 : find bronchial bifurcations
88 FindBronchialBifurcationsOn();
90 //--------------------------------------------------------------------
93 //--------------------------------------------------------------------
94 template <class ImageType, class MaskImageType>
96 clitk::ExtractLungFilter<ImageType, MaskImageType>::
97 SetInput(const ImageType * image)
99 this->SetNthInput(0, const_cast<ImageType *>(image));
101 //--------------------------------------------------------------------
104 //--------------------------------------------------------------------
105 template <class ImageType, class MaskImageType>
107 clitk::ExtractLungFilter<ImageType, MaskImageType>::
108 SetInputPatientMask(MaskImageType * image, MaskImagePixelType bg )
110 this->SetNthInput(1, const_cast<MaskImageType *>(image));
111 SetPatientMaskBackgroundValue(bg);
113 //--------------------------------------------------------------------
116 //--------------------------------------------------------------------
117 template <class ImageType, class MaskImageType>
119 clitk::ExtractLungFilter<ImageType, MaskImageType>::
120 AddSeed(InternalIndexType s)
122 m_Seeds.push_back(s);
124 //--------------------------------------------------------------------
127 //--------------------------------------------------------------------
128 template <class ImageType, class MaskImageType>
129 template<class ArgsInfoType>
131 clitk::ExtractLungFilter<ImageType, MaskImageType>::
132 SetArgsInfo(ArgsInfoType mArgsInfo)
134 SetVerboseOption_GGO(mArgsInfo);
135 SetVerboseStep_GGO(mArgsInfo);
136 SetWriteStep_GGO(mArgsInfo);
137 SetVerboseWarningOff_GGO(mArgsInfo);
139 SetUpperThreshold_GGO(mArgsInfo);
140 SetLowerThreshold_GGO(mArgsInfo);
141 SetNumberOfSlicesToSkipBeforeSearchingSeed_GGO(mArgsInfo);
142 SetLabelizeParameters1_GGO(mArgsInfo);
143 if (!mArgsInfo.remove1_given) {
144 GetLabelizeParameters1()->AddLabelToRemove(2);
145 if (GetVerboseOption()) VerboseOption("remove1", 2);
148 SetUpperThresholdForTrachea_GGO(mArgsInfo);
149 SetMultiplierForTrachea_GGO(mArgsInfo);
150 SetThresholdStepSizeForTrachea_GGO(mArgsInfo);
151 AddSeed_GGO(mArgsInfo);
153 SetMinimalComponentSize_GGO(mArgsInfo);
155 SetNumberOfHistogramBins_GGO(mArgsInfo);
156 SetLabelizeParameters2_GGO(mArgsInfo);
158 SetRadiusForTrachea_GGO(mArgsInfo);
159 SetLabelizeParameters3_GGO(mArgsInfo);
161 SetAFDBFilename_GGO(mArgsInfo);
163 //--------------------------------------------------------------------
166 //--------------------------------------------------------------------
167 template <class ImageType, class MaskImageType>
169 clitk::ExtractLungFilter<ImageType, MaskImageType>::
170 GenerateOutputInformation()
172 Superclass::GenerateOutputInformation();
173 //this->GetOutput(0)->SetRequestedRegion(this->GetOutput(0)->GetLargestPossibleRegion());
175 // Get input pointers
176 patient = dynamic_cast<const MaskImageType*>(itk::ProcessObject::GetInput(1));
177 input = dynamic_cast<const ImageType*>(itk::ProcessObject::GetInput(0));
180 if (!HaveSameSizeAndSpacing<ImageType, MaskImageType>(input, patient)) {
181 clitkExceptionMacro("the 'input' and 'patient' masks must have the same size & spacing.");
184 //--------------------------------------------------------------------
185 //--------------------------------------------------------------------
186 StartNewStep("Set background to initial image");
187 working_input = SetBackground<ImageType, MaskImageType>
188 (input, patient, GetPatientMaskBackgroundValue(), -1000);
189 StopCurrentStep<ImageType>(working_input);
191 //--------------------------------------------------------------------
192 //--------------------------------------------------------------------
193 StartNewStep("Remove Air");
195 if (m_UseLowerThreshold) {
196 if (m_LowerThreshold > m_UpperThreshold) {
197 clitkExceptionMacro("lower threshold cannot be greater than upper threshold.");
200 // Threshold to get air
201 typedef itk::BinaryThresholdImageFilter<ImageType, InternalImageType> InputBinarizeFilterType;
202 typename InputBinarizeFilterType::Pointer binarizeFilter=InputBinarizeFilterType::New();
203 binarizeFilter->SetInput(working_input);
204 if (m_UseLowerThreshold) binarizeFilter->SetLowerThreshold(m_LowerThreshold);
205 binarizeFilter->SetUpperThreshold(m_UpperThreshold);
206 binarizeFilter ->SetInsideValue(this->GetForegroundValue());
207 binarizeFilter ->SetOutsideValue(this->GetBackgroundValue());
208 binarizeFilter->Update();
209 working_image = binarizeFilter->GetOutput();
211 // Labelize and keep right labels
212 working_image = Labelize<InternalImageType>(working_image, GetBackgroundValue(), true, GetMinimalComponentSize());
214 working_image = RemoveLabels<InternalImageType>
215 (working_image, GetBackgroundValue(), GetLabelizeParameters1()->GetLabelsToRemove());
217 typename InternalImageType::Pointer air = KeepLabels<InternalImageType>
219 GetBackgroundValue(),
220 GetForegroundValue(),
221 GetLabelizeParameters1()->GetFirstKeep(),
222 GetLabelizeParameters1()->GetLastKeep(),
223 GetLabelizeParameters1()->GetUseLastKeep());
226 working_input = SetBackground<ImageType, InternalImageType>
227 (working_input, air, this->GetForegroundValue(), this->GetBackgroundValue());
230 StopCurrentStep<ImageType>(working_input);
232 //--------------------------------------------------------------------
233 //--------------------------------------------------------------------
234 StartNewStep("Search for the trachea");
237 //--------------------------------------------------------------------
238 //--------------------------------------------------------------------
239 StartNewStep("Extract the lung with Otsu filter");
240 // Automated Otsu thresholding and relabeling
241 typedef itk::OtsuThresholdImageFilter<ImageType,InternalImageType> OtsuThresholdImageFilterType;
242 typename OtsuThresholdImageFilterType::Pointer otsuFilter=OtsuThresholdImageFilterType::New();
243 otsuFilter->SetInput(working_input);
244 otsuFilter->SetNumberOfHistogramBins(GetNumberOfHistogramBins());
245 otsuFilter->SetInsideValue(this->GetForegroundValue());
246 otsuFilter->SetOutsideValue(this->GetBackgroundValue());
247 otsuFilter->Update();
248 working_image = otsuFilter->GetOutput();
251 StopCurrentStep<InternalImageType>(working_image);
253 //--------------------------------------------------------------------
254 //--------------------------------------------------------------------
255 StartNewStep("Select labels");
257 working_image = LabelizeAndSelectLabels<InternalImageType>
259 GetBackgroundValue(),
260 GetForegroundValue(),
262 GetMinimalComponentSize(),
263 GetLabelizeParameters2());
266 StopCurrentStep<InternalImageType>(working_image);
268 //--------------------------------------------------------------------
269 //--------------------------------------------------------------------
270 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
271 StartNewStep("Remove the trachea");
273 working_image = SetBackground<InternalImageType, InternalImageType>
274 (working_image, trachea_tmp, 1, -1);
276 // Dilate the trachea
277 static const unsigned int Dim = ImageType::ImageDimension;
278 typedef itk::BinaryBallStructuringElement<InternalPixelType, Dim> KernelType;
279 KernelType structuringElement;
280 structuringElement.SetRadius(GetRadiusForTrachea());
281 structuringElement.CreateStructuringElement();
282 typedef clitk::ConditionalBinaryDilateImageFilter<InternalImageType, InternalImageType, KernelType> ConditionalBinaryDilateImageFilterType;
283 typename ConditionalBinaryDilateImageFilterType::Pointer dilateFilter = ConditionalBinaryDilateImageFilterType::New();
284 dilateFilter->SetBoundaryToForeground(false);
285 dilateFilter->SetKernel(structuringElement);
286 dilateFilter->SetBackgroundValue (1);
287 dilateFilter->SetForegroundValue (-1);
288 dilateFilter->SetInput (working_image);
289 dilateFilter->Update();
290 working_image = dilateFilter->GetOutput();
292 // Set trachea with dilatation
293 trachea_tmp = SetBackground<InternalImageType, InternalImageType>
294 (trachea_tmp, working_image, -1, this->GetForegroundValue());
296 // Remove the trachea
297 working_image = SetBackground<InternalImageType, InternalImageType>
298 (working_image, working_image, -1, this->GetBackgroundValue());
301 working_image = LabelizeAndSelectLabels<InternalImageType>
303 GetBackgroundValue(),
304 GetForegroundValue(),
306 GetMinimalComponentSize(),
307 GetLabelizeParameters3());
310 StopCurrentStep<InternalImageType>(working_image);
313 //--------------------------------------------------------------------
314 //--------------------------------------------------------------------
315 typedef clitk::AutoCropFilter<InternalImageType> CropFilterType;
316 typename CropFilterType::Pointer cropFilter = CropFilterType::New();
317 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
318 StartNewStep("Croping trachea");
319 cropFilter->SetInput(trachea_tmp);
320 cropFilter->Update(); // Needed
321 typedef itk::CastImageFilter<InternalImageType, MaskImageType> CastImageFilterType;
322 typename CastImageFilterType::Pointer caster= CastImageFilterType::New();
323 caster->SetInput(cropFilter->GetOutput());
325 trachea = caster->GetOutput();
326 StopCurrentStep<MaskImageType>(trachea);
329 //--------------------------------------------------------------------
330 //--------------------------------------------------------------------
331 StartNewStep("Croping lung");
332 typename CropFilterType::Pointer cropFilter2 = CropFilterType::New(); // Needed to reset pipeline
333 cropFilter2->SetInput(working_image);
334 cropFilter2->Update();
335 working_image = cropFilter2->GetOutput();
336 StopCurrentStep<InternalImageType>(working_image);
338 //--------------------------------------------------------------------
339 //--------------------------------------------------------------------
340 StartNewStep("Separate Left/Right lungs");
342 working_image = Labelize<InternalImageType>(working_image,
343 GetBackgroundValue(),
345 GetMinimalComponentSize());
348 typedef itk::StatisticsImageFilter<InternalImageType> StatisticsImageFilterType;
349 typename StatisticsImageFilterType::Pointer statisticsImageFilter=StatisticsImageFilterType::New();
350 statisticsImageFilter->SetInput(working_image);
351 statisticsImageFilter->Update();
352 unsigned int initialNumberOfLabels = statisticsImageFilter->GetMaximum();
353 working_image = statisticsImageFilter->GetOutput();
355 // Decompose the first label
356 static const unsigned int Dim = ImageType::ImageDimension;
357 if (initialNumberOfLabels<2) {
358 // Structuring element radius
359 typename ImageType::SizeType radius;
360 for (unsigned int i=0;i<Dim;i++) radius[i]=1;
361 typedef clitk::DecomposeAndReconstructImageFilter<InternalImageType,InternalImageType> DecomposeAndReconstructFilterType;
362 typename DecomposeAndReconstructFilterType::Pointer decomposeAndReconstructFilter=DecomposeAndReconstructFilterType::New();
363 decomposeAndReconstructFilter->SetInput(working_image);
364 decomposeAndReconstructFilter->SetVerbose(false);
365 decomposeAndReconstructFilter->SetRadius(radius);
366 decomposeAndReconstructFilter->SetMaximumNumberOfLabels(2);
367 decomposeAndReconstructFilter->SetMinimumObjectSize(this->GetMinimalComponentSize());
368 decomposeAndReconstructFilter->SetMinimumNumberOfIterations(1);
369 decomposeAndReconstructFilter->SetBackgroundValue(this->GetBackgroundValue());
370 decomposeAndReconstructFilter->SetForegroundValue(this->GetForegroundValue());
371 decomposeAndReconstructFilter->SetFullyConnected(true);
372 decomposeAndReconstructFilter->SetNumberOfNewLabels(1);
373 decomposeAndReconstructFilter->Update();
374 working_image = decomposeAndReconstructFilter->GetOutput();
377 // Retain labels ('1' is largset lung, so right. '2' is left)
378 typedef itk::ThresholdImageFilter<InternalImageType> ThresholdImageFilterType;
379 typename ThresholdImageFilterType::Pointer thresholdFilter = ThresholdImageFilterType::New();
380 thresholdFilter->SetInput(working_image);
381 thresholdFilter->ThresholdAbove(2);
382 thresholdFilter->SetOutsideValue(this->GetBackgroundValue());
383 thresholdFilter->Update();
384 working_image = thresholdFilter->GetOutput();
385 StopCurrentStep<InternalImageType> (working_image);
388 StartNewStep("Cast the lung mask");
389 typedef itk::CastImageFilter<InternalImageType, MaskImageType> CastImageFilterType;
390 typename CastImageFilterType::Pointer caster= CastImageFilterType::New();
391 caster->SetInput(working_image);
393 output = caster->GetOutput();
395 // Update output info
396 this->GetOutput(0)->SetRegions(output->GetLargestPossibleRegion());
398 // Try to extract bifurcation in the trachea (bronchi)
399 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
401 if (GetFindBronchialBifurcations()) {
402 StartNewStep("Find bronchial bifurcations");
403 // Step 1 : extract skeleton
404 typedef itk::BinaryThinningImageFilter3D<MaskImageType, MaskImageType> ThinningFilterType;
405 typename ThinningFilterType::Pointer thinningFilter = ThinningFilterType::New();
406 thinningFilter->SetInput(trachea);
407 thinningFilter->Update();
408 typename MaskImageType::Pointer skeleton = thinningFilter->GetOutput();
410 // Step 2.1 : find first point for tracking
411 typedef itk::ImageRegionConstIteratorWithIndex<MaskImageType> IteratorType;
412 IteratorType it(skeleton, skeleton->GetLargestPossibleRegion());
413 it.GoToReverseBegin();
414 while ((!it.IsAtEnd()) && (it.Get() == GetBackgroundValue())) {
418 clitkExceptionMacro("first point in the trachea skeleton not found.");
420 typename MaskImageType::IndexType index = it.GetIndex();
422 // Step 2.2 : initialize neighborhooditerator
423 typedef itk::NeighborhoodIterator<MaskImageType> NeighborhoodIteratorType;
424 typename NeighborhoodIteratorType::SizeType radius;
426 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
428 // Find first label number (must be different from BG and FG)
429 typename MaskImageType::PixelType label = GetForegroundValue()+1;
430 while ((label == GetBackgroundValue()) || (label == GetForegroundValue())) { label++; }
432 // Track from the first point
433 std::vector<BifurcationType> listOfBifurcations;
434 m_SkeletonTree.set_head(index);
435 TrackFromThisIndex(listOfBifurcations, skeleton, index, label, m_SkeletonTree.begin());
437 DD(listOfBifurcations.size());
438 DD(m_SkeletonTree.size());
440 for(unsigned int i=0; i<listOfBifurcations.size(); i++) {
441 skeleton->TransformIndexToPhysicalPoint(listOfBifurcations[i].index,
442 listOfBifurcations[i].point);
445 // Search for the first slice that separate the bronchus (carena)
446 typedef clitk::ExtractSliceFilter<MaskImageType> ExtractSliceFilterType;
447 typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
448 extractSliceFilter->SetInput(trachea);
449 extractSliceFilter->SetDirection(2);
450 extractSliceFilter->Update();
451 typedef typename ExtractSliceFilterType::SliceType SliceType;
452 std::vector<typename SliceType::Pointer> mInputSlices;
453 extractSliceFilter->GetOutputSlices(mInputSlices);
456 DD(listOfBifurcations[0].index);
457 DD(listOfBifurcations[1].index);
458 int slice_index = listOfBifurcations[0].index[2]; // first slice from carena in skeleton
460 TreeIterator firstIter = m_SkeletonTree.child(listOfBifurcations[1].treeIter, 0);
461 TreeIterator secondIter = m_SkeletonTree.child(listOfBifurcations[1].treeIter, 1);
462 typename SliceType::IndexType in1;
463 typename SliceType::IndexType in2;
467 // Labelize the current slice
468 typename SliceType::Pointer temp = Labelize<SliceType>(mInputSlices[slice_index],
469 GetBackgroundValue(),
471 GetMinimalComponentSize());
472 // Check the value of the two skeleton points;
473 in1[0] = (*firstIter)[0];
474 in1[1] = (*firstIter)[1];
475 typename SliceType::PixelType v1 = temp->GetPixel(in1);
478 in2[0] = (*secondIter)[0];
479 in2[1] = (*secondIter)[1];
480 typename SliceType::PixelType v2 = temp->GetPixel(in2);
484 // TODO IF NOT FOUND ????
496 MaskImageIndexType carena_index;
497 carena_index[0] = lrint(in2[0] + in1[0])/2.0;
498 carena_index[1] = lrint(in2[1] + in1[1])/2.0;
499 carena_index[2] = slice_index;
500 MaskImagePointType carena_position;
502 skeleton->TransformIndexToPhysicalPoint(carena_index,
506 // Set and save Carina position
507 StartNewStep("Save carina position");
508 // Try to load the DB
511 } catch (clitk::ExceptionObject e) {
512 // Do nothing if not found, it will be used anyway to write the result
514 GetAFDB()->SetPoint3D("Carena", carena_position);
518 //--------------------------------------------------------------------
521 //--------------------------------------------------------------------
522 template <class ImageType, class MaskImageType>
524 clitk::ExtractLungFilter<ImageType, MaskImageType>::
527 // If everything goes well, set the output
528 this->GraftOutput(output); // not SetNthOutput
530 //--------------------------------------------------------------------
533 //--------------------------------------------------------------------
534 template <class ImageType, class MaskImageType>
536 clitk::ExtractLungFilter<ImageType, MaskImageType>::
537 TrackFromThisIndex(std::vector<BifurcationType> & listOfBifurcations,
538 MaskImagePointer skeleton,
539 MaskImageIndexType index,
540 MaskImagePixelType label,
541 TreeIterator currentNode)
543 // Create NeighborhoodIterator
544 typedef itk::NeighborhoodIterator<MaskImageType> NeighborhoodIteratorType;
545 typename NeighborhoodIteratorType::SizeType radius;
547 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
550 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
553 nit.SetLocation(index);
554 nit.SetCenterPixel(label);
555 listOfTrackedPoint.clear();
556 for(unsigned int i=0; i<nit.Size(); i++) {
557 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
558 if (nit.GetPixel(i) == GetForegroundValue()) { // if this is foreground, we continue the tracking
559 listOfTrackedPoint.push_back(nit.GetIndex(i));
563 if (listOfTrackedPoint.size() == 1) {
564 // Add this point to the current path
565 currentNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]);
566 index = listOfTrackedPoint[0];
569 if (listOfTrackedPoint.size() == 2) {
570 // m_SkeletonTree->Add(listOfTrackedPoint[0], index); // the parent is 'index'
571 // m_SkeletonTree->Add(listOfTrackedPoint[1], index); // the parent is 'index'
572 BifurcationType bif(index, label, label+1, label+2);
573 bif.treeIter = currentNode;
574 listOfBifurcations.push_back(bif);
575 TreeIterator firstNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]);
576 TreeIterator secondNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[1]);
577 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1, firstNode);
578 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2, secondNode);
581 if (listOfTrackedPoint.size() > 2) {
582 clitkExceptionMacro("error while tracking trachea bifurcation. Too much bifurcation points ... ?");
584 // Else this it the end of the tracking
590 //--------------------------------------------------------------------
593 //--------------------------------------------------------------------
594 template <class ImageType, class MaskImageType>
596 clitk::ExtractLungFilter<ImageType, MaskImageType>::
597 SearchForTracheaSeed(int skip)
599 if (m_Seeds.size() == 0) { // try to find seed (if not zero, it is given by user)
600 // Restart the search until a seed is found, skipping more and more slices
603 // Search seed (parameters = UpperThresholdForTrachea)
604 static const unsigned int Dim = ImageType::ImageDimension;
605 typename InternalImageType::RegionType sliceRegion = working_input->GetLargestPossibleRegion();
606 typename InternalImageType::SizeType sliceRegionSize = sliceRegion.GetSize();
607 typename InternalImageType::IndexType sliceRegionIndex = sliceRegion.GetIndex();
608 sliceRegionIndex[Dim-1]=sliceRegionSize[Dim-1]-skip-5;
609 sliceRegionSize[Dim-1]=5;
610 sliceRegion.SetSize(sliceRegionSize);
611 sliceRegion.SetIndex(sliceRegionIndex);
612 typedef itk::ImageRegionConstIterator<ImageType> IteratorType;
613 IteratorType it(working_input, sliceRegion);
615 while (!it.IsAtEnd()) {
616 if(it.Get() < GetUpperThresholdForTrachea() ) {
617 AddSeed(it.GetIndex());
618 // DD(it.GetIndex());
623 // if we do not found : restart
624 stop = (m_Seeds.size() != 0);
626 if (GetVerboseStep()) {
627 std::cout << "\t No seed found this time. I skip some slices and restart." << std::endl;
629 if (skip > 0.5 * working_input->GetLargestPossibleRegion().GetSize()[2]) {
630 // we want to skip more than a half of the image, it is probably a bug
631 std::cerr << "Number of slices to skip to find trachea to high = " << skip << std::endl;
638 return (m_Seeds.size() != 0);
640 //--------------------------------------------------------------------
643 //--------------------------------------------------------------------
644 template <class ImageType, class MaskImageType>
646 clitk::ExtractLungFilter<ImageType, MaskImageType>::
647 TracheaRegionGrowing()
649 // Explosion controlled region growing
650 typedef clitk::ExplosionControlledThresholdConnectedImageFilter<ImageType, InternalImageType> ImageFilterType;
651 typename ImageFilterType::Pointer f= ImageFilterType::New();
652 f->SetInput(working_input);
653 f->SetVerbose(false);
655 f->SetUpper(GetUpperThresholdForTrachea());
656 f->SetMinimumLowerThreshold(-2000);
657 f->SetMaximumUpperThreshold(0);
658 f->SetAdaptLowerBorder(false);
659 f->SetAdaptUpperBorder(true);
660 f->SetMinimumSize(5000);
661 f->SetReplaceValue(1);
662 f->SetMultiplier(GetMultiplierForTrachea());
663 f->SetThresholdStepSize(GetThresholdStepSizeForTrachea());
664 f->SetMinimumThresholdStepSize(1);
665 for(unsigned int i=0; i<m_Seeds.size();i++) {
666 f->AddSeed(m_Seeds[i]);
671 // take first (main) connected component
672 trachea_tmp = Labelize<InternalImageType>(f->GetOutput(),
673 GetBackgroundValue(),
675 GetMinimalComponentSize());
676 trachea_tmp = KeepLabels<InternalImageType>(trachea_tmp,
677 GetBackgroundValue(),
678 GetForegroundValue(),
681 //--------------------------------------------------------------------
684 //--------------------------------------------------------------------
685 template <class ImageType, class MaskImageType>
687 clitk::ExtractLungFilter<ImageType, MaskImageType>::
688 ComputeTracheaVolume()
690 typedef itk::ImageRegionConstIterator<InternalImageType> IteratorType;
691 IteratorType iter(trachea_tmp, trachea_tmp->GetLargestPossibleRegion());
694 while (!iter.IsAtEnd()) {
695 if (iter.Get() == this->GetForegroundValue()) volume++;
699 double voxelsize = trachea_tmp->GetSpacing()[0]*trachea_tmp->GetSpacing()[1]*trachea_tmp->GetSpacing()[2];
700 return volume*voxelsize;
702 //--------------------------------------------------------------------
705 //--------------------------------------------------------------------
706 template <class ImageType, class MaskImageType>
708 clitk::ExtractLungFilter<ImageType, MaskImageType>::
711 // Search for seed among n slices, skip some slices before starting
712 // if not found -> skip more and restart
714 // when seed found : perform region growing
715 // compute trachea volume
716 // if volume not plausible -> skip more slices and restart
720 int skip = GetNumberOfSlicesToSkipBeforeSearchingSeed();
722 stop = SearchForTracheaSeed(skip);
724 TracheaRegionGrowing();
725 volume = ComputeTracheaVolume()/1000; // assume mm3, so divide by 1000 to get cc
726 if ((volume > 10) && (volume < 55 )) { // it is ok
727 // Typical volume 22.59 cm 3 (± 7.69 cm 3 ) [ Leader 2004 ]
728 if (GetVerboseStep()) {
729 std::cout << "\t Found trachea with volume " << volume << " cc." << std::endl;
734 if (GetVerboseStep()) {
735 std::cout << "\t The volume of the trachea (" << volume
736 << " cc) seems not correct. I skip some slices (" << skip << ") and restart to find seeds."
741 // empty the list of seed
749 StopCurrentStep<InternalImageType>(trachea_tmp);
751 else { // Trachea not found
752 this->SetWarning("* WARNING * No seed found for trachea.");
756 //--------------------------------------------------------------------
758 #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX