]> Creatis software - clitk.git/commitdiff
constraint a binary image to be at a relative position of an object
authordsarrut <dsarrut>
Wed, 30 Jun 2010 06:01:31 +0000 (06:01 +0000)
committerdsarrut <dsarrut>
Wed, 30 Jun 2010 06:01:31 +0000 (06:01 +0000)
itk/clitkAddRelativePositionConstraintToLabelImageFilter.h [new file with mode: 0644]
itk/clitkAddRelativePositionConstraintToLabelImageFilter.txx [new file with mode: 0644]

diff --git a/itk/clitkAddRelativePositionConstraintToLabelImageFilter.h b/itk/clitkAddRelativePositionConstraintToLabelImageFilter.h
new file mode 100644 (file)
index 0000000..83400ef
--- /dev/null
@@ -0,0 +1,146 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://oncora1.lyon.fnclcc.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+  ======================================================================-====*/
+
+#ifndef CLITKADDRELATIVEPOSITIONCONSTRAINTTOLABELIMAGEFILTER_H
+#define CLITKADDRELATIVEPOSITIONCONSTRAINTTOLABELIMAGEFILTER_H
+
+// clitk
+#include "clitkFilterBase.h"
+
+// itk
+#include "itkPasteImageFilter.h"
+
+// itk ENST
+#include "RelativePositionPropImageFilter.h"
+
+namespace clitk {
+  
+  //--------------------------------------------------------------------
+  /*
+    Let A be an initial label image.
+    Let B be a label image with an object. 
+    Let o be an orientation relatively to the B object (for example RightTo, AntTo, InferiorTo ...)
+
+    This filter removes (=set background) from A all points that are
+    not in the wanted o orientation. It uses downsampled version for
+    faster processing, and (try to) take into account discretization
+    problem. Uses [Bloch 1999].
+  */
+  //--------------------------------------------------------------------
+  
+  template <class TImageType>
+  class ITK_EXPORT AddRelativePositionConstraintToLabelImageFilter:
+    public clitk::FilterBase, 
+    public itk::ImageToImageFilter<TImageType, TImageType> 
+  {
+
+  public:
+    /** Standard class typedefs. */
+    typedef itk::ImageToImageFilter<TImageType, TImageType> Superclass;
+    typedef AddRelativePositionConstraintToLabelImageFilter    Self;
+    typedef itk::SmartPointer<Self>                            Pointer;
+    typedef itk::SmartPointer<const Self>                      ConstPointer;
+       
+    /** Method for creation through the object factory. */
+    itkNewMacro(Self);
+    
+    /** Run-time type information (and related methods). */
+    itkTypeMacro(AddRelativePositionConstraintToLabelImageFilter, ImageToImageFilter);
+    FILTERBASE_INIT;
+
+    /** Some convenient typedefs. */
+    typedef TImageType                      ImageType;
+    typedef typename ImageType::ConstPointer ImageConstPointer;
+    typedef typename ImageType::Pointer      ImagePointer;
+    typedef typename ImageType::RegionType   RegionType; 
+    typedef typename ImageType::PixelType    PixelType;
+    typedef typename ImageType::SpacingType  SpacingType;
+    typedef typename ImageType::SizeType     SizeType;
+    
+    /** ImageDimension constants */
+    itkStaticConstMacro(ImageDimension, unsigned int, TImageType::ImageDimension);
+    typedef itk::Image<float, ImageDimension> FloatImageType;
+
+    /** Orientation types */
+    typedef enum { RightTo = 0, LeftTo = 1,
+                   AntTo = 2,   PostTo = 3, 
+                   InfTo = 4,   SupTo = 5, Angle = 6
+    } OrientationTypeEnumeration;
+
+    /** Input : initial image and object */
+    void SetInput(const ImageType * image);
+    void SetInputObject(const ImageType * image);
+    
+    // Options
+    void SetOrientationType(OrientationTypeEnumeration orientation);
+    itkGetConstMacro(OrientationType, OrientationTypeEnumeration);
+    void SetAngle1(double a);
+    void SetAngle2(double a);
+    itkGetConstMacro(Angle1, double);
+    itkGetConstMacro(Angle2, double);
+    itkGetConstMacro(ResampleBeforeRelativePositionFilter, bool);
+    itkSetMacro(ResampleBeforeRelativePositionFilter, bool);
+    itkBooleanMacro(ResampleBeforeRelativePositionFilter);
+    itkGetConstMacro(IntermediateSpacing, double);
+    itkSetMacro(IntermediateSpacing, double);
+    itkGetConstMacro(FuzzyThreshold, double);
+    itkSetMacro(FuzzyThreshold, double);
+    itkGetConstMacro(BackgroundValue, PixelType);
+    itkSetMacro(BackgroundValue, PixelType);
+    itkGetConstMacro(ObjectBackgroundValue, PixelType);
+    itkSetMacro(ObjectBackgroundValue, PixelType);
+
+  protected:
+    AddRelativePositionConstraintToLabelImageFilter();
+    virtual ~AddRelativePositionConstraintToLabelImageFilter() {}
+    
+    OrientationTypeEnumeration m_OrientationType;
+    double m_IntermediateSpacing;
+    double m_FuzzyThreshold;
+    PixelType m_BackgroundValue;
+    PixelType m_ObjectBackgroundValue;
+    double m_Angle1;
+    double m_Angle2;
+    bool m_ResampleBeforeRelativePositionFilter;
+
+    virtual void GenerateOutputInformation();
+    virtual void GenerateInputRequestedRegion();
+    virtual void GenerateData();
+
+    typedef itk::PasteImageFilter<ImageType,ImageType> PadFilterType;
+    typename ImageType::Pointer working_image;
+    typename ImageType::Pointer object_resampled;
+    typename FloatImageType::Pointer relPos;
+    ImagePointer input;
+    ImagePointer object;
+
+  private:
+    AddRelativePositionConstraintToLabelImageFilter(const Self&); //purposely not implemented
+    void operator=(const Self&); //purposely not implemented
+    
+  }; // end class
+  //--------------------------------------------------------------------
+
+} // end namespace clitk
+//--------------------------------------------------------------------
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkAddRelativePositionConstraintToLabelImageFilter.txx"
+#endif
+
+#endif
diff --git a/itk/clitkAddRelativePositionConstraintToLabelImageFilter.txx b/itk/clitkAddRelativePositionConstraintToLabelImageFilter.txx
new file mode 100644 (file)
index 0000000..4078b84
--- /dev/null
@@ -0,0 +1,358 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://oncora1.lyon.fnclcc.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+  ======================================================================-====*/
+
+// clitk
+#include "clitkCommon.h"
+#include "clitkBooleanOperatorLabelImageFilter.h"
+#include "clitkAutoCropFilter.h"
+#include "clitkResampleImageWithOptionsFilter.h"
+#include "clitkBooleanOperatorLabelImageFilter.h"
+
+// itk
+#include <deque>
+#include "itkStatisticsLabelMapFilter.h"
+#include "itkLabelImageToStatisticsLabelMapFilter.h"
+#include "itkRegionOfInterestImageFilter.h"
+#include "itkBinaryThresholdImageFilter.h"
+#include "itkBinaryErodeImageFilter.h"
+#include "itkBinaryBallStructuringElement.h"
+
+// itk [Bloch et al] 
+#include "RelativePositionPropImageFilter.h"
+
+//--------------------------------------------------------------------
+template <class TImageType>
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+AddRelativePositionConstraintToLabelImageFilter():
+  clitk::FilterBase(),
+  itk::ImageToImageFilter<TImageType, TImageType>()
+{
+  this->SetNumberOfRequiredInputs(2);
+  SetFuzzyThreshold(0.6);
+  SetBackgroundValue(0);
+  SetObjectBackgroundValue(0);
+  SetOrientationType(LeftTo);
+  ResampleBeforeRelativePositionFilterOn();
+  SetIntermediateSpacing(10);
+
+  // Step 1 : resample (option=sampling)
+  // Step 2 : Pad (no)
+  // Step 3 : relative position  (option = angle)
+  // Step 4 : Threshold
+  // Step 5 : Erode for boundary
+  // Step 6 : resample to initial spacing
+  // Step 7 : pad if not the same size : it can occur when downsample and upsample
+  // Step 6: combine input+thresholded relpos
+  // Step 7: autocrop
+
+  // Step 1 : resample
+  ResampleBeforeRelativePositionFilterOn();
+  SetIntermediateSpacing(10);
+  SetOrientationType(LeftTo);
+  // SegmentationStep * step = new SegmentationStep;
+  //   step->SetName("Initial resampling and relative position map");
+  //   SetStep(step, &Self::ResampleAndRelativePositionMap);
+
+  // Step 3 : threshold + postprocess
+  // SetFuzzyThreshold(0.4);
+  //  step = new SegmentationStep;
+  //   step->SetName("Map threshold and post-processing");
+  //   SetStep(step, &Self::MapThresholdAndPostProcessing);
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+SetInput(const ImageType * image) {
+  // Process object is not const-correct so the const casting is required.
+  this->SetNthInput(0, const_cast<ImageType *>(image));
+}
+//--------------------------------------------------------------------
+  
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+SetInputObject(const ImageType * image) {
+  // Process object is not const-correct so the const casting is required.
+  this->SetNthInput(1, const_cast<ImageType *>(image));
+}
+//--------------------------------------------------------------------
+  
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+GenerateOutputInformation() { 
+  ImagePointer input = dynamic_cast<TImageType*>(itk::ProcessObject::GetInput(0));
+  ImagePointer outputImage = this->GetOutput(0);
+  outputImage->SetRegions(outputImage->GetLargestPossibleRegion());
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+GenerateInputRequestedRegion() {
+  // Call default
+  itk::ImageToImageFilter<TImageType, TImageType>::GenerateInputRequestedRegion();
+  // Get input pointers and set requested region to common region
+  ImagePointer input1 = dynamic_cast<TImageType*>(itk::ProcessObject::GetInput(0));
+  ImagePointer input2 = dynamic_cast<TImageType*>(itk::ProcessObject::GetInput(1));
+  input1->SetRequestedRegion(input1->GetLargestPossibleRegion());
+  input2->SetRequestedRegion(input2->GetLargestPossibleRegion());
+}
+//--------------------------------------------------------------------
+
+  
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+SetAngle1(double a) {
+  SetOrientationType(Angle);
+  m_Angle1 = a;
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+SetAngle2(double a) {
+  SetOrientationType(Angle);
+  m_Angle2 = a;
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+SetOrientationType(OrientationTypeEnumeration orientation) {
+  m_OrientationType = orientation;
+  switch (m_OrientationType) {
+  case LeftTo:   m_Angle1 = clitk::deg2rad(0);   m_Angle2 = clitk::deg2rad(0);   break;
+  case RightTo:  m_Angle1 = clitk::deg2rad(180); m_Angle2 = clitk::deg2rad(0);   break;
+  case AntTo:    m_Angle1 = clitk::deg2rad(90);  m_Angle2 = clitk::deg2rad(0);   break;
+  case PostTo:   m_Angle1 = clitk::deg2rad(-90); m_Angle2 = clitk::deg2rad(0);   break;
+  case InfTo:    m_Angle1 = clitk::deg2rad(0);   m_Angle2 = clitk::deg2rad(90);  break;
+  case SupTo:    m_Angle1 = clitk::deg2rad(0);   m_Angle2 = clitk::deg2rad(-90); break;
+  case Angle:  break;      
+  }
+  /*         A1   A2
+             Left      0    0
+             Right   180    0
+             Ant      90    0
+             Post    -90    0
+             Inf       0   90
+             Sup       0  -90
+  */
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void 
+clitk::AddRelativePositionConstraintToLabelImageFilter<TImageType>::
+GenerateData() {
+  // Get input pointer
+  input = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(0));
+  object = dynamic_cast<ImageType*>(itk::ProcessObject::GetInput(1));
+
+  //--------------------------------------------------------------------
+  //--------------------------------------------------------------------
+  StartNewStep("Resample And Relative Position Map");
+  
+  static const unsigned int dim = ImageType::ImageDimension;
+  // Step 1 : resample
+  if (m_ResampleBeforeRelativePositionFilter) {
+    typedef clitk::ResampleImageWithOptionsFilter<ImageType> ResampleFilterType;
+    typename ResampleFilterType::Pointer resampleFilter = ResampleFilterType::New();
+    resampleFilter->SetInput(object);
+    resampleFilter->SetOutputIsoSpacing(m_IntermediateSpacing);
+    resampleFilter->SetGaussianFilteringEnabled(false);
+    // resampleFilter->SetVerboseOptions(true);
+    resampleFilter->Update();
+    working_image = resampleFilter->GetOutput();
+  }
+  else {
+    working_image = object;
+  }    
+  // writeImage<ImageType>(working_image, "res.mhd");
+
+  // Step 2: object pad to input image -> we want to compute the
+  // relative position for each point belonging to the input image
+  // domain, so we have to extend (pad) the object image to fit the
+  // domain size
+  typename ImageType::Pointer output = ImageType::New();
+  SizeType size;
+  for(unsigned int i=0; i<dim; i++) {
+    size[i] = lrint((input->GetLargestPossibleRegion().GetSize()[i]*input->GetSpacing()[i])/(double)working_image->GetSpacing()[i]);
+  }
+  RegionType region;
+  region.SetSize(size);
+  // output->SetLargestPossibleRegion(region);
+  output->SetRegions(region);
+  output->SetSpacing(working_image->GetSpacing());
+  output->SetOrigin(input->GetOrigin());
+  output->Allocate();
+  output->FillBuffer(m_BackgroundValue);
+  typename PadFilterType::Pointer padFilter = PadFilterType::New();
+  typename PadFilterType::InputImageIndexType index;
+  for(unsigned int i=0; i<dim; i++) {
+    index[i] = lrint((working_image->GetOrigin()[i] - input->GetOrigin()[i])/(double)m_IntermediateSpacing);
+  }
+  padFilter->SetSourceImage(working_image);
+  padFilter->SetDestinationImage(output);
+  padFilter->SetDestinationIndex(index);
+  padFilter->SetSourceRegion(working_image->GetLargestPossibleRegion());
+  padFilter->Update();
+  working_image = padFilter->GetOutput();
+
+  // Keep object image (with resampline and pad)
+  object_resampled = working_image;
+ //  writeImage<ImageType>(working_image, "pad.mhd");
+
+  // Step 3: compute rel pos in object
+  typedef itk::RelativePositionPropImageFilter<ImageType, FloatImageType> RelPosFilterType;
+  typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
+  relPosFilter->SetInput(working_image);
+  relPosFilter->SetAlpha1(m_Angle1); // xy plane
+  relPosFilter->SetAlpha2(m_Angle2);
+  relPosFilter->SetK1(M_PI/2.0); // Opening parameter, default = pi/2
+  relPosFilter->SetFast(true);
+  relPosFilter->SetRadius(1); // seems sufficient in this cas
+  // relPosFilter->SetVerboseProgress(true);
+  relPosFilter->Update();
+  relPos = relPosFilter->GetOutput();
+  this->template StopCurrentStep<FloatImageType>(relPos);
+               
+  //--------------------------------------------------------------------
+  //--------------------------------------------------------------------
+  StartNewStep("Map Threshold And Post Processing");
+
+  // Step 1: threshold
+  typedef itk::BinaryThresholdImageFilter<FloatImageType, ImageType> BinaryThresholdImageFilterType;
+  typename BinaryThresholdImageFilterType::Pointer thresholdFilter = BinaryThresholdImageFilterType::New();
+  thresholdFilter->SetInput(relPos);
+  thresholdFilter->SetOutsideValue(m_BackgroundValue);
+  thresholdFilter->SetInsideValue(m_BackgroundValue+1);
+  thresholdFilter->SetLowerThreshold(m_FuzzyThreshold);
+  thresholdFilter->Update();
+  working_image = thresholdFilter->GetOutput();
+
+  // Step 2 : erosion with initial mask to exclude pixels that were
+  // inside the resampled version and outside the original mask
+  typedef itk::BinaryBallStructuringElement<unsigned int, 3> StructuringElementType; 
+  StructuringElementType kernel;
+  kernel.SetRadius(1);
+  kernel.CreateStructuringElement();
+  typedef itk::BinaryErodeImageFilter<ImageType, ImageType, StructuringElementType> ErodeFilterType;
+  typename ErodeFilterType::Pointer erodeFilter = ErodeFilterType::New();
+  erodeFilter->SetInput(working_image);
+  erodeFilter->SetKernel(kernel);
+  erodeFilter->SetBackgroundValue(m_BackgroundValue);
+  erodeFilter->SetErodeValue(m_BackgroundValue+1);
+  erodeFilter->Update();
+  working_image = erodeFilter->GetOutput();
+
+  // Step 5: resample to initial spacing
+  if (m_ResampleBeforeRelativePositionFilter) {
+    typedef clitk::ResampleImageWithOptionsFilter<ImageType> ResampleFilterType;
+    typename ResampleFilterType::Pointer resampleFilter = ResampleFilterType::New();
+    resampleFilter->SetDefaultPixelValue(m_BackgroundValue);
+    resampleFilter->SetInput(working_image);
+    resampleFilter->SetOutputSpacing(input->GetSpacing());
+    resampleFilter->SetGaussianFilteringEnabled(false);
+    // resampleFilter->SetVerboseOptions(true);
+    resampleFilter->Update();
+    working_image = resampleFilter->GetOutput();
+  }
+
+  // writeImage<ImageType>(working_image, "resinitial.mhd");
+
+  // Pre Step 6: pad if not the same size : it can occur when downsample and upsample
+  if (working_image->GetLargestPossibleRegion() != input->GetLargestPossibleRegion()) {
+    typename ImageType::Pointer temp = ImageType::New();
+    temp->CopyInformation(input);
+    temp->SetRegions(input->GetLargestPossibleRegion()); // Do not forget !!
+    temp->Allocate();
+    temp->FillBuffer(m_BackgroundValue);
+    typename PadFilterType::Pointer padFilter2 = PadFilterType::New(); // if yes : redo relpos
+    padFilter2->SetSourceImage(working_image);
+    padFilter2->SetDestinationImage(temp);
+    padFilter2->SetDestinationIndex(input->GetLargestPossibleRegion().GetIndex());
+    padFilter2->SetSourceRegion(working_image->GetLargestPossibleRegion());
+    padFilter2->Update();
+    working_image = padFilter2->GetOutput();
+  }
+  // writeImage<ImageType>(working_image, "pad2.mhd");
+
+  // Step 6: combine input+thresholded relpos
+  typedef clitk::BooleanOperatorLabelImageFilter<ImageType> BoolFilterType;
+  typename BoolFilterType::Pointer combineFilter = BoolFilterType::New();
+  combineFilter->SetBackgroundValue(m_BackgroundValue);
+  combineFilter->SetBackgroundValue1(m_BackgroundValue);
+  combineFilter->SetBackgroundValue2(m_BackgroundValue);
+  combineFilter->SetForegroundValue(m_BackgroundValue+1);
+  combineFilter->SetInput1(input);
+  combineFilter->SetInput2(working_image);
+  combineFilter->SetOperationType(BoolFilterType::And);
+  combineFilter->InPlaceOn();
+  combineFilter->Update(); 
+  working_image = combineFilter->GetOutput();
+  combineFilter = BoolFilterType::New();
+  combineFilter->SetInput1(working_image);
+  combineFilter->SetInput2(object);
+  combineFilter->SetOperationType(BoolFilterType::AndNot);
+  combineFilter->InPlaceOn();
+  combineFilter->Update(); 
+
+  working_image = combineFilter->GetOutput();
+  // writeImage<ImageType>(working_image, "combine.mhd");
+
+  // Step 7: autocrop
+  typedef clitk::AutoCropFilter<ImageType> CropFilterType;
+  typename CropFilterType::Pointer cropFilter = CropFilterType::New();
+  cropFilter->SetInput(working_image);
+  cropFilter->ReleaseDataFlagOff();
+  cropFilter->Update();   
+  working_image = cropFilter->GetOutput();
+  this->template StopCurrentStep<ImageType>(working_image);
+
+  //--------------------------------------------------------------------
+  //--------------------------------------------------------------------
+  
+  // Final Step -> set output
+  this->SetNthOutput(0, working_image);
+  return;
+}
+//--------------------------------------------------------------------
+