[Insight-users] Re: Segmentation of the left ventricle

Luis Ibanez luis . ibanez at kitware . com
Thu, 06 Nov 2003 16:23:45 -0500


Hi Christian,

You don't need to use a NormalizeImageFilter for
converting the 8bit image into a float image.

In ITK you can do this directly with the reader.

Simply instantiate the reader using a float image,
like:

typedef itk::Image< float, 3 >     InputImageType;
typedef itk::ImageFileReader<
                        InputImageType
                                  > ReaderType;


The reader will cast your data from uchar to float
during the process of reading the image. The intensity
values of the image will not be changed in this case.

The use of the NormalizeImageFilter certainly may be
affecting the way in which the Sigmoid filter is
applied to your data, since Alpha and Beta are values
associated to the intensity range of the image.

---

Just to be on the safe side, what you could do,
is to save the output of the SigmoidImageFilter
in a file format supporting "float" pixels,
(for example "MetaImage"), and then load this file
in the ImageViewer application available in
InsightApplications.

The parameters of your Sigmoid will be ok when
the output of the sigmod looks like an almost
binary image.

Another option is to add to your program the functionality
for clicking on the image and reading pixel values.
 From these pixel values you could compute the values
of Alpha and Beta.  This last option will be more
ergonomic.


Regards,


    Luis


