关于KMeans上一篇文章有提到,也用了一些随机点进行数据分类,那么图像呢?图像也是一堆数据,我们只需要把1图像转变为一组数据即可进行数据分类,然后重新显示在图像上,这就是图像分割。
代码部分与上一章文章没多大区别,只需稍作改动即可:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat src = imread("F:/test/lena.jpg");
if (!src.data)
{
printf("图片未找到!\n");
return -1;
}
imshow("input image", src);
int width = src.cols; //图像宽
int height = src.rows; //图像高
int channels = src.channels(); //通道数
Scalar colorTab[] = { //定义颜色数组
Scalar(0,0,255),
Scalar(255,0,255),
Scalar(0,255,0),
Scalar(255,0,0),
Scalar(0,255,255),
};
//初始化定义
int clusterCount = 4;//4分类
int sampleCount = width * height;//样本点数量
Mat points(sampleCount, channels, CV_32F, Scalar(10));
Mat labels;
Mat centers(clusterCount, 1, points.type());
//RGB转换到样本数据
int index = 0;
for (int row = 0; row < src.rows; row++)
{
for (int col = 0; col < src.cols; col++)
{
index = row * width + col;
Vec3b bgr = src.at<Vec3b>(row, col);
//获取每个通道的值
points.at<float>(index, 0) = static_cast<int>(bgr[0]);
points.at<float>(index, 1) = static_cast<int>(bgr[1]);
points.at<float>(index, 2) = static_cast<int>(bgr[2]);
}
}
//运行KMeans
TermCriteria cirteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
kmeans(points, clusterCount, labels, cirteria, 3, KMEANS_PP_CENTERS, centers);
//显示图像分割结果
Mat result = Mat::zeros(src.size(), src.type());
for (int row = 0; row < src.rows; row++)
{
for (int col = 0; col < src.cols; col++)
{
index = row * width + col;
int label = labels.at<int>(index,0);
result.at<Vec3b>(row, col)[0] = colorTab[label][0];
result.at<Vec3b>(row, col)[1] = colorTab[label][1];
result.at<Vec3b>(row, col)[2]= colorTab[label][2];
}
}
imshow("result", result);
waitKey(0);
return 0;
}
运行结果:
分割后的结果: