Cluster Pixels in Grayscale Image#
Synopsis#
Cluster the pixels in a greyscale image.
Results#
Output:
cluster[0] estimated mean : 10
cluster[1] estimated mean : 100
cluster[2] estimated mean : 200
Number of pixels per class
Code#
C++#
#include "itkScalarImageKmeansImageFilter.h"
#include "itkRelabelComponentImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkImageRegionIterator.h"
#include <itkImageToVTKImageFilter.h>
#include "vtkVersion.h"
#include "vtkImageViewer.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkImageActor.h"
#include "vtkImageMapper3D.h"
#include "vtkInteractorStyleImage.h"
#include "vtkRenderer.h"
using ImageType = itk::Image<unsigned char, 2>;
static void
CreateImage(ImageType::Pointer image);
int
main()
{
auto image = ImageType::New();
CreateImage(image);
using KMeansFilterType = itk::ScalarImageKmeansImageFilter<ImageType>;
auto kmeansFilter = KMeansFilterType::New();
kmeansFilter->SetInput(image);
kmeansFilter->SetUseNonContiguousLabels(true);
kmeansFilter->AddClassWithInitialMean(8);
kmeansFilter->AddClassWithInitialMean(110);
kmeansFilter->AddClassWithInitialMean(210);
kmeansFilter->Update();
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;
}
using OutputImageType = KMeansFilterType::OutputImageType;
using RelabelFilterType = itk::RelabelComponentImageFilter<OutputImageType, OutputImageType>;
auto relabeler = RelabelFilterType::New();
relabeler->SetInput(kmeansFilter->GetOutput());
using RescaleFilterType = itk::RescaleIntensityImageFilter<ImageType, ImageType>;
auto rescaleFilter = RescaleFilterType::New();
rescaleFilter->SetInput(relabeler->GetOutput());
rescaleFilter->SetOutputMinimum(0);
rescaleFilter->SetOutputMaximum(255);
using SizesType = RelabelFilterType::ObjectSizeInPixelsContainerType;
const SizesType & sizes = relabeler->GetSizeOfObjectsInPixels();
auto sizeItr = sizes.begin();
auto sizeEnd = sizes.end();
std::cout << "Number of pixels per class " << std::endl;
unsigned int kclass = 0;
while (sizeItr != sizeEnd)
{
std::cout << "Class " << kclass << " = " << *sizeItr << std::endl;
++kclass;
++sizeItr;
}
// Visualize
using ConnectorType = itk::ImageToVTKImageFilter<ImageType>;
auto originalConnector = ConnectorType::New();
originalConnector->SetInput(image);
vtkSmartPointer<vtkImageActor> originalActor = vtkSmartPointer<vtkImageActor>::New();
#if VTK_MAJOR_VERSION <= 5
originalActor->SetInput(originalConnector->GetOutput());
#else
originalConnector->Update();
originalActor->SetInputData(originalConnector->GetOutput());
#endif
auto outputConnector = ConnectorType::New();
outputConnector->SetInput(rescaleFilter->GetOutput());
vtkSmartPointer<vtkImageActor> outputActor = vtkSmartPointer<vtkImageActor>::New();
#if VTK_MAJOR_VERSION <= 5
outputActor->SetInput(outputConnector->GetOutput());
#else
outputConnector->Update();
outputActor->SetInputData(outputConnector->GetOutput());
#endif
// There will be one render window
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetSize(600, 300);
vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactor->SetRenderWindow(renderWindow);
// Define viewport ranges
// (xmin, ymin, xmax, ymax)
double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
// Setup both renderers
vtkSmartPointer<vtkRenderer> leftRenderer = vtkSmartPointer<vtkRenderer>::New();
renderWindow->AddRenderer(leftRenderer);
leftRenderer->SetViewport(leftViewport);
leftRenderer->SetBackground(.6, .5, .4);
vtkSmartPointer<vtkRenderer> rightRenderer = vtkSmartPointer<vtkRenderer>::New();
renderWindow->AddRenderer(rightRenderer);
rightRenderer->SetViewport(rightViewport);
rightRenderer->SetBackground(.4, .5, .6);
// Add the sphere to the left and the cube to the right
leftRenderer->AddActor(originalActor);
rightRenderer->AddActor(outputActor);
leftRenderer->ResetCamera();
rightRenderer->ResetCamera();
renderWindow->Render();
vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
interactor->SetInteractorStyle(style);
interactor->Start();
return EXIT_SUCCESS;
}
void
CreateImage(ImageType::Pointer image)
{
// Create an image with 2 connected components
ImageType::RegionType region;
ImageType::IndexType start;
start[0] = 0;
start[1] = 0;
ImageType::SizeType size;
size[0] = 200;
size[1] = 300;
region.SetSize(size);
region.SetIndex(start);
image->SetRegions(region);
image->Allocate();
itk::ImageRegionIterator<ImageType> imageIterator(image, region);
while (!imageIterator.IsAtEnd())
{
if (imageIterator.GetIndex()[0] > 100 && imageIterator.GetIndex()[0] < 150 && imageIterator.GetIndex()[1] > 100 &&
imageIterator.GetIndex()[1] < 150)
{
imageIterator.Set(100);
}
else if (imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70 && imageIterator.GetIndex()[1] > 50 &&
imageIterator.GetIndex()[1] < 70)
{
imageIterator.Set(200);
}
else
{
imageIterator.Set(10);
}
++imageIterator;
}
}
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: