本章我們用kmeans算法實現一個簡單圖像的分割。如下面的圖像,我們知道圖像分3個簇,背景、白色的任務,紅色的絲帶以及帽子。
Mat img = cv::imread("../kmeans.jpg");
namedWindow("image");
imshow("image", img);
首先我們會生成采樣點,采樣點包括原始圖像中的所有像素點,采樣點用32位浮點數表示,接着我們會定義一個標記矩陣labels,用來存放kmeans的結果。該矩陣中存放的是索引的采樣點屬於那一個簇,在本例子中,值應該是0,1或2,因為有3個簇。
//生成一維采樣點,包括所有圖像像素點,注意采樣點格式為32bit浮點數。
Mat samples(img.cols*img.rows, 1, CV_32FC3);
//標記矩陣,32位整形
Mat labels(img.cols*img.rows, 1, CV_32SC1);
uchar* p;
int i, j, k=0;
for(i=0; i < img.rows; i++)
{
p = img.ptr<uchar>(i);
for(j=0; j< img.cols; j++)
{
samples.at<Vec3f>(k,0)[0] = float(p[j*3]);
samples.at<Vec3f>(k,0)[1] = float(p[j*3+1]);
samples.at<Vec3f>(k,0)[2] = float(p[j*3+2]);
k++;
}
}
int clusterCount = 3;
Mat centers(clusterCount, 1, samples.type());
kmeans(samples, clusterCount, labels,
TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0),
3, KMEANS_PP_CENTERS, centers);
最后我們把不同的簇用不同灰度來表示,並把結果放在img1中。
//我們已知有3個聚類,用不同的灰度層表示。
Mat img1(img.rows, img.cols, CV_8UC1);
float step=255/(clusterCount - 1);
k=0;
for(i=0; i < img1.rows; i++)
{
p = img1.ptr<uchar>(i);
for(j=0; j< img1.cols; j++)
{
int tt = labels.at<int>(k, 0);
k++;
p[j] = 255 - tt*step;
}
}
namedWindow("image1");
imshow("image1", img1);
程序運行后的效果:
程序代碼:工程FirstOpenCV17