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"
34 //--------------------------------------------------------------------
35 template <class TInputImageType, class TMaskImageType>
36 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
39 itk::ImageToImageFilter<TInputImageType, TMaskImageType>()
41 // Default global options
42 this->SetNumberOfRequiredInputs(2);
43 SetPatientMaskBackgroundValue(0);
44 SetBackgroundValue(0); // Must be zero
45 SetForegroundValue(1);
47 // Step 1 default values
48 SetUpperThreshold(-300);
49 SetLowerThreshold(-1000);
50 UseLowerThresholdOff();
51 LabelParamType * p1 = new LabelParamType;
54 p1->AddLabelToRemove(2);
55 SetLabelizeParameters1(p1);
57 // Step 2 default values
58 SetUpperThresholdForTrachea(-900);
59 SetMultiplierForTrachea(5);
60 SetThresholdStepSizeForTrachea(64);
62 // Step 3 default values
63 SetNumberOfHistogramBins(500);
64 LabelParamType * p2 = new LabelParamType;
67 // p->AddLabelToRemove(2); // No label to remove by default
68 SetLabelizeParameters2(p2);
70 // Step 4 default values
71 SetRadiusForTrachea(1);
72 LabelParamType * p3 = new LabelParamType;
76 SetLabelizeParameters3(p3);
78 //--------------------------------------------------------------------
81 //--------------------------------------------------------------------
82 template <class TInputImageType, class TMaskImageType>
84 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
85 SetInput(const TInputImageType * image)
87 this->SetNthInput(0, const_cast<TInputImageType *>(image));
89 //--------------------------------------------------------------------
92 //--------------------------------------------------------------------
93 template <class TInputImageType, class TMaskImageType>
95 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
96 SetInputPatientMask(TMaskImageType * image, MaskImagePixelType bg )
98 this->SetNthInput(1, const_cast<TMaskImageType *>(image));
99 SetPatientMaskBackgroundValue(bg);
101 //--------------------------------------------------------------------
104 //--------------------------------------------------------------------
105 template <class TInputImageType, class TMaskImageType>
107 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
108 AddSeed(InternalIndexType s)
110 m_Seeds.push_back(s);
112 //--------------------------------------------------------------------
115 //--------------------------------------------------------------------
116 template <class TInputImageType, class TMaskImageType>
117 template<class ArgsInfoType>
119 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
120 SetArgsInfo(ArgsInfoType mArgsInfo)
122 SetVerboseOption_GGO(mArgsInfo);
123 SetVerboseStep_GGO(mArgsInfo);
124 SetWriteStep_GGO(mArgsInfo);
125 SetVerboseWarningOff_GGO(mArgsInfo);
127 SetUpperThreshold_GGO(mArgsInfo);
128 SetLowerThreshold_GGO(mArgsInfo);
129 SetLabelizeParameters1_GGO(mArgsInfo);
130 if (!mArgsInfo.remove1_given) {
131 GetLabelizeParameters1()->AddLabelToRemove(2);
132 if (GetVerboseOption()) VerboseOption("remove1", 2);
135 SetUpperThresholdForTrachea_GGO(mArgsInfo);
136 SetMultiplierForTrachea_GGO(mArgsInfo);
137 SetThresholdStepSizeForTrachea_GGO(mArgsInfo);
138 AddSeed_GGO(mArgsInfo);
140 SetNumberOfHistogramBins_GGO(mArgsInfo);
141 SetLabelizeParameters2_GGO(mArgsInfo);
143 SetRadiusForTrachea_GGO(mArgsInfo);
144 SetLabelizeParameters3_GGO(mArgsInfo);
146 //--------------------------------------------------------------------
149 //--------------------------------------------------------------------
150 template <class TInputImageType, class TMaskImageType>
152 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
153 GenerateOutputInformation()
155 input = dynamic_cast<const TInputImageType*>(itk::ProcessObject::GetInput(0));
156 Superclass::GenerateOutputInformation();
157 // MaskImagePointer output = this->GetOutput(0);
159 // Get input pointers
160 input = dynamic_cast<const TInputImageType*>(itk::ProcessObject::GetInput(0));
161 patient = dynamic_cast<const TMaskImageType*>(itk::ProcessObject::GetInput(1));
164 if (!HaveSameSizeAndSpacing<TInputImageType, TMaskImageType>(input, patient)) {
165 this->SetLastError("* ERROR * the images (input and patient mask) must have the same size & spacing");
169 //--------------------------------------------------------------------
170 //--------------------------------------------------------------------
171 StartNewStep("Set background to initial image");
172 working_input = SetBackground<TInputImageType, TMaskImageType>
173 (input, patient, GetPatientMaskBackgroundValue(), -1000);
174 StopCurrentStep<InputImageType>(working_input);
176 //--------------------------------------------------------------------
177 //--------------------------------------------------------------------
178 StartNewStep("Remove Air");
179 // Threshold to get air
180 typedef itk::BinaryThresholdImageFilter<InputImageType, InternalImageType> InputBinarizeFilterType;
181 typename InputBinarizeFilterType::Pointer binarizeFilter=InputBinarizeFilterType::New();
182 binarizeFilter->SetInput(working_input);
183 if (m_UseLowerThreshold) binarizeFilter->SetLowerThreshold(m_LowerThreshold);
184 binarizeFilter->SetUpperThreshold(m_UpperThreshold);
185 binarizeFilter ->SetInsideValue(this->GetForegroundValue());
186 binarizeFilter ->SetOutsideValue(this->GetBackgroundValue());
187 binarizeFilter->Update();
188 working_image = binarizeFilter->GetOutput();
190 // Labelize and keep right labels
191 working_image = Labelize<InternalImageType>(working_image, GetBackgroundValue(), true, GetMinimalComponentSize());
192 working_image = RemoveLabels<InternalImageType>
193 (working_image, GetBackgroundValue(), GetLabelizeParameters1()->GetLabelsToRemove());
194 typename InternalImageType::Pointer air = KeepLabels<InternalImageType>
196 GetBackgroundValue(),
197 GetForegroundValue(),
198 GetLabelizeParameters1()->GetFirstKeep(),
199 GetLabelizeParameters1()->GetLastKeep(),
200 GetLabelizeParameters1()->GetUseLastKeep());
203 working_input = SetBackground<TInputImageType, InternalImageType>
204 (working_input, air, this->GetForegroundValue(), this->GetBackgroundValue());
207 StopCurrentStep<InputImageType>(working_input);
209 //--------------------------------------------------------------------
210 //--------------------------------------------------------------------
211 StartNewStep("Find the trachea");
212 //DD(m_Seeds.size());
213 if (m_Seeds.size() == 0) { // try to find seed
214 // Search seed (parameters = UpperThresholdForTrachea)
215 static const unsigned int Dim = InputImageType::ImageDimension;
216 typename InternalImageType::RegionType sliceRegion = working_input->GetLargestPossibleRegion();
217 typename InternalImageType::SizeType sliceRegionSize = sliceRegion.GetSize();
218 typename InternalImageType::IndexType sliceRegionIndex = sliceRegion.GetIndex();
219 sliceRegionIndex[Dim-1]=sliceRegionSize[Dim-1]-5;
220 sliceRegionSize[Dim-1]=5;
221 sliceRegion.SetSize(sliceRegionSize);
222 sliceRegion.SetIndex(sliceRegionIndex);
223 //DD(GetUpperThresholdForTrachea());
225 typedef itk::ImageRegionConstIterator<InputImageType> IteratorType;
226 IteratorType it(working_input, sliceRegion);
228 while (!it.IsAtEnd()) {
229 if(it.Get() < GetUpperThresholdForTrachea() ) {
230 AddSeed(it.GetIndex());
231 // DD(it.GetIndex());
237 //DD(m_Seeds.size());
238 if (m_Seeds.size() != 0) {
239 // Explosion controlled region growing
240 typedef clitk::ExplosionControlledThresholdConnectedImageFilter<InputImageType, InternalImageType> ImageFilterType;
241 typename ImageFilterType::Pointer f= ImageFilterType::New();
242 f->SetInput(working_input);
243 f->SetVerbose(false);
245 f->SetUpper(GetUpperThresholdForTrachea());
246 f->SetMinimumLowerThreshold(-2000);
247 f->SetMaximumUpperThreshold(0);
248 f->SetAdaptLowerBorder(false);
249 f->SetAdaptUpperBorder(true);
250 f->SetMinimumSize(5000);
251 f->SetReplaceValue(1);
252 f->SetMultiplier(GetMultiplierForTrachea());
253 f->SetThresholdStepSize(GetThresholdStepSizeForTrachea());
254 f->SetMinimumThresholdStepSize(1);
255 for(unsigned int i=0; i<m_Seeds.size();i++) {
256 // std::cout<<"Adding seed " <<m_Seeds[i]<<"..."<<std::endl;
257 f->AddSeed(m_Seeds[i]);
260 trachea_tmp = f->GetOutput();
262 StopCurrentStep<InternalImageType>(trachea_tmp);
265 else { // Trachea not found
266 this->SetWarning("* WARNING * No seed found for trachea.");
271 //--------------------------------------------------------------------
272 //--------------------------------------------------------------------
273 StartNewStep("Extract the lung with Otsu filter");
274 // Automated Otsu thresholding and relabeling
275 typedef itk::OtsuThresholdImageFilter<InputImageType,InternalImageType> OtsuThresholdImageFilterType;
276 typename OtsuThresholdImageFilterType::Pointer otsuFilter=OtsuThresholdImageFilterType::New();
277 otsuFilter->SetInput(working_input);
278 otsuFilter->SetNumberOfHistogramBins(GetNumberOfHistogramBins());
279 otsuFilter->SetInsideValue(this->GetForegroundValue());
280 otsuFilter->SetOutsideValue(this->GetBackgroundValue());
281 otsuFilter->Update();
282 working_image = otsuFilter->GetOutput();
285 StopCurrentStep<InternalImageType>(working_image);
287 //--------------------------------------------------------------------
288 //--------------------------------------------------------------------
289 StartNewStep("Select labels");
291 working_image = LabelizeAndSelectLabels<InternalImageType>
293 GetBackgroundValue(),
294 GetForegroundValue(),
296 GetMinimalComponentSize(),
297 GetLabelizeParameters2());
300 StopCurrentStep<InternalImageType>(working_image);
302 //--------------------------------------------------------------------
303 //--------------------------------------------------------------------
304 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
305 StartNewStep("Remove the trachea");
307 working_image = SetBackground<InternalImageType, InternalImageType>
308 (working_image, trachea_tmp, 1, -1);
310 // Dilate the trachea
311 static const unsigned int Dim = InputImageType::ImageDimension;
312 typedef itk::BinaryBallStructuringElement<InternalPixelType, Dim> KernelType;
313 KernelType structuringElement;
314 structuringElement.SetRadius(GetRadiusForTrachea());
315 structuringElement.CreateStructuringElement();
316 typedef clitk::ConditionalBinaryDilateImageFilter<InternalImageType, InternalImageType, KernelType> ConditionalBinaryDilateImageFilterType;
317 typename ConditionalBinaryDilateImageFilterType::Pointer dilateFilter = ConditionalBinaryDilateImageFilterType::New();
318 dilateFilter->SetBoundaryToForeground(false);
319 dilateFilter->SetKernel(structuringElement);
320 dilateFilter->SetBackgroundValue (1);
321 dilateFilter->SetForegroundValue (-1);
322 dilateFilter->SetInput (working_image);
323 dilateFilter->Update();
324 working_image = dilateFilter->GetOutput();
326 // Set trachea with dilatation
327 trachea_tmp = SetBackground<InternalImageType, InternalImageType>
328 (trachea_tmp, working_image, -1, this->GetForegroundValue());
330 // Remove the trachea
331 working_image = SetBackground<InternalImageType, InternalImageType>
332 (working_image, working_image, -1, this->GetBackgroundValue());
335 working_image = LabelizeAndSelectLabels<InternalImageType>
337 GetBackgroundValue(),
338 GetForegroundValue(),
340 GetMinimalComponentSize(),
341 GetLabelizeParameters3());
344 StopCurrentStep<InternalImageType>(working_image);
348 //--------------------------------------------------------------------
349 //--------------------------------------------------------------------
350 typedef clitk::AutoCropFilter<InternalImageType> CropFilterType;
351 typename CropFilterType::Pointer cropFilter = CropFilterType::New();
352 if (m_Seeds.size() != 0) { // if ==0 ->no trachea found
353 StartNewStep("Croping trachea");
354 cropFilter->SetInput(trachea_tmp);
355 cropFilter->Update(); // Needed
356 typedef itk::CastImageFilter<InternalImageType, MaskImageType> CastImageFilterType;
357 typename CastImageFilterType::Pointer caster= CastImageFilterType::New();
358 caster->SetInput(cropFilter->GetOutput());
360 trachea = caster->GetOutput();
361 StopCurrentStep<MaskImageType>(trachea);
364 //--------------------------------------------------------------------
365 //--------------------------------------------------------------------
366 StartNewStep("Croping lung");
367 typename CropFilterType::Pointer cropFilter2 = CropFilterType::New(); // Needed to reset pipeline
368 cropFilter2->SetInput(working_image);
369 cropFilter2->Update();
370 working_image = cropFilter2->GetOutput();
371 StopCurrentStep<InternalImageType>(working_image);
373 //--------------------------------------------------------------------
374 //--------------------------------------------------------------------
375 StartNewStep("Separate Left/Right lungs");
377 working_image = Labelize<InternalImageType>(working_image,
378 GetBackgroundValue(),
380 GetMinimalComponentSize());
383 typedef itk::StatisticsImageFilter<InternalImageType> StatisticsImageFilterType;
384 typename StatisticsImageFilterType::Pointer statisticsImageFilter=StatisticsImageFilterType::New();
385 statisticsImageFilter->SetInput(working_image);
386 statisticsImageFilter->Update();
387 unsigned int initialNumberOfLabels = statisticsImageFilter->GetMaximum();
388 working_image = statisticsImageFilter->GetOutput();
390 // Decompose the first label
391 static const unsigned int Dim = InputImageType::ImageDimension;
392 if (initialNumberOfLabels<2) {
393 // Structuring element radius
394 typename InputImageType::SizeType radius;
395 for (unsigned int i=0;i<Dim;i++) radius[i]=1;
396 typedef clitk::DecomposeAndReconstructImageFilter<InternalImageType,InternalImageType> DecomposeAndReconstructFilterType;
397 typename DecomposeAndReconstructFilterType::Pointer decomposeAndReconstructFilter=DecomposeAndReconstructFilterType::New();
398 decomposeAndReconstructFilter->SetInput(working_image);
399 decomposeAndReconstructFilter->SetVerbose(false);
400 decomposeAndReconstructFilter->SetRadius(radius);
401 decomposeAndReconstructFilter->SetMaximumNumberOfLabels(2);
402 decomposeAndReconstructFilter->SetMinimumObjectSize(this->GetMinimalComponentSize());
403 decomposeAndReconstructFilter->SetMinimumNumberOfIterations(1);
404 decomposeAndReconstructFilter->SetBackgroundValue(this->GetBackgroundValue());
405 decomposeAndReconstructFilter->SetForegroundValue(this->GetForegroundValue());
406 decomposeAndReconstructFilter->SetFullyConnected(true);
407 decomposeAndReconstructFilter->SetNumberOfNewLabels(1);
408 decomposeAndReconstructFilter->Update();
409 working_image = decomposeAndReconstructFilter->GetOutput();
412 // Retain labels (lungs)
413 typedef itk::ThresholdImageFilter<InternalImageType> ThresholdImageFilterType;
414 typename ThresholdImageFilterType::Pointer thresholdFilter = ThresholdImageFilterType::New();
415 thresholdFilter->SetInput(working_image);
416 thresholdFilter->ThresholdAbove(2);
417 thresholdFilter->SetOutsideValue(this->GetBackgroundValue());
418 thresholdFilter->Update();
419 working_image = thresholdFilter->GetOutput();
420 StopCurrentStep<InternalImageType> (working_image);
422 //--------------------------------------------------------------------
425 //--------------------------------------------------------------------
426 template <class TInputImageType, class TMaskImageType>
428 clitk::ExtractLungFilter<TInputImageType, TMaskImageType>::
431 typedef itk::CastImageFilter<InternalImageType, MaskImageType> CastImageFilterType;
432 typename CastImageFilterType::Pointer caster= CastImageFilterType::New();
433 caster->SetInput(working_image);
436 //this->SetNthOutput(0, caster->GetOutput()); // -> no because redo filter otherwise
437 this->GraftOutput(caster->GetOutput());
440 //--------------------------------------------------------------------
443 #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX