]> Creatis software - clitk.git/commitdiff
Merge branch 'master' of tux.creatis.insa-lyon.fr:clitk
authorVivien Delmon <vivien.delmon@creatis.insa-lyon.fr>
Mon, 12 Dec 2011 17:07:26 +0000 (18:07 +0100)
committerVivien Delmon <vivien.delmon@creatis.insa-lyon.fr>
Mon, 12 Dec 2011 17:07:26 +0000 (18:07 +0100)
segmentation/clitkExtractLung.ggo
segmentation/clitkExtractLungFilter.h
segmentation/clitkExtractLungFilter.txx
segmentation/clitkExtractLungGenericFilter.txx
tools/clitkDicomWave2Text.ggo

index 98ddbd80d3dc29f1dcb53355d663a6dc7e1a4638..0cae0f89ad0a926ba70e365d0e0e779e56b06435 100644 (file)
@@ -31,13 +31,17 @@ option "minSize"     -      "Minimum component size in voxels"        int           no      default="1
 
 section "Step 2 : find trachea"
 
-option "skipslices"                  -  "Number of slices to skip before searching seed" int no default="0"
-option "upperThresholdForTrachea"    - "Initial upper threshold for trachea"  double   no  default="-900"
-option "multiplierForTrachea"       -  "Multiplier for the region growing"    double   no  default="5"
-option "thresholdStepSizeForTrachea" - "Threshold step size"                  int      no  default="64"
-option "seed"                        - "Index of the trachea seed point (in pixel, not in mm)"      int        no  multiple
-option "doNotCheckTracheaVolume"     -  "If set, do not check the trachea volume" flag off
-option "verboseRG"                   -  "Verbose RegionGrowing"   flag off
+option "type"                        -  "trachea finding algorithm (0: original; 1: LOLA11)" int no default="0"
+option "skipslices"                  -  "0: Number of slices to skip before searching seed" int no default="0"
+option "upperThresholdForTrachea"    - "0: Initial upper threshold for trachea"  double        no  default="-900"
+option "multiplierForTrachea"       -  "0: Multiplier for the region growing"    double        no  default="5"
+option "thresholdStepSizeForTrachea" - "0: Threshold step size"                       int      no  default="64"
+option "seed"                        - "0,1: Index of the trachea seed point (in pixel, not in mm)"      int   no  multiple
+option "doNotCheckTracheaVolume"     -  "0,1: If set, do not check the trachea volume" flag off
+option "verboseRG"                   -  "0,1: Verbose RegionGrowing"   flag off
+option "maxElongation"               -  "1: Maximum allowed elongation of candidate regions for the trachea" float no default="0.5"
+option "numSlices"                   -  "1: Number of slices to search for trachea" int no default="50"
+option "seedPreProcessingThreshold"   -  "1: Threshold for the pre-processing step of the algorithm" int no default="-400"
 
 section "Step 3 : auto extract lung"
 
