[Insight-users] Still struggling with implementing grayscale morphological operators

Neal R. Harvey harve at lanl.gov
Tue Jul 28 18:40:34 EDT 2009


I am still struggling to implement grayscale morphological operators.
The basic functions have been written:
e.g. 
http://www.itk.org/Doxygen310/html/classitk_1_1GrayscaleFunctionDilateImageFilter.html
Unfortunately there are no nice papers or tutorials, or even test code 
to provide insight as to exactly
how to go about using them.

It appears that they use a neighborhood kernel and I haven't been able 
to figure out how to create a
grayscale kernel. I have gone through the book - the part that describes 
neighborhood iterators, but
so far that hasn't been a great deal of help. The examples they provide 
don't exactly show how to
assign "grayscale" values to your kernel neighborhood iterator. The 
examples are for sobel and gaussian
kernels, where there is code in the background that calculates the 
values for you. You never see the details
of how it is used. The other examples they show in the book are for 
binary morphology, but that doesn't
require graylevel values kernels, so also isn't much use.

Basically, I think I need to be able to read in an input image that is 
to be processed and two images that
define the grayscale structuring element (SE), and output a processed 
image. One of the SE images
defines the region of support of the SE (i.e. anywhere where the values 
are greater than 0 are in the
SE's region of support). The other SE image defines the grayscale values 
in the SE. I then have to use
these two SE images to create a suitable kernel.

Below is the code that I have written so far, in attempting to write my 
own Grayscale Morphological
Operator based on the examples provided in the book. As you can see, 
it's not working and I am not
sure how to solve that problem.

If anyone has any suggestions or ideas of how to get this working, or 
where to look to get information
that could lead in that direction, it would be very much appreciated.

Kindest regards

Harve

#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#ifdef __BORLANDC__
#define ITK_LEAN_AND_MEAN
#endif

#include <iostream>
#include <string>
#include <sstream>

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkTIFFImageIO.h"
#include "itkNumericTraits.h"
#include "itkMinimumMaximumImageFilter.h"
#include "itkConstNeighborhoodIterator.h"
#include "itkConstShapedNeighborhoodIterator.h"
#include "itkNeighborhoodAlgorithm.h"
#include "itkImageRegionIterator.h"

using namespace std;

string itos(int i)    // convert int to string
{
  stringstream s;
  s << i;
  return s.str();
}

int main( int argc, char * argv[] )
{

  // Verify the number of parameters in the command line
  if( argc < 5 )
    {
      std::cerr << "Usage: " << argv[0];
      std::cerr << " inputImage inputSEValuesImage inputSEExtentImage 
outputImage ";
      return EXIT_FAILURE;
    }


  //  Then, as usual, we select the pixel types and the image
  //  dimension. Remember, if the file format represents pixels with a
  //  particular type, C-style casting will be performed to convert the 
data.

  typedef unsigned char       InputPixelType;
  typedef unsigned int        InputSEValuesPixelType;
  typedef int                 InputSEExtentPixelType;
  typedef unsigned char       OutputPixelType;
  typedef float               FloatPixelType;
  const   unsigned int        Dimension = 2;

  typedef itk::Image< InputPixelType,  Dimension >         InputImageType;
  typedef itk::Image< InputSEValuesPixelType, Dimension >  
InputSEValuesImageType;
  typedef itk::Image< InputSEExtentPixelType, Dimension >  
InputSEExtentImageType;
  typedef itk::Image< OutputPixelType, Dimension >         OutputImageType;
  typedef itk::Image< FloatPixelType, Dimension >          FloatImageType;

  typedef itk::ConstShapedNeighborhoodIterator< InputImageType > 
ShapedNeighborhoodIteratorType;

  typedef itk::ImageRegionIterator< InputImageType > IteratorType;

  //  Below we instantiate the MinimumMaximumImageCalculator class

  typedef itk::MinimumMaximumImageFilter< InputImageType > 
MinimumMaximumInputImageFilterType;

  typedef itk::MinimumMaximumImageFilter< InputSEValuesImageType > 
MinimumMaximumSEValuesImageFilterType;

  typedef itk::MinimumMaximumImageFilter< InputSEExtentImageType > 
MinimumMaximumSEExtentImageFilterType;

  //  We can now instantiate the reader and writer. These two classes are
  //  parameterized over the image type. We instantiate the
  //  TIFFImageIO class as well. Note that the ImageIO objects are
  //  not templated.

  typedef itk::ImageFileReader< InputImageType  >          
InputImageReaderType;

  typedef itk::ImageFileReader< InputSEValuesImageType  >   
InputSEValuesImageReaderType;

  typedef itk::ImageFileReader< InputSEExtentImageType  >  
InputSEExtentImageReaderType;

  typedef itk::ImageFileWriter< OutputImageType >          
OutputImageWriterType;

  typedef itk::TIFFImageIO                                 ImageIOType;

  //  Then, we create one object of each type using the New() method and
  //  assigning the result to a SmartPointer.

  InputImageReaderType::Pointer InputImageReader = 
InputImageReaderType::New();

  InputSEValuesImageReaderType::Pointer InputSEValuesImageReader = 
InputSEValuesImageReaderType::New();

  InputSEExtentImageReaderType::Pointer InputSEExtentImageReader = 
InputSEExtentImageReaderType::New();

  //  A MinMaxImageFilter object is constructed

  MinimumMaximumInputImageFilterType::Pointer 
MinimumMaximumInputImageFilter = MinimumMaximumInputImageFilterType::New();

  MinimumMaximumSEValuesImageFilterType::Pointer 
MinimumMaximumSEValuesImageFilter = 
MinimumMaximumSEValuesImageFilterType::New();

  MinimumMaximumSEExtentImageFilterType::Pointer 
MinimumMaximumSEExtentImageFilter = 
MinimumMaximumSEExtentImageFilterType::New();

  OutputImageWriterType::Pointer OutputImageWriter = 
OutputImageWriterType::New();
  ImageIOType::Pointer tiffIO = ImageIOType::New();

  //
  // Here we recover the file names from the command line arguments
  //
  const char * inputImageFileName         = argv[1];
  const char * inputSEValuesImageFileName = argv[2];
  const char * inputSEExtentImageFileName = argv[3];
  const char * outputImageFileName        = argv[4];

  //  The name of the file to be read or written is passed with the
  //  SetFileName() method.

  InputImageReader->SetFileName( inputImageFileName );

  InputImageReader->Update();

  InputSEValuesImageReader->SetFileName( inputSEValuesImageFileName );

  InputSEValuesImageReader->Update();

  InputSEExtentImageReader->SetFileName( inputSEExtentImageFileName );

  InputSEExtentImageReader->Update();

  MinimumMaximumInputImageFilter->SetInput( InputImageReader->GetOutput() );

  MinimumMaximumInputImageFilter->Update();

  InputPixelType imageMinVal = MinimumMaximumInputImageFilter->GetMinimum();
  InputPixelType imageMaxVal = MinimumMaximumInputImageFilter->GetMaximum();

  std::cerr << "Maximum Value from Input Image = " << 
static_cast<int>(imageMaxVal) << std::endl;
  std::cerr << "Minimum Value from Input Image = " << 
static_cast<int>(imageMinVal) << std::endl;

  MinimumMaximumSEValuesImageFilter->SetInput( 
InputSEValuesImageReader->GetOutput() );

  MinimumMaximumSEValuesImageFilter->Update();

  InputSEValuesPixelType SEValuesMinVal = 
MinimumMaximumSEValuesImageFilter->GetMinimum();
  InputSEValuesPixelType SEValuesMaxVal = 
MinimumMaximumSEValuesImageFilter->GetMaximum();

  std::cerr << "Maximum Value from Input SE Values Image = " << 
static_cast<int>(SEValuesMaxVal) << std::endl;
  std::cerr << "Minimum Value from Input SE Values Image = " << 
static_cast<int>(SEValuesMinVal) << std::endl; 

  MinimumMaximumSEExtentImageFilter->SetInput( 
InputSEExtentImageReader->GetOutput() );

  MinimumMaximumSEExtentImageFilter->Update();

  InputSEExtentPixelType SEExtentMinVal = 
MinimumMaximumSEExtentImageFilter->GetMinimum();
  InputSEExtentPixelType SEExtentMaxVal = 
MinimumMaximumSEExtentImageFilter->GetMaximum();

  std::cerr << "Maximum Value from Input SE Extent Image = " << 
static_cast<int>(SEExtentMaxVal) << std::endl;
  std::cerr << "Minimum Value from Input SE Extent Image = " << 
static_cast<int>(SEExtentMinVal) << std::endl; 

  long int inputImageCols = 
InputImageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];

  long int inputImageRows = 
InputImageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];

  std::cout << "Number of Columns for Input Image = " << inputImageCols 
<< "; Number of Rows for Input Image = " << inputImageRows << std::endl;

  long int inputSEValuesImageCols = 
InputSEValuesImageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];

  long int inputSEValuesImageRows = 
InputSEValuesImageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];

  std::cout << "Number of Columns for Input SE Values Image = " << 
inputSEValuesImageCols << "; Number of Rows for Input SE Values Image = 
" << inputSEValuesImageRows << std::endl;

  long int inputSEExtentImageCols = 
InputSEExtentImageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];

  long int inputSEExtentImageRows = 
InputSEExtentImageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];

  std::cout << "Number of Columns for Input SE Extent Image = " << 
inputSEExtentImageCols << "; Number of Rows for Input SE Extent Image = 
" << inputSEExtentImageRows << std::endl;

  // Check that the dimensions of the inputSEValuesImage and 
inputSEExtentImage are the same

  if ((inputSEValuesImageCols != inputSEExtentImageCols) || 
(inputSEValuesImageRows != inputSEExtentImageRows)) {

    std::cout << "inputSEValuesImage and inputSEExtentImage do not have 
the same dimensions - EXITING!" << std::endl;
    return EXIT_FAILURE;

  }

  // OK, so now we have read in the input image, an image containing the 
greyscale values for the Structuring Element
  // and an image containing 1s and 0s defining the region of support of 
