K-Means Clustering#

Warning

Fix Problem Contains problems not fixed from original wiki.

Synopsis#

KMeans clustering.

Results#

Note

Help Wanted Implementation of Results for sphinx examples containing this message. Reconfiguration of CMakeList.txt may be necessary. Write An Example <https://itk.org/ITKExamples/Documentation/Contribute/WriteANewExample.html>

Code#

C++#

#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkScalarImageKmeansImageFilter.h>

int
main(int argc, char * argv[])
{
  // sample usage
  //./kMeansClustering input.jpg output.jpg 1 3 0 100 200

  // verify command line arguments
  if (argc < 5)
  {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0];
    std::cerr << " inputScalarImage outputLabeledImage contiguousLabels";
    std::cerr << " numberOfClasses mean1 mean2... meanN " << std::endl;
    return EXIT_FAILURE;
  }

  // parse command line arguments
  const char *       inputImageFileName = argv[1];
  const char *       outputImageFileName = argv[2];
  const unsigned int useNonContiguousLabels = std::stoi(argv[3]);
  const unsigned int numberOfInitialClasses = std::stoi(argv[4]);

  constexpr unsigned int argoffset = 5;

  if (static_cast<unsigned int>(argc) < numberOfInitialClasses + argoffset)
  {
    std::cerr << "Error: " << std::endl;
    std::cerr << numberOfInitialClasses << " classes has been specified ";
    std::cerr << "but no enough means have been provided in the command ";
    std::cerr << "line arguments " << std::endl;
    return EXIT_FAILURE;
  }

  std::vector<double> userMeans;
  for (unsigned k = 0; k < numberOfInitialClasses; ++k)
  {
    const double userProvidedInitialMean = std::stod(argv[k + argoffset]);
    userMeans.push_back(userProvidedInitialMean);
  }

  // Define the pixel type and dimension of the image that we intend to
  // classify.

  using PixelType = signed short;
  constexpr unsigned int Dimension = 2;
  using ImageType = itk::Image<PixelType, Dimension>;

  const auto input = itk::ReadImage<ImageType>(inputImageFileName);

  // Instantiate the ScalarImageKmeansImageFilter
  using KMeansFilterType = itk::ScalarImageKmeansImageFilter<ImageType>;

  auto kmeansFilter = KMeansFilterType::New();

  kmeansFilter->SetInput(input);

  // Make the output image intellegable by expanding the range of output image values, if desired

  kmeansFilter->SetUseNonContiguousLabels(useNonContiguousLabels);

  // initialize using the user input means

  for (unsigned k = 0; k < numberOfInitialClasses; ++k)
  {
    kmeansFilter->AddClassWithInitialMean(userMeans[k]);
  }

  try
  {
    itk::WriteImage(kmeansFilter->GetOutput(), outputImageFileName);
  }
  catch (const itk::ExceptionObject & excp)
  {
    std::cerr << "Problem encountered while writing ";
    std::cerr << " image file : " << outputImageFileName << std::endl;
    std::cerr << excp << std::endl;
    return EXIT_FAILURE;
  }

  // inspect the means
  KMeansFilterType::ParametersType estimatedMeans = kmeansFilter->GetFinalMeans();

  const unsigned int numberOfClasses = estimatedMeans.Size();

  for (unsigned int i = 0; i < numberOfClasses; ++i)
  {
    std::cout << "cluster[" << i << "] ";
    std::cout << "    estimated mean : " << estimatedMeans[i] << std::endl;
  }

  return EXIT_SUCCESS;
}

Classes demonstrated#

template<typename TInputImage, typename TOutputImage = Image<unsigned char, TInputImage::ImageDimension>>
class ScalarImageKmeansImageFilter : public itk::ImageToImageFilter<TInputImage, TOutputImage>

Classifies the intensity values of a scalar image using the K-Means algorithm.

Given an input image with scalar values, it uses the K-Means statistical classifier in order to define labels for every pixel in the image. The filter is templated over the type of the input image. The output image is predefined as having the same dimension of the input image and pixel type unsigned char, under the assumption that the classifier will generate less than 256 classes.

You may want to look also at the RelabelImageFilter that may be used as a postprocessing stage, in particular if you are interested in ordering the labels by their relative size in number of pixels.

See

Image

See

ImageKmeansModelEstimator

See

KdTreeBasedKmeansEstimator, WeightedCentroidKdTreeGenerator, KdTree

See

RelabelImageFilter

ITK Sphinx Examples:

See itk::ScalarImageKmeansImageFilter for additional documentation.