Apply Morphological Closing On All Label Objects

Synopsis

Apply morphological closing operation on all LabelObjects of a given LabelMap.

In details:

  • read image is converted to LabelMap

  • apply morphological closing operation on all LabelObjects of the LabelMap

  • make sure there is no overlapping LabelObject with using LabelUniqueLabelMapFilter

  • convert the LabelMap to LabelImage

  • write the corresponding LabelImage

Results

Input image

Input image

Output image

Output image

Code

C++

#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkLabelImageToLabelMapFilter.h"
#include "itkLabelMapToLabelImageFilter.h"
#include "itkObjectByObjectLabelMapFilter.h"
#include "itkLabelUniqueLabelMapFilter.h"
#include "itkFlatStructuringElement.h"
#include "itkBinaryMorphologicalClosingImageFilter.h"

int
main(int argc, char * argv[])
{
  if (argc != 4)
  {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0];
    std::cerr << " <InputFileName> <OutputFileName> <radius>";
    std::cerr << std::endl;
    return EXIT_FAILURE;
  }

  const char *       inputFileName = argv[1];
  const char *       outputFileName = argv[2];
  const unsigned int radiusValue = std::stoi(argv[3]);

  constexpr unsigned int Dimension = 2;

  using PixelType = unsigned char;
  using ImageType = itk::Image<PixelType, Dimension>;

  using ReaderType = itk::ImageFileReader<ImageType>;
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(inputFileName);

  using LabelObjectType = itk::LabelObject<PixelType, Dimension>;
  using LabelMapType = itk::LabelMap<LabelObjectType>;

  using LabelImageToLabelMapFilterType = itk::LabelImageToLabelMapFilter<ImageType, LabelMapType>;
  LabelImageToLabelMapFilterType::Pointer labelMapConverter = LabelImageToLabelMapFilterType::New();
  labelMapConverter->SetInput(reader->GetOutput());
  labelMapConverter->SetBackgroundValue(itk::NumericTraits<PixelType>::Zero);

  using StructuringElementType = itk::FlatStructuringElement<Dimension>;
  StructuringElementType::RadiusType radius;
  radius.Fill(radiusValue);

  StructuringElementType structuringElement = StructuringElementType::Ball(radius);

  using MorphologicalFilterType =
    itk::BinaryMorphologicalClosingImageFilter<ImageType, ImageType, StructuringElementType>;
  MorphologicalFilterType::Pointer closingFilter = MorphologicalFilterType::New();

  using ObjectByObjectLabelMapFilterType = itk::ObjectByObjectLabelMapFilter<LabelMapType>;
  ObjectByObjectLabelMapFilterType::Pointer objectByObjectLabelMapFilter = ObjectByObjectLabelMapFilterType::New();
  objectByObjectLabelMapFilter->SetInput(labelMapConverter->GetOutput());
  objectByObjectLabelMapFilter->SetBinaryInternalOutput(true);
  objectByObjectLabelMapFilter->SetFilter(closingFilter);

  using UniqueLabelMapFilterType = itk::LabelUniqueLabelMapFilter<LabelMapType>;
  UniqueLabelMapFilterType::Pointer unique = UniqueLabelMapFilterType::New();
  unique->SetInput(objectByObjectLabelMapFilter->GetOutput());

  using LabelMapToLabelImageFilterType = itk::LabelMapToLabelImageFilter<LabelMapType, ImageType>;
  LabelMapToLabelImageFilterType::Pointer labelImageConverter = LabelMapToLabelImageFilterType::New();
  labelImageConverter->SetInput(unique->GetOutput());

  using WriterType = itk::ImageFileWriter<ImageType>;
  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName(outputFileName);
  writer->SetInput(labelImageConverter->GetOutput());
  try
  {
    writer->Update();
  }
  catch (itk::ExceptionObject & error)
  {
    std::cerr << "Error: " << error << std::endl;
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}

Classes demonstrated

template<typename TInputImage, typename TOutputImage = TInputImage, typename TInputFilter = ImageToImageFilter<Image<unsigned char, TInputImage::ImageDimension>, Image<unsigned char, TOutputImage::ImageDimension>>, class TOutputFilter = typename TInputFilter::Superclass, class TInternalInputImage = typename TInputFilter::InputImageType, class TInternalOutputImage = typename TOutputFilter::OutputImageType>
class ObjectByObjectLabelMapFilter : public itk::LabelMapFilter<TInputImage, TOutputImage>

ObjectByObjectLabelMapFilter applies an image pipeline to all the objects of a label map and produce a new label map.

The image pipeline can simply produce a modified object or produce several objects from the single input object. Several options are provided to handle the different cases.

KeepLabel, which defaults to true, makes the filter try to keep as much as possible the labels of the original objects. If an image pipeline produce several objects the label of the input object is assigned to the first output object. The other output objects get another label not present in the input image. When KeepLabel is set to false, all the objects are relabeled in the order of apparition during the filter process.

BinaryInternalOutput can be set to true if the image pipeline produce binary output image. In that case, the objects produced are identified with a connected component algorithm before being reinserted in the output label map. InternalForegroundValue can be set to a specific value which represent the foreground value in the binary image.

PadSize and ConstrainPaddingToImage can be used to extend the size of the image to process passed to the image pipeline. This is useful if the image pipeline is known to be able to enlarge the object. The padding can be constrained to the input label map region by setting ConstrainPaddingToImage to true - this parameter can make a difference for the algorithm with a different behavior on the border of the image. By default, the image is padded by 1 pixel and constrained to the image region.

This implementation was taken from the Insight Journal paper:

https://hdl.handle.net/1926/584 or http://www.insight-journal.org/browse/publication/176
Note

: When applying a single filter, input and output filters are the same; while applying a pipeline, input and output filters are different, may not even be of the same type. It is the responsibility of the user to connect the pipeline properly outside of this filter.

Author

Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France.

ITK Sphinx Examples:

See itk::ObjectByObjectLabelMapFilter for additional documentation.
template<typename TImage>
class LabelUniqueLabelMapFilter : public itk::AttributeUniqueLabelMapFilter<TImage, Functor::LabelLabelObjectAccessor<TImage::LabelObjectType>>

Make sure that the objects are not overlapping.

AttributeUniqueLabelMapFilter search the overlapping zones in the overlapping objects and keeps only a single object on all the pixels of the image. The object to keep is selected according to their label.

This implementation was taken from the Insight Journal paper:

https://hdl.handle.net/1926/584 or http://www.insight-journal.org/browse/publication/176
Author

Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France.

See

AttributeLabelObject

See itk::LabelUniqueLabelMapFilter for additional documentation.