the S.E.

  // First - calculate the SE radius from the size of the Input SE image

  // Which is the larger of the two dimensions

  long int SE_size = ((inputSEExtentImageCols > inputSEExtentImageRows) 
? inputSEExtentImageCols : inputSEExtentImageRows);

  std::cout << "SE Size = " << SE_size << std::endl;

  // Is this dimension an odd or even number

  bool even = ((SE_size % 2) == 0) ? true : false;

  string response = (even == true) ? "even" : "odd";

  std::cout << "SE dimension is " << response << std::endl;

  long int SE_radius = (SE_size / static_cast<long int>(2));

  std::cout << "SE radius is " << SE_radius << std::endl;

  // Now we need to do stuff for the iterator

  ShapedNeighborhoodIteratorType::RadiusType radius;
  radius.Fill(SE_radius);

  OutputImageType::Pointer OutputImage = OutputImageType::New();
  OutputImage->SetRegions( 
InputImageReader->GetOutput()->GetRequestedRegion());
  OutputImage->Allocate();

  typedef itk::NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< 
InputImageType > FaceCalculatorType;

  FaceCalculatorType faceCalculator;
  FaceCalculatorType::FaceListType faceList;
  FaceCalculatorType::FaceListType::iterator fit;
 
  faceList = faceCalculator( InputImageReader->GetOutput(),
                             OutputImage->GetRequestedRegion(),
                             radius );

  IteratorType out;

  InputSEExtentImageType::IndexType pixelIndex;

  InputSEExtentPixelType::IndexType SEExtentPixelValue;

  InputPixelType InputPixelValue;

  // Looping through each of the regions determined by faceCalculator
  for ( fit=faceList.begin(); fit != faceList.end(); fit++) {

    ShapedNeighborhoodIteratorType InputIterator( radius, 
InputImageReader->GetOutput(), *fit );

    out = IteratorType ( outputImage, *fit );

    // Defining what the offsets are for the InputIterator

    // However, how does one set the "grayscale" values of the iterator 
kernel?

    for (register int j = -SE_radius; j <= SE_radius; j++) {
      for (register int i = -SE_radius; i <= SE_radius; i++) {

    ShapedNeighborhoodIteratorType::OffsetType off;

    pixelIndex[0] = (i + SE_radius); // Set the pixelIndex x/column position
    pixelIndex[1] = (j + SE_radius); // Set the pixelIndex y/row position

    SEExtentPixelValue = 
InputSEExtentImageReader->GetOutput()->GetPixel( pixelIndex );

    if (SEExtentPixelValue == static_cast<InputSEExtentPixelType>(1)) {

      off[0] = static_cast<int>(i);
      off[1] = static_cast<int>(j);

      InputIterator.ActivateOffset(off);
    }

      }
    }

    // Implements GrayScale Erosion

    for (InputIterator.GoToBegin(), out.GoToBegin(); 
!InputIterator.IsAtEnd(); ++InputIterator, ++out) {

      ShapedNeighborhoodIteratorType::ConstIterator ci;

      OutputPixelType minValue = static_cast<OutputPixelType>(imageMaxVal);

      for (ci = InputIterator.Begin(); ci != InputIterator.End(); ci++) {
   
    InputPixelValue = ci.Get();

        // Here we want to get the "grayscale" value of the 
corresponding SE point - but how?

    // Then we want to subtract the SE grayscale value from the 
inputPixelValue -
    // but how, if we don't know how to set or retrieve the SE kernel 
values?

    // Then see if this is less than the minValue - if it is set it to 
the minValue

      }

      // Then set the output value to be this minValue

      out.Set(minValue);
    }

  }


More information about the Insight-users mailing list