index 23fbbc7a8fd427a8f5f749aaee81c69eae275064..ead4d988f36597401d3684ada7e69da62ee68851 100644 (file)
@@ -142,6 +142,9 @@ namespace clitk {
 
     void SetLabelizeParameters1(LabelParamType * a) { m_LabelizeParameters1 = a; }
     itkGetConstMacro(LabelizeParameters1, LabelParamType*);
+    
+    itkSetMacro(TracheaSeedAlgorithm, int);
+    itkGetConstMacro(TracheaSeedAlgorithm, int);
 
     // Step 2 options FindTrachea
     itkSetMacro(UpperThresholdForTrachea, InputImagePixelType);
@@ -153,6 +156,14 @@ namespace clitk {
     itkSetMacro(ThresholdStepSizeForTrachea, InputImagePixelType);
     itkGetConstMacro(ThresholdStepSizeForTrachea, InputImagePixelType);
 
+    // options FindTrachea2
+    itkSetMacro(NumSlices, int);
+    itkGetConstMacro(NumSlices, int);
+    itkSetMacro(MaxElongation, double);
+    itkGetConstMacro(MaxElongation, double);
+    itkSetMacro(SeedPreProcessingThreshold, int);
+    itkGetConstMacro(SeedPreProcessingThreshold, int);
+
     void AddSeed(InternalIndexType s);
     std::vector<InternalIndexType> & GetSeeds() { return  m_Seeds; }
 
@@ -200,7 +211,7 @@ namespace clitk {
     itkSetMacro(AutoCrop, bool);
     itkGetConstMacro(AutoCrop, bool);
     itkBooleanMacro(AutoCrop);
-
+    
   protected:
     ExtractLungFilter();
     virtual ~ExtractLungFilter() {}
@@ -231,6 +242,7 @@ namespace clitk {
     LabelParamType* m_LabelizeParameters1;
 
     // Step 2
+    int m_TracheaSeedAlgorithm;
     InputImagePixelType m_UpperThresholdForTrachea;
     InputImagePixelType m_ThresholdStepSizeForTrachea;
     double m_MultiplierForTrachea;
@@ -238,6 +250,9 @@ namespace clitk {
     int m_NumberOfSlicesToSkipBeforeSearchingSeed;
     bool m_TracheaVolumeMustBeCheckedFlag;
     bool m_VerboseRegionGrowingFlag;
+    int m_NumSlices;
+    double m_MaxElongation;
+    int m_SeedPreProcessingThreshold;
 
     // Step 3
     int m_NumberOfHistogramBins;
@@ -264,6 +279,7 @@ namespace clitk {
     
     // Functions for trachea extraction
     bool SearchForTracheaSeed(int skip);
+    bool SearchForTracheaSeed2(int numberOfSlices);
     void SearchForTrachea();
     void TracheaRegionGrowing();
     double ComputeTracheaVolume();
index d2bff5edd5962647512fd2d1070017b17a5dab34..b7b28665baff5006bc2de75cfc158fd8be9821fe 100644 (file)
 #include "itkBinaryMorphologicalOpeningImageFilter.h"
 #include "itkBinaryMorphologicalClosingImageFilter.h"
 #include "itkConstantPadImageFilter.h"
+#include <itkBinaryBallStructuringElement.h>
+#include "itkStatisticsLabelObject.h"
+#include "itkLabelMap.h"
+#include "itkLabelImageToShapeLabelMapFilter.h"
+#include "itkLabelMapToLabelImageFilter.h"
+#include "itkExtractImageFilter.h"
+#include "itkOrientImageFilter.h"
+#include "itkSpatialOrientationAdapter.h"
+#include "itkImageDuplicator.h"
+#include <fcntl.h>
 
 //--------------------------------------------------------------------
 template <class ImageType>
@@ -69,11 +79,16 @@ ExtractLungFilter():
   SetLabelizeParameters1(p1);
 
   // Step 2 default values
-  SetUpperThresholdForTrachea(-900);
+  SetTracheaSeedAlgorithm(0);
+  SetUpperThresholdForTrachea(-700);
   SetMultiplierForTrachea(5);
   SetThresholdStepSizeForTrachea(64);
   SetNumberOfSlicesToSkipBeforeSearchingSeed(0);
   TracheaVolumeMustBeCheckedFlagOn();
+  SetNumSlices(50);
+  SetMaxElongation(0.5);
+  SetSeedPreProcessingThreshold(-400);
   
   // Step 3 default values
   SetNumberOfHistogramBins(500);
@@ -561,7 +576,217 @@ SearchForTracheaSeed(int skip)
 }
 //--------------------------------------------------------------------
 
+
+bool is_orientation_superior(itk::SpatialOrientation::ValidCoordinateOrientationFlags orientation)
+{
+  itk::SpatialOrientation::CoordinateTerms sup = itk::SpatialOrientation::ITK_COORDINATE_Superior;
+  std::cout << "orientation: " << std::hex << orientation << "; superior: " << std::hex << sup << std::endl;
+  std::cout << std::dec;
+
+  bool primary = (orientation & 0x0000ff) == sup;
+  bool secondary = ((orientation & 0x00ff00) >> 8) == sup;
+  bool tertiary = ((orientation & 0xff0000) >> 16) == sup;
+  return primary || secondary || tertiary;
+}
+
+//--------------------------------------------------------------------
+template <class ImageType>
+bool 
+clitk::ExtractLungFilter<ImageType>::
+SearchForTracheaSeed2(int numberOfSlices)
+{
+  if (m_Seeds.size() == 0) { // try to find seed (if not zero, it is given by user)    
+    if (GetVerboseFlag())
+      std::cout << "SearchForTracheaSeed2(" << numberOfSlices << ", " << GetMaxElongation() << ")" << std::endl;
+    
+    typedef unsigned char MaskPixelType;
+    typedef itk::Image<MaskPixelType, ImageType::ImageDimension> MaskImageType;
+    typedef itk::Image<typename MaskImageType::PixelType, 2> MaskImageType2D;
+    typedef itk::BinaryThresholdImageFilter<ImageType, MaskImageType> ThresholdFilterType;
+    typedef itk::BinaryBallStructuringElement<MaskPixelType, MaskImageType::ImageDimension> KernelType;
+    typedef itk::BinaryMorphologicalClosingImageFilter<MaskImageType, MaskImageType, KernelType> ClosingFilterType;
+    typedef itk::BinaryMorphologicalOpeningImageFilter<MaskImageType, MaskImageType, KernelType> OpeningFilterType;
+    typedef itk::ExtractImageFilter<MaskImageType, MaskImageType2D> SlicerFilterType;
+    typedef itk::ConnectedComponentImageFilter<MaskImageType2D, MaskImageType2D> LabelFilterType;
+    typedef itk::ShapeLabelObject<MaskPixelType, MaskImageType2D::ImageDimension> ShapeLabelType;
+    typedef itk::LabelMap<ShapeLabelType> LabelImageType;
+    typedef itk::LabelImageToShapeLabelMapFilter<MaskImageType2D, LabelImageType> ImageToLabelMapFilterType;
+    typedef itk::LabelMapToLabelImageFilter<LabelImageType, MaskImageType2D> LabelMapToImageFilterType;
+    typedef itk::ImageFileWriter<MaskImageType2D> FileWriterType;
+    
+    // threshold to isolate airawys and lungs
+    typename ThresholdFilterType::Pointer threshold = ThresholdFilterType::New();
+    threshold->SetLowerThreshold(-2000);
+    threshold->SetUpperThreshold(GetSeedPreProcessingThreshold());
+    threshold->SetInput(working_input);
+    threshold->Update();
+    
+    KernelType kernel_closing, kernel_opening;
+    
+    // remove small noise
+    typename OpeningFilterType::Pointer opening = OpeningFilterType::New();
+    kernel_opening.SetRadius(1);
+    opening->SetKernel(kernel_opening);
+    opening->SetInput(threshold->GetOutput());
+    opening->Update();
+    
+    typename SlicerFilterType::Pointer slicer = SlicerFilterType::New();
+    slicer->SetInput(opening->GetOutput());
+    
+    // label result
+    typename LabelFilterType::Pointer label_filter = LabelFilterType::New();
+    label_filter->SetInput(slicer->GetOutput());
+    
+    // extract shape information from labels
+    typename ImageToLabelMapFilterType::Pointer label_to_map_filter = ImageToLabelMapFilterType::New();
+    label_to_map_filter->SetInput(label_filter->GetOutput());
+    
+    typename LabelMapToImageFilterType::Pointer map_to_label_filter = LabelMapToImageFilterType::New();
+    typename FileWriterType::Pointer writer = FileWriterType::New();
+    
+    typename ImageType::IndexType index;
+    typename ImageType::RegionType region = working_input->GetLargestPossibleRegion();
+    typename ImageType::SizeType size = region.GetSize();
+    typename ImageType::SpacingType spacing = working_input->GetSpacing();
+    typename ImageType::PointType origin = working_input->GetOrigin();
+
+    int nslices = min(numberOfSlices, size[2]);
+    int start = 0, increment = 1;
+    itk::SpatialOrientationAdapter orientation;
+    typename ImageType::DirectionType dir = working_input->GetDirection();
+    if (!is_orientation_superior(orientation.FromDirectionCosines(dir))) {
+      start = size[2]-1;
+      increment = -1;
+    }
+    
+    typename MaskImageType::PointType image_centre;
+    image_centre[0] = size[0]/2;
+    image_centre[1] = size[1]/2;
+    image_centre[2] = 0;
+  
+    typedef InternalIndexType SeedType;
+    SeedType trachea_centre, shape_centre, max_e_centre, prev_e_centre;
+    typedef std::list<SeedType> PointListType;
+    typedef std::list<PointListType> SequenceListType;
+    PointListType* current_sequence = NULL;
+    SequenceListType sequence_list;
+
+    prev_e_centre.Fill(0);
+    std::ostringstream file_name;
+    index[0] = index[1] = 0;
+    size[0] = size[1] = 512;
+    size[2] = 0;
+    while (nslices--) {
+      index[2] = start;
+      start += increment;
+      
+      region.SetIndex(index);
+      region.SetSize(size);
+      slicer->SetExtractionRegion(region);
+      slicer->Update();
+      label_filter->SetInput(slicer->GetOutput());
+      label_filter->Update();
+
+      label_to_map_filter->SetInput(label_filter->GetOutput());
+      label_to_map_filter->Update();
+      typename LabelImageType::Pointer label_map = label_to_map_filter->GetOutput();
+
+      if (GetWriteStepFlag()) {
+        map_to_label_filter->SetInput(label_map);
+        writer->SetInput(map_to_label_filter->GetOutput());
+        file_name.str("");
+        file_name << "labels_";
+        file_name.width(3);
+        file_name.fill('0');
+        file_name << index[2] << ".mhd";
+        writer->SetFileName(file_name.str().c_str());
+        writer->Update();
+      }
+      
+      typename LabelImageType::LabelObjectContainerType shapes_map = label_map->GetLabelObjectContainer();
+      typename LabelImageType::LabelObjectContainerType::const_iterator s;
+      typename ShapeLabelType::Pointer shape, max_e_shape;
+      double max_elongation = GetMaxElongation();
+      double max_size = size[0]*size[1]/128;
+      double max_e = 0;
+      int nshapes = 0;
+      for (s = shapes_map.begin(); s != shapes_map.end(); s++) {
+        shape = s->second;
+        if (shape->GetSize() > 150 && shape->GetSize() <= max_size) {
+          double e = 1 - 1/shape->GetBinaryElongation();
+          //double area = 1 - r->Area() ;
+          if (e < max_elongation) {
+            nshapes++;
+            shape_centre[0] = (shape->GetCentroid()[0] - origin[0])/spacing[0];
+            shape_centre[1] = (shape->GetCentroid()[1] - origin[1])/spacing[1];
+            shape_centre[2] = index[2];
+            //double d = 1 - (shape_centre - image_centre).Magnitude()/max_dist;
+            double dx = shape_centre[0] - image_centre[0];
+            double d = 1 - dx*2/size[0];
+            e = e + d;
+            if (e > max_e)
+            {
+              max_e = e;
+              max_e_shape = shape;
+              max_e_centre = shape_centre;
+            }
+          }
+        }
+      }
+      
+      if (nshapes > 0)
+      {
+        itk::Point<typename SeedType::IndexValueType, ImageType::ImageDimension> p1, p2;
+        p1[0] = max_e_centre[0];
+        p1[1] = max_e_centre[1];
+        p1[2] = max_e_centre[2];
+        
+        p2[0] = prev_e_centre[0];
+        p2[1] = prev_e_centre[1];
+        p2[2] = prev_e_centre[2];
+        
+        double mag = (p2 - p1).GetNorm();
+        if (GetVerboseFlag()) {
+          cout.precision(3);
+          cout << index[2] << ": ";
+          cout << "region(" << max_e_centre[0] << ", " << max_e_centre[1] << ", " << max_e_centre[2] << "); ";
+          cout << "prev_region(" << prev_e_centre[0] << ", " << prev_e_centre[1] << ", " << prev_e_centre[2] << "); ";
+          cout << "mag(" << mag << "); " << endl;
+        }
+        
+        if (mag > 5)
+        {
+          PointListType point_list;
+          point_list.push_back(max_e_centre);
+          sequence_list.push_back(point_list);
+          current_sequence = &sequence_list.back();
+        }
+        else if (current_sequence)
+          current_sequence->push_back(max_e_centre);
+        
+        prev_e_centre= max_e_centre;
+      }
+    }
+    
+    size_t longest = 0;
+    for (typename SequenceListType::iterator s = sequence_list.begin(); s != sequence_list.end(); s++)
+    {
+      if (s->size() > longest)
+      {
+        longest = s->size();
+        trachea_centre = s->front();
+      }
+    }
+    
+    if (GetVerboseFlag()) 
+      std::cout << "seed at: " << trachea_centre << std::endl;
+    m_Seeds.push_back(trachea_centre);
+  }
   
+  return (m_Seeds.size() != 0);
+}
+//--------------------------------------------------------------------
+
 //--------------------------------------------------------------------
 template <class ImageType>
 void 
@@ -577,7 +802,7 @@ TracheaRegionGrowing()
   f->SetUpper(GetUpperThresholdForTrachea());
   f->SetMinimumLowerThreshold(-2000);
   //  f->SetMaximumUpperThreshold(0); // MAYBE TO CHANGE ???
-  f->SetMaximumUpperThreshold(-800); // MAYBE TO CHANGE ???
+  f->SetMaximumUpperThreshold(-700); // MAYBE TO CHANGE ???
   f->SetAdaptLowerBorder(false);
   f->SetAdaptUpperBorder(true);
   f->SetMinimumSize(5000); 
@@ -642,12 +867,18 @@ SearchForTrachea()
   // compute trachea volume
   // if volume not plausible  -> skip more slices and restart 
 
+  bool has_seed;
   bool stop = false;
   double volume = 0.0;
   int skip = GetNumberOfSlicesToSkipBeforeSearchingSeed();
   while (!stop) {
     stop = true;
-    if (SearchForTracheaSeed(skip)) {
+    if (GetTracheaSeedAlgorithm() == 0)
+      has_seed = SearchForTracheaSeed(skip);
+    else
+      has_seed = SearchForTracheaSeed2(GetNumSlices());
+    
+    if (has_seed) {
       TracheaRegionGrowing();
       volume = ComputeTracheaVolume()/1000; // assume mm3, so divide by 1000 to get cc
       if (GetWriteStepFlag()) {
@@ -656,7 +887,8 @@ SearchForTrachea()
       if (GetTracheaVolumeMustBeCheckedFlag()) {
         if ((volume > 10) && (volume < 65 )) { // depend on image size ...
           // Typical volume 22.59 cm 3 (± 7.69 cm 3 ) [ Leader 2004 ]
-          if (GetVerboseStepFlag()) {
+          if (GetVerboseStepFlag())
+          {
             std::cout << "\t Found trachea with volume " << volume << " cc." << std::endl;
           }
         }
index b40dc28fc49de81bd0a4784fba74158b7dd333f6..b76b365e12ef34dca1af9e8fc50c4e8090b5e1f9 100644 (file)
@@ -89,6 +89,10 @@ SetOptionsFromArgsInfoToFilter(FilterType * f)
   f->SetUpperThresholdForTrachea(mArgsInfo.upperThresholdForTrachea_arg);
   f->SetMultiplierForTrachea(mArgsInfo.multiplierForTrachea_arg);
   f->SetThresholdStepSizeForTrachea(mArgsInfo.thresholdStepSizeForTrachea_arg);
+  f->SetTracheaSeedAlgorithm(mArgsInfo.type_arg);
+  f->SetNumSlices(mArgsInfo.numSlices_arg);
+  f->SetMaxElongation(mArgsInfo.maxElongation_arg);
+  f->SetSeedPreProcessingThreshold(mArgsInfo.seedPreProcessingThreshold_arg);
 
   typename FilterType::InputImageIndexType s;
   if (mArgsInfo.seed_given) {
index 77a3597048a80d59a41975a217d41a4a6475e682..3aa195397fb030f2ca2502f51e65c46ed64b289a 100644 (file)
@@ -3,6 +3,6 @@ package "clitk"
 version "1.2"
 purpose "Extract data from a Dicom wave file to a text file"
 option "config"         -    "config file"                     string  no
-option "InputFile"      i    "Dicom inputfile name"                       string  no
-option "OutputFile"     o   "Text outputfile name"                       string  no
+option "InputFile"      i    "Dicom inputfile name"                       string  yes
+option "OutputFile"     o   "Text outputfile name"                       string  yes
 option "Verbose"        v    "verbose"                         int  no