Manuel Sebastian Rios Manuel Sebastian Rios - 9 days ago 7
C++ Question

how to set initial centers of K-means openCV c++

I am trying to do a segmentation of an image using OpenCv and Kmeans, the code that I have just implemented is the following:

#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

int main(int, char** argv)
{
Mat src, Imagen2, Imagris, labels, centers,imgfondo;
src = imread("C:/Users/Sebastian/Documents/Visual Studio 2015/Projects/ClusteringImage/data/leon.jpg");
imgfondo = imread("C:/Users/Sebastian/Documents/Visual Studio 2015/Projects/ClusteringImage/data/goku640.jpg");
if (src.empty()|| imgfondo.empty())
{
printf("Error al cargar imagenes");
waitKey();
return -1;
}
Mat samples(src.rows * src.cols, 3, CV_32F);
for (int y = 0; y < src.rows; y++)
for (int x = 0; x < src.cols; x++)
for (int z = 0; z < 3; z++)
samples.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y, x)[z];
//KMEANS_USE_INITIAL_LABELS
kmeans(samples, 2, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0), 3, KMEANS_USE_INITIAL_LABELS, centers);
Mat new_image(src.size(), src.type());
int cluster;
if (centers.at<float>(0, 1) > centers.at<float>(1, 1)) cluster = 0;
else cluster = 1;
for (int y = 0; y < src.rows; y++)
for (int x = 0; x < src.cols; x++)
{
int cluster_idx = labels.at<int>(y + x*src.rows, 0);
if (cluster_idx == cluster)
{
new_image.at<Vec3b>(y, x)[0] = imgfondo.at<Vec3b>(y, x)[0];
new_image.at<Vec3b>(y, x)[1] = imgfondo.at<Vec3b>(y, x)[1];
new_image.at<Vec3b>(y, x)[2] = imgfondo.at<Vec3b>(y, x)[2];
}
else
{
new_image.at<Vec3b>(y, x)[0] = src.at<Vec3b>(y, x)[0];
new_image.at<Vec3b>(y, x)[1] = src.at<Vec3b>(y, x)[1];
new_image.at<Vec3b>(y, x)[2] = src.at<Vec3b>(y, x)[2];
}
}
imshow("Original image", src);
imshow("clustered image", new_image);
waitKey();
}


It works pretty well and it does what i want, but i would like to set my own initial centers values. I have read that it could be done using the flag "KMEANS_USE_INITIAL_LABELS" but im not very sure about how to use it, and how and where should I set the initial values.
thanks.

Answer

The function allows you to directly set the initial labeling, not centers. Fortunately, since k-means alternates between assignment and update steps, you can get the effect you want indirectly.


From the docs:

labels – Input/output integer array that stores the cluster indices for every sample.

KMEANS_USE_INITIAL_LABELS During the first (and possibly the only) attempt, use the user-supplied labels instead of computing them from the initial centers. For the second and further attempts, use the random or semi-random centers. Use one of KMEANS_*_CENTERS flag to specify the exact method.

So, what the docs say is that you can set the initial labeling. If you want to do this, in your code

kmeans(samples, 2, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0), 3, KMEANS_USE_INITIAL_LABELS, centers);

initialize the 3rd parameter to be the input labels (for use in the first iteration).


If you want to get the effect of setting the initial centers, you can do the following:

  1. Decide what the centers are.

  2. Calculate the labeling like the algorithm does in the assignment step.

  3. Pass the resulting labeling to the function.