From b7c9c237d95cc8becc1402263a02903dc255b414 Mon Sep 17 00:00:00 2001 From: David Sarrut Date: Mon, 26 Sep 2011 09:08:38 +0200 Subject: [PATCH] Now inherit from clitkStructuresExtractionFilter --- segmentation/clitkExtractMediastinum.ggo | 18 +- segmentation/clitkExtractMediastinumFilter.h | 25 +-- .../clitkExtractMediastinumFilter.txx | 208 ++++++++---------- .../clitkExtractMediastinumGenericFilter.txx | 9 +- 4 files changed, 99 insertions(+), 161 deletions(-) diff --git a/segmentation/clitkExtractMediastinum.ggo b/segmentation/clitkExtractMediastinum.ggo index bbb473d..bd2bbe4 100644 --- a/segmentation/clitkExtractMediastinum.ggo +++ b/segmentation/clitkExtractMediastinum.ggo @@ -17,22 +17,8 @@ section "I/O" option "input" i "Input CT filename" string yes option "output" o "Output lungs mask filename" string yes option "afdb" a "Input Anatomical Feature DB (needs Patient, Lungs, Trachea, VertebralBody)" string no +option "relpos" - "List of filenames for relativeposition operations" string no multiple + option "useBones" - "If set : do use bones mask (when image is not injected)" flag off option "maxAntSpine" - "Distance max to anterior part of the VertebralBody (spine) in mm" double no default="10" -option "spacing" - "Intermediate resampling spacing" double no default="6" -option "ft_LR_lungs" - "RelPos LR limits lungs" double no default="0.3" -option "ft_bones" - "RelPos AP limits bones" double no default="0.6" -option "ft_inf_lungs" - "RelPos inf limits bones" double no default="0.05" -option "ft_ant_sternum" - "RelPos ant limits sternum" double no default="0.5" - -#section "Step 1 : Left/Right limits with lungs" - -section "Step 2 : Ant/Post limits with bones" -#option "antSpine" - "Distance max to anterior part of the spine in mm" double no default="10" - -#section "Step 3 : Inf limits with Lung" - -# section "Step x : threshold for removing bones and injected parts" -# option "upper" - "Upper threshold" double no default="150" -# option "lower" - "Lower threshold" double no default="-200" \ No newline at end of file diff --git a/segmentation/clitkExtractMediastinumFilter.h b/segmentation/clitkExtractMediastinumFilter.h index 61abdee..3144c95 100644 --- a/segmentation/clitkExtractMediastinumFilter.h +++ b/segmentation/clitkExtractMediastinumFilter.h @@ -19,8 +19,7 @@ #ifndef CLITKEXTRACTMEDIASTINUMFILTER_H #define CLITKEXTRACTMEDIASTINUMFILTER_H -#include "clitkFilterBase.h" -#include "clitkFilterWithAnatomicalFeatureDatabaseManagement.h" +#include "clitkStructuresExtractionFilter.h" namespace clitk { @@ -38,9 +37,7 @@ namespace clitk { template class ITK_EXPORT ExtractMediastinumFilter: - public virtual clitk::FilterBase, - public clitk::FilterWithAnatomicalFeatureDatabaseManagement, - public itk::ImageToImageFilter > + public clitk::StructuresExtractionFilter { public: @@ -69,7 +66,8 @@ namespace clitk { typedef typename MaskSliceType::PointType MaskSlicePointType; /** Standard class typedefs. */ - typedef itk::ImageToImageFilter Superclass; + // typedef itk::ImageToImageFilter Superclass; + typedef clitk::StructuresExtractionFilter Superclass; typedef ExtractMediastinumFilter Self; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; @@ -88,8 +86,8 @@ namespace clitk { MaskImagePixelType fgLeftLung=1, MaskImagePixelType fgRightLung=2); void SetInputBonesLabelImage(const MaskImageType * image, MaskImagePixelType bg=0); void SetInputTracheaLabelImage(const MaskImageType * image, MaskImagePixelType bg=0); - - // Output filename (for AFBD) + + // Output filename (for AFBD) itkSetMacro(OutputMediastinumFilename, std::string); itkGetConstMacro(OutputMediastinumFilename, std::string); @@ -106,9 +104,6 @@ namespace clitk { itkSetMacro(BackgroundValueBones, MaskImagePixelType); itkGetConstMacro(BackgroundValueBones, MaskImagePixelType); - itkGetConstMacro(BackgroundValue, MaskImagePixelType); - itkGetConstMacro(ForegroundValue, MaskImagePixelType); - itkSetMacro(ForegroundValueLeftLung, MaskImagePixelType); itkGetConstMacro(ForegroundValueLeftLung, MaskImagePixelType); @@ -138,10 +133,7 @@ namespace clitk { virtual void GenerateOutputInformation(); virtual void GenerateInputRequestedRegion(); virtual void GenerateData(); - - itkSetMacro(BackgroundValue, MaskImagePixelType); - itkSetMacro(ForegroundValue, MaskImagePixelType); - + MaskImagePixelType m_BackgroundValuePatient; MaskImagePixelType m_BackgroundValueLung; MaskImagePixelType m_BackgroundValueBones; @@ -149,9 +141,6 @@ namespace clitk { MaskImagePixelType m_ForegroundValueLeftLung; MaskImagePixelType m_ForegroundValueRightLung; - MaskImagePixelType m_BackgroundValue; - MaskImagePixelType m_ForegroundValue; - MaskImagePointer output; MaskImagePointer patient; MaskImagePointer lung; diff --git a/segmentation/clitkExtractMediastinumFilter.txx b/segmentation/clitkExtractMediastinumFilter.txx index 67bc305..e3f83dc 100644 --- a/segmentation/clitkExtractMediastinumFilter.txx +++ b/segmentation/clitkExtractMediastinumFilter.txx @@ -45,9 +45,7 @@ template clitk::ExtractMediastinumFilter:: ExtractMediastinumFilter(): - clitk::FilterBase(), - clitk::FilterWithAnatomicalFeatureDatabaseManagement(), - itk::ImageToImageFilter() + clitk::StructuresExtractionFilter() { this->SetNumberOfRequiredInputs(1); SetBackgroundValuePatient(0); @@ -55,12 +53,6 @@ ExtractMediastinumFilter(): SetBackgroundValueBones(0); SetForegroundValueLeftLung(1); SetForegroundValueRightLung(2); - SetBackgroundValue(0); - SetForegroundValue(1); - SetIntermediateSpacing(6); - SetFuzzyThreshold("LR_lungs", 0.3); - SetFuzzyThreshold("bones", 0.6); - SetFuzzyThreshold("inf_lungs", 0.05); SetDistanceMaxToAnteriorPartOfTheVertebralBody(10); SetOutputMediastinumFilename("mediastinum.mhd"); UseBonesOff(); @@ -156,7 +148,6 @@ GenerateInputRequestedRegion() // Superclass::GenerateInputRequestedRegion(); // DD("End GenerateInputRequestedRegion"); } - //-------------------------------------------------------------------- @@ -171,6 +162,7 @@ SetInput(const ImageType * image) //-------------------------------------------------------------------- + //-------------------------------------------------------------------- template void @@ -181,19 +173,21 @@ GenerateOutputInformation() { //-------------------------------------------------------------------- // Get input pointers - LoadAFDB(); + this->LoadAFDB(); ImageConstPointer input = dynamic_cast(itk::ProcessObject::GetInput(0)); MaskImagePointer patient, lung, bones, trachea; - patient = GetAFDB()->template GetImage ("Patient"); - lung = GetAFDB()->template GetImage ("Lungs"); + patient = this->GetAFDB()->template GetImage ("Patient"); + lung = this->GetAFDB()->template GetImage ("Lungs"); if (GetUseBones()) { - bones = GetAFDB()->template GetImage ("Bones"); + bones = this->GetAFDB()->template GetImage ("Bones"); } - trachea = GetAFDB()->template GetImage ("Trachea"); - + trachea = this->GetAFDB()->template GetImage ("Trachea"); + + //this->VerboseImageSizeFlagOn(); + //-------------------------------------------------------------------- // Step : Crop support (patient) to lung extend in RL - StartNewStep("Crop support like lungs along LR"); + this->StartNewStep("[Mediastinum] Crop support like lungs along LR"); typedef clitk::CropLikeImageFilter CropFilterType; typename CropFilterType::Pointer cropFilter = CropFilterType::New(); cropFilter->SetInput(patient); @@ -205,7 +199,7 @@ GenerateOutputInformation() { //-------------------------------------------------------------------- // Step : Crop support (previous) to bones extend in AP if (GetUseBones()) { - StartNewStep("Crop support like bones along AP"); + this->StartNewStep("[Mediastinum] Crop support like bones along AP"); cropFilter = CropFilterType::New(); cropFilter->SetInput(output); cropFilter->SetCropLikeImage(bones, 1);// Indicate that we only crop in Y (Ant-Post) axe @@ -216,7 +210,7 @@ GenerateOutputInformation() { //-------------------------------------------------------------------- // Step : patient minus lungs, minus bones, minus trachea - StartNewStep("Patient contours minus lungs, trachea [and bones]"); + this->StartNewStep("[Mediastinum] Patient contours minus lungs, trachea [and bones]"); typedef clitk::BooleanOperatorLabelImageFilter BoolFilterType; typename BoolFilterType::Pointer boolFilter = BoolFilterType::New(); boolFilter->InPlaceOn(); @@ -237,7 +231,7 @@ GenerateOutputInformation() { output = boolFilter->GetOutput(); // Auto crop to gain support area - output = clitk::AutoCrop(output, GetBackgroundValue()); + output = clitk::AutoCrop(output, this->GetBackgroundValue()); this->template StopCurrentStep(output); //-------------------------------------------------------------------- @@ -246,69 +240,50 @@ GenerateOutputInformation() { // (because RelativePositionPropImageFilter only consider fg=1); // (label must be '1' because right is greater than left). (WE DO // NOT NEED TO SEPARATE ? ) - StartNewStep("Left/Right limits with lungs"); + this->StartNewStep("[Mediastinum] Left/Right limits with lungs"); // The following cannot be "inplace" because mask is the same than input ... MaskImagePointer right_lung = clitk::SetBackground(lung, lung, 2, 0, false); MaskImagePointer left_lung = clitk::SetBackground(lung, lung, 1, 0, false); - right_lung = clitk::ResizeImageLike(right_lung, output, GetBackgroundValue()); - left_lung = clitk::ResizeImageLike(left_lung, output, GetBackgroundValue()); - // writeImage(right_lung, "right.mhd"); - // writeImage(left_lung, "left.mhd"); - - typedef clitk::AddRelativePositionConstraintToLabelImageFilter RelPosFilterType; - typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New(); - relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()); - relPosFilter->VerboseStepFlagOff(); - relPosFilter->WriteStepFlagOff(); - relPosFilter->SetInput(output); - relPosFilter->SetInputObject(left_lung); - relPosFilter->AddOrientationType(RelPosFilterType::AtRightTo); // warning left lung is at right ;) - relPosFilter->SetIntermediateSpacing(GetIntermediateSpacing()); - relPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("LR_lungs")); - relPosFilter->Update(); - output = relPosFilter->GetOutput(); - - relPosFilter = RelPosFilterType::New(); - relPosFilter->SetInput(output); - relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()); - relPosFilter->VerboseStepFlagOff(); - relPosFilter->WriteStepFlagOff(); - relPosFilter->SetInput(output); - relPosFilter->SetInputObject(right_lung); - relPosFilter->AddOrientationType(RelPosFilterType::AtLeftTo); - relPosFilter->SetIntermediateSpacing(GetIntermediateSpacing()); - relPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("LR_lungs")); - relPosFilter->Update(); - output = relPosFilter->GetOutput(); - this->template StopCurrentStep(output); - - //-------------------------------------------------------------------- - // Step : superior limits - StartNewStep("Keep inferior to CricoidCartilag"); - // load Cricoid, get centroid, cut above (or below), lower bound - MaskImagePointer CricoidCartilag = GetAFDB()->template GetImage ("CricoidCartilag"); - MaskImagePointType p; - p[0] = p[1] = p[2] = 0.0; // to avoid warning - clitk::FindExtremaPointInAGivenDirection(CricoidCartilag, GetBackgroundValue(), 2, true, p); - output = clitk::CropImageRemoveGreaterThan(output, 2, p[2], true, GetBackgroundValue()); + left_lung = clitk::SetBackground(left_lung, left_lung, 2, 1, false); + right_lung = clitk::ResizeImageLike(right_lung, output, this->GetBackgroundValue()); + left_lung = clitk::ResizeImageLike(left_lung, output, this->GetBackgroundValue()); + this->GetAFDB()->template SetImage("RightLung", "seg/RightLung.mhd", + right_lung, true); + this->GetAFDB()->template SetImage("LeftLung", "seg/LeftLung.mhd", + left_lung, true); + this->GetAFDB()->Write(); this->template StopCurrentStep(output); //-------------------------------------------------------------------- // Step : AP limits from bones // Separate the bones in the ant-post middle - MaskImageConstPointer bones_ant; - MaskImageConstPointer bones_post; - MaskImagePointType middle_AntPost__position; + MaskImagePointer bones_ant; + MaskImagePointer bones_post; + MaskImagePointType middle_AntPost_position; if (GetUseBones()) { - StartNewStep("Ant/Post limits with bones"); - middle_AntPost__position[0] = middle_AntPost__position[2] = 0; - middle_AntPost__position[1] = bones->GetOrigin()[1]+(bones->GetLargestPossibleRegion().GetSize()[1]*bones->GetSpacing()[1])/2.0; + this->StartNewStep("[Mediastinum] Ant/Post limits with bones"); + + // To define ant and post part of the bones with a single horizontal line MaskImageIndexType index_bones_middle; - bones->TransformPhysicalPointToIndex(middle_AntPost__position, index_bones_middle); + + // Method1: cut in the middle (not optimal) + /* + middle_AntPost_position[0] = middle_AntPost_position[2] = 0; + middle_AntPost_position[1] = bones->GetOrigin()[1]+ + (bones->GetLargestPossibleRegion().GetSize()[1]*bones->GetSpacing()[1])/2.0; + DD(middle_AntPost_position); + bones->TransformPhysicalPointToIndex(middle_AntPost_position, index_bones_middle); + */ + // Method2: Use VertebralBody, take most ant point + MaskImagePointer VertebralBody = this->GetAFDB()->template GetImage("VertebralBody"); + FindExtremaPointInAGivenDirection(VertebralBody, this->GetBackgroundValue(), + 1, true, middle_AntPost_position); + bones->TransformPhysicalPointToIndex(middle_AntPost_position, index_bones_middle); + // Split bone image first into two parts (ant and post), and crop // lateraly to get vertebral typedef itk::RegionOfInterestImageFilter ROIFilterType; @@ -319,8 +294,8 @@ GenerateOutputInformation() { MaskImageIndexType index = region.GetIndex(); // ANT part // crop LR to keep 1/4 center part - index[0] = size[0]/4+size[0]/8; - size[0] = size[0]/4; + // index[0] = size[0]/4+size[0]/8; + //size[0] = size[0]/4; // crop AP to keep first (ant) part size[1] = index_bones_middle[1]; //size[1]/2.0; region.SetSize(size); @@ -330,7 +305,7 @@ GenerateOutputInformation() { roiFilter->ReleaseDataFlagOff(); roiFilter->Update(); bones_ant = roiFilter->GetOutput(); - // writeImage(bones_ant, "b_ant.mhd"); + // writeImage(bones_ant, "b_ant.mhd"); // POST part roiFilter = ROIFilterType::New(); index[1] = bones->GetLargestPossibleRegion().GetIndex()[1] + size[1]-1; @@ -342,64 +317,53 @@ GenerateOutputInformation() { roiFilter->ReleaseDataFlagOff(); roiFilter->Update(); bones_post = roiFilter->GetOutput(); - // writeImage(bones_post, "b_post.mhd"); - - // Go ! - relPosFilter->SetCurrentStepNumber(0); - relPosFilter->ResetPipeline();// = RelPosFilterType::New(); - relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()); - relPosFilter->VerboseStepFlagOff(); - relPosFilter->WriteStepFlagOff(); - relPosFilter->SetInput(output); - relPosFilter->SetInputObject(bones_post); - relPosFilter->AddOrientationType(RelPosFilterType::AntTo); - relPosFilter->SetIntermediateSpacing(GetIntermediateSpacing()); - relPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("bones")); - relPosFilter->Update(); - output = relPosFilter->GetOutput(); - // writeImage(output, "post.mhd"); - - relPosFilter->SetInput(relPosFilter->GetOutput()); - relPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()); - relPosFilter->VerboseStepFlagOff(); - relPosFilter->WriteStepFlagOff(); - relPosFilter->SetInput(output); - relPosFilter->SetInputObject(bones_ant); - relPosFilter->AddOrientationType(RelPosFilterType::PostTo); - relPosFilter->SetIntermediateSpacing(GetIntermediateSpacing()); - relPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("bones")); - relPosFilter->Update(); - output = relPosFilter->GetOutput(); + // writeImage(bones_post, "b_post.mhd"); + + // Now, insert this image in the AFDB + this->GetAFDB()->template SetImage("Bones_Post", "seg/Bones_Post.mhd", + bones_post, true); + this->GetAFDB()->template SetImage("Bones_Ant", "seg/Bones_Ant.mhd", + bones_ant, true); + this->GetAFDB()->Write(); + this->template StopCurrentStep(output); } + //-------------------------------------------------------------------- + // Generic RelativePosition processes + output = this->ApplyRelativePositionList("Mediastinum", output); + + //-------------------------------------------------------------------- + // Step : SI limits It is better to do this limit *AFTER* the + // RelativePosition to avoid some issue due to superior boundaries. + this->StartNewStep("[Mediastinum] Keep inferior to CricoidCartilag"); + // load Cricoid, get centroid, cut above (or below), lower bound + MaskImagePointer CricoidCartilag = this->GetAFDB()->template GetImage ("CricoidCartilag"); + MaskImagePointType p; + p[0] = p[1] = p[2] = 0.0; // to avoid warning + clitk::FindExtremaPointInAGivenDirection(CricoidCartilag, + this->GetBackgroundValue(), 2, true, p); + output = clitk::CropImageRemoveGreaterThan(output, 2, p[2], true, this->GetBackgroundValue()); + this->template StopCurrentStep(output); + //-------------------------------------------------------------------- // Step: Get CCL - StartNewStep("Keep main connected component"); - output = clitk::Labelize(output, GetBackgroundValue(), false, 500); + this->StartNewStep("[Mediastinum] Keep main connected component"); + output = clitk::Labelize(output, this->GetBackgroundValue(), false, 500); // output = RemoveLabels(output, BG, param->GetLabelsToRemove()); - output = clitk::KeepLabels(output, GetBackgroundValue(), - GetForegroundValue(), 1, 1, 0); + output = clitk::KeepLabels(output, this->GetBackgroundValue(), + this->GetForegroundValue(), 1, 1, 0); this->template StopCurrentStep(output); //-------------------------------------------------------------------- // Step: Remove post part from VertebralBody - StartNewStep("Remove post part according to VertebralBody"); + this->StartNewStep("[Mediastinum] Remove post part according to VertebralBody"); RemovePostPartOfVertebralBody(); this->template StopCurrentStep(output); - //-------------------------------------------------------------------- - // Step: Remove ant part according to Sternum - StartNewStep("Remove ant part according to Sternum"); - MaskImagePointer Sternum = GetAFDB()->template GetImage ("Sternum"); - output = clitk::SliceBySliceRelativePosition(output, Sternum, 2, - GetFuzzyThreshold("ant_sternum"), - "PostTo", false, 3, true, false); - this->template StopCurrentStep(output); - //-------------------------------------------------------------------- // Step: Slice by Slice CCL - StartNewStep("Slice by Slice keep only one component"); + this->StartNewStep("[Mediastinum] Slice by Slice keep only one component"); typedef clitk::ExtractSliceFilter ExtractSliceFilterType; // typename ExtractSliceFilterType::Pointer ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New(); @@ -426,8 +390,8 @@ GenerateOutputInformation() { //-------------------------------------------------------------------- // Step 10 : AutoCrop - StartNewStep("AutoCrop"); - output = clitk::AutoCrop(output, GetBackgroundValue()); + this->StartNewStep("[Mediastinum] AutoCrop"); + output = clitk::AutoCrop(output, this->GetBackgroundValue()); this->template StopCurrentStep(output); // End, set the real size @@ -447,8 +411,8 @@ GenerateData() { this->GraftOutput(output); // Store image filenames into AFDB - GetAFDB()->SetImageFilename("Mediastinum", this->GetOutputMediastinumFilename()); - WriteAFDB(); + this->GetAFDB()->SetImageFilename("Mediastinum", this->GetOutputMediastinumFilename()); + this->WriteAFDB(); } //-------------------------------------------------------------------- @@ -475,7 +439,7 @@ RemovePostPartOfVertebralBody() // Get VertebralBody mask image MaskImagePointer VertebralBody = - GetAFDB()->template GetImage ("VertebralBody"); + this->GetAFDB()->template GetImage ("VertebralBody"); // Consider vertebral body slice by slice std::vector vertebralSlices; @@ -486,7 +450,7 @@ RemovePostPartOfVertebralBody() for(uint i=0; i(vertebralSlices[i], - GetBackgroundValue(), + this->GetBackgroundValue(), 1, true, p); if (found) { vertebralAntPositionBySlice[i] = p; @@ -540,7 +504,7 @@ RemovePostPartOfVertebralBody() itk::ImageRegionIterator it(output, region); it.GoToBegin(); while (!it.IsAtEnd()) { - it.Set(GetBackgroundValue()); + it.Set(this->GetBackgroundValue()); ++it; } } diff --git a/segmentation/clitkExtractMediastinumGenericFilter.txx b/segmentation/clitkExtractMediastinumGenericFilter.txx index 0670ed7..679fb36 100644 --- a/segmentation/clitkExtractMediastinumGenericFilter.txx +++ b/segmentation/clitkExtractMediastinumGenericFilter.txx @@ -78,11 +78,10 @@ SetOptionsFromArgsInfoToFilter(FilterType * f) f->SetDistanceMaxToAnteriorPartOfTheVertebralBody(mArgsInfo.maxAntSpine_arg); f->SetUseBones(mArgsInfo.useBones_flag); - f->SetIntermediateSpacing(mArgsInfo.spacing_arg); - f->SetFuzzyThreshold("LR_lungs", mArgsInfo.ft_LR_lungs_arg); - f->SetFuzzyThreshold("bones", mArgsInfo.ft_bones_arg); - f->SetFuzzyThreshold("inf_lungs", mArgsInfo.ft_inf_lungs_arg); - f->SetFuzzyThreshold("ant_sternum", mArgsInfo.ft_ant_sternum_arg); + // Set RelativePositionList filenames + for(uint i=0; iAddRelativePositionListFilename(mArgsInfo.relpos_arg[i]); + } } //-------------------------------------------------------------------- -- 2.47.1