Label Connect Components in Binary Image#

Synopsis#

Label connected components in a binary image.

Results#

Warning

Fix Errors Example contains errors needed to be fixed for proper output.

Code#

C++#

#include "itkLiThresholdImageFilter.h"
#include "itkHuangThresholdImageFilter.h"
#include "itkIntermodesThresholdImageFilter.h"
#include "itkIsoDataThresholdImageFilter.h"
#include "itkKittlerIllingworthThresholdImageFilter.h"
#include "itkMaximumEntropyThresholdImageFilter.h"
#include "itkMomentsThresholdImageFilter.h"
#include "itkOtsuThresholdImageFilter.h"
#include "itkRenyiEntropyThresholdImageFilter.h"
#include "itkShanbhagThresholdImageFilter.h"
#include "itkTriangleThresholdImageFilter.h"
#include "itkYenThresholdImageFilter.h"

#include "itkConnectedComponentImageFilter.h"
#include "itkLabelToRGBImageFilter.h"
#include "itkImageFileReader.h"

#include "itksys/SystemTools.hxx"
#include <sstream>
#include <map>
#ifdef ENABLE_QUICKVIEW
#  include "QuickView.h"
#endif

int
main(int argc, char * argv[])
{
  if (argc < 2)
  {
    std::cout << "Usage: " << argv[0];
    std::cout << " inputImageFile";
    std::cerr << std::endl;
    return EXIT_FAILURE;
  }

  using InputPixelType = short;
  using OutputPixelType = int;
  using RGBPixelType = itk::RGBPixel<unsigned char>;

  using InputImageType = itk::Image<InputPixelType, 2>;
  using OutputImageType = itk::Image<OutputPixelType, 2>;
  using RGBImageType = itk::Image<RGBPixelType, 2>;

  using LiFilterType = itk::LiThresholdImageFilter<InputImageType, OutputImageType>;
  using HuangFilterType = itk::HuangThresholdImageFilter<InputImageType, OutputImageType>;
  using IntermodesFilterType = itk::IntermodesThresholdImageFilter<InputImageType, OutputImageType>;
  using IsoDataFilterType = itk::IsoDataThresholdImageFilter<InputImageType, OutputImageType>;
  using KittlerIllingworthFilterType = itk::KittlerIllingworthThresholdImageFilter<InputImageType, OutputImageType>;
  using LiFilterType = itk::LiThresholdImageFilter<InputImageType, OutputImageType>;
  using MaximumEntropyFilterType = itk::MaximumEntropyThresholdImageFilter<InputImageType, OutputImageType>;
  using MomentsFilterType = itk::MomentsThresholdImageFilter<InputImageType, OutputImageType>;
  using OtsuFilterType = itk::OtsuThresholdImageFilter<InputImageType, OutputImageType>;
  using RenyiEntropyFilterType = itk::RenyiEntropyThresholdImageFilter<InputImageType, OutputImageType>;
  using ShanbhagFilterType = itk::ShanbhagThresholdImageFilter<InputImageType, OutputImageType>;
  using TriangleFilterType = itk::TriangleThresholdImageFilter<InputImageType, OutputImageType>;
  using YenFilterType = itk::YenThresholdImageFilter<InputImageType, OutputImageType>;

  using ConnectedComponentImageFilterType = itk::ConnectedComponentImageFilter<OutputImageType, OutputImageType>;
  using RGBFilterType = itk::LabelToRGBImageFilter<OutputImageType, RGBImageType>;

  const auto input = itk::ReadImage<InputImageType>(argv[1]);

#ifdef ENABLE_QUICKVIEW
  QuickView viewer;
  viewer.AddImage(input.GetPointer(), true, itksys::SystemTools::GetFilenameName(argv[1]));

  using FilterContainerType =
    std::map<std::string, itk::HistogramThresholdImageFilter<InputImageType, OutputImageType>::Pointer>;
  FilterContainerType filterContainer;

  filterContainer["Huang"] = HuangFilterType::New();
  filterContainer["Intermodes"] = IntermodesFilterType::New();
  filterContainer["IsoData"] = IsoDataFilterType::New();
  filterContainer["KittlerIllingworth"] = KittlerIllingworthFilterType::New();
  filterContainer["Li"] = LiFilterType::New();
  filterContainer["MaximumEntropy"] = MaximumEntropyFilterType::New();
  filterContainer["Moments"] = MomentsFilterType::New();
  filterContainer["Otsu"] = OtsuFilterType::New();
  filterContainer["RenyiEntropy"] = RenyiEntropyFilterType::New();
  filterContainer["Shanbhag"] = ShanbhagFilterType::New();
  filterContainer["Triangle"] = TriangleFilterType::New();
  filterContainer["Yen"] = YenFilterType::New();

  auto it = filterContainer.begin();
  for (it = filterContainer.begin(); it != filterContainer.end(); ++it)
  {
    (*it).second->SetInsideValue(255);
    (*it).second->SetOutsideValue(0);
    (*it).second->SetNumberOfHistogramBins(25);
    (*it).second->SetInput(input);
    try
    {
      (*it).second->Update();
    }
    catch (const itk::ExceptionObject & err)
    {
      std::cout << "Caught exception" << std::endl;
      std::cout << err << std::endl;
      continue;
    }
    auto connected = ConnectedComponentImageFilterType::New();
    connected->SetInput((*it).second->GetOutput());

    auto rgbFilter = RGBFilterType::New();
    rgbFilter->SetInput(connected->GetOutput());
    std::stringstream desc;
    desc << (*it).first << " threshold = " << (*it).second->GetThreshold();
    viewer.AddRGBImage(rgbFilter->GetOutput(), true, desc.str());
  }

  viewer.Visualize();
#endif
  return EXIT_SUCCESS;
}

Classes demonstrated#

template<typename TInputImage, typename TOutputImage, typename TMaskImage = TInputImage>
class ConnectedComponentImageFilter : public itk::ImageToImageFilter<TInputImage, TOutputImage>, protected itk::ScanlineFilterCommon<TInputImage, TOutputImage>

Label the objects in a binary image.

ConnectedComponentImageFilter labels the objects in a binary image (non-zero pixels are considered to be objects, zero-valued pixels are considered to be background). Each distinct object is assigned a unique label. The filter experiments with some improvements to the existing implementation, and is based on run length encoding along raster lines. If the output background value is set to zero (the default), the final object labels start with 1 and are consecutive. If the output background is set to a non-zero value (by calling the SetBackgroundValue() routine of the filter), the final labels start at 0, and remain consecutive except for skipping the background value as needed. Objects that are reached earlier by a raster order scan have a lower label. This is different to the behaviour of the original connected component image filter which did not produce consecutive labels or impose any particular ordering.

After the filter is executed, ObjectCount holds the number of connected components.

See

ImageToImageFilter

ITK Sphinx Examples:

Subclassed by itk::ConnectedComponentFunctorImageFilter< TInputImage, TOutputImage, Functor::SimilarPixelsFunctor< TInputImage::ValueType >, TMaskImage >, itk::ConnectedComponentFunctorImageFilter< TInputImage, TOutputImage, Functor::SimilarVectorsFunctor< TInputImage::ValueType >, TMaskImage >, itk::ConnectedComponentFunctorImageFilter< TInputImage, TOutputImage, TFunctor, TMaskImage >

See itk::ConnectedComponentImageFilter for additional documentation.