-----------------------
Christian Dold wrote:
> Hi Luis,
> I forgot something important to tell. In the beginning I am using a 
> normalize filter to transfer the uchar values to float values. These 
> normalize filter change also the values of my original image in widen 
> relative to the brightest spot in the 3D Image. Mabey these cause a 
> problem for the sigmoid filter.
> Is there another way transfer different loading images with 8bit, 12bit 
> values transfer to float images using templates too.
> Here my header and template for the nomalize filter:
> //Header------------------------------------------------------------------------------ 
> 
> #ifndef _MIPNORMALIZEFILTER_H_
> #define _MIPNORMALIZEFILTER_H_
> 
> 
> #include "MIPFilter.h"
> 
> template< class TInputImage, class TOutputImage >
> struct MIPNormalizeFilterWorkaround
> {
>  typedef TInputImage InputImageType;
>  typedef TOutputImage OutputImageType;
> 
>  typedef itk::NormalizeImageFilter<
>      typename InputImageType::ITKImageType,
>      typename OutputImageType::ITKImageType > ITKFilterType;
> };
> 
> 
> /**
> * \brief Subclass of MIPFilter utilizing itk::NormalizeImageFilter.
> */
> template< class TInputImage, class TOutputImage >
> class MIPNormalizeFilter
>    : public MIPFilter< MIPNormalizeFilterWorkaround
>        < TInputImage, TOutputImage > >
> {
> public:
>  typedef MIPNormalizeFilter Self;
>  typedef MIPFilter< MIPNormalizeFilterWorkaround
>      < TInputImage, TOutputImage > > Superclass;
> 
>  typedef boost::shared_ptr< Self > Pointer;
>  typedef boost::shared_ptr< const Self > ConstPointer;
> 
>  /** Creates an object and returns a smart pointer to it. */
>  static Pointer New(MIPFilterChain *pFilterChain);
> 
>  virtual ~MIPNormalizeFilter();
> 
>  // Documentation inherited from parent class
>  const std::string GetFilterName();
> 
> protected:
>  /** Constructor. */
>  MIPNormalizeFilter(MIPFilterChain *pFilterChain);
> 
>  // Documentation inherited from parent class
>  void ConfigureFilter();
> 
>  // Documentation inherited from parent class
>  void ConfigureFilter(int propertyId);
> 
> private:
> 
> };
> 
> #include "MIPNormalizeFilter.tpp"
> 
> #endif
> 
> //Template---------------------------------------------------------------------------------------------------- 
> 
> #ifndef _MIPNORMALIZEFILTER_TPP_
> #define _MIPNORMALIZEFILTER_TPP_
> 
> template< class TInputImage, class TOutputImage >
> typename MIPNormalizeFilter< TInputImage, TOutputImage >::Pointer
> MIPNormalizeFilter< TInputImage, TOutputImage >
> ::New(MIPFilterChain *pFilterChain)
> {
>  return new MIPNormalizeFilter(pFilterChain);
> }
> 
> 
> template< class TInputImage, class TOutputImage >
> MIPNormalizeFilter< TInputImage, TOutputImage >
> ::MIPNormalizeFilter(MIPFilterChain *pFilterChain)
> : Superclass(pFilterChain)
> {
> }
> 
> template< class TInputImage, class TOutputImage >
> MIPNormalizeFilter< TInputImage, TOutputImage >
> ::~MIPNormalizeFilter()
> {
> }
> 
> template< class TInputImage, class TOutputImage >
> const std::string
> MIPNormalizeFilter< TInputImage, TOutputImage >
> ::GetFilterName()
> {
>  return "Normalize";
> }
> 
> template< class TInputImage, class TOutputImage >
> void
> MIPNormalizeFilter< TInputImage, TOutputImage >
> ::ConfigureFilter()
> {
>  // Do nothing since this filter takes no parameters
> }
> 
> template< class TInputImage, class TOutputImage >
> void
> MIPNormalizeFilter< TInputImage, TOutputImage >
> ::ConfigureFilter(int propertyId)
> {
>  // Do nothing since this filter takes no parameters
> }
> 
> #endif
> 
> 
> Luis Ibanez wrote:
> 
>>
>>
>> Hi Christian,
>>
>>
>> The general process that you are using for segmenting
>> the ventricle looks fine. I'm assuming that you are
>> after the region containing blood inside the left
>> ventricle, is that correct ?
>>
>> - The Step of curvature anisotropic diffusion
>>   seems to be ok. You have to visually verify
>>   if the image looks fine after the smoothing.
>>   There are no rules about how much iterations
>>   of smoothing to apply. You simple look for
>>   a balance between reducing noise and avoiding
>>   to degrade the edges of the structure that
>>   you want to segment.
>>
>> - The GradientMagnitudeRecursiveGaussian with
>>   sigma = 1, should be ok *IFF* your images have
>>   the spacing correctly defined in millimeters.
>>
>> - The Alpha and Beta values for the sigmoid
>>   are probably the source of the problem.
>>   These values are critical in the preparation
>>   of the speed image to be passed to the
>>   LevelSet segmentation filter.
>>
>>   Alpha = -0.5 and Beta = 1.0 are nominal default
>>   values. It is unlikely that they will work for
>>   your images.  Instructions on how to tune these
>>   values to your image are described in the
>>   SoftwareGuide
>>
>>   http://www . itk . org/ItkSoftwareGuide . pdf
>>
>>   The sigmoid filter itself is described in
>>   section  6.2.2, pdf-page 149, paper-page 175
>>
>>   The tunning of Alpha and Beta, for using
>>   the sigmoid as preprocessing for level sets
>>   is described in section 9.3.3, pdf-page 380,
>>   paper-page 406.
>>
>>   Please look at the output of your Sigmoid
>>   filter. The image should have an appearance
>>   similar to the one illustrated in Figure 9.16
>>   in pdf-page 381.  That is, it should look almost
>>   binarized, with dark regions on the edges of the
>>   structure that you want to segment, and bright
>>   values on the inside of the region to be segmented.
>>   Any bright spots along the edge of the structure
>>   to be segmented will result in leaks for the
>>   level set.
>>
>>   The rule of thumb used for estimating Alpha and
>>   Beta is described in pdf-page 369.
>>
>>   The fact that the level set is leaking in your
>>   example, is an indication that the alpha and beta
>>   parameters are not tunned correctly.
>>
>>
>> - Don't bother to run the Geodesic Active Contours
>>   until you fine tune the output of the Sigmoid
>>   filter to be a nice looking image.  This image,
>>   is all what the LevelSet filter sees. Your segmentation
>>   will be as good or as bad as the feature image that
>>   you prepare.
>>
>>
>> - It wasn't clear for me if you were applying these
>>   process in 3D or 2D. It should work in both ways.
>>   Once you figure out the parameters, it should be
>>   simple to run this process in 3D.
>>
>>
>> - BTW Thanks for sending such detailed description
>>   of your process. Attaching the images illustrating
>>   your results is a big help for figuring out the
>>   potential source of the problem.
>>
>>
>> -----
>>
>>
>> You may find useful to play with the application:
>> GeodesicActiveContour, available in:
>>
>>    InsightApplications/GeodesicActiveContour
>>
>> or with the free version of VolView and the ITK
>> plugin for GeodesicActiveContours. You can download
>> VolView from:
>>
>>     http://www . kitware . com/products/volview . html
>>     http://www . kitware . com/KitwareScripts/download . cgi/volview
>>
>>
>>
>> Please let us know if you have further question.
>>
>>
>>
>>  Thanks
>>
>>
>>    Luis
>>
>>
>>
>> ------------------------------
>> Christian Dold wrote:
>>
>>> Hello Mr. Ibanez
>>> I am using ITK since a couple month to segment the left ventricle of 
>>> the heart, but the results are not satisfactory.
>>> The steps I doing with the MRI Images 16 Slices 256x256 resolution:
>>> Curvature Anisotropic diffusion #of Iterations:10, Time step:0.1, 
>>> Conductance Parameter: 0.5
>>> Gradient Magnitude Recursive Gaussian with Sigma: 1
>>> Sigmoid: Alpha: -0.5 Beta: 1
>>> Fast Marching: Initial Distance:4 Speed constant:1
>>> Geodesic Active Contour: Propagation- Curvature- Advection scaling: 1 
>>> Maximum RMS: 0.02 Maximum Iterations:800
>>> Binary Treshold: Lower-Upper Value: -1000/0 Outside-Inside Value:0/255
>>>
>>> The result you can see in the attached images. Further the seed 
>>> points are in the text file.
>>>
>>> I also implemented the Region Growing but no good results.
>>> Thanks for your suggestions
>>> Christian
>>>
>>>
>>> ------------------------------------------------------------------------
>>>
>>>
>>> ------------------------------------------------------------------------
>>>
>>>
>>> ------------------------------------------------------------------------
>>>
>>
>>
>>
>>
>