一、Kmeans聚类算法基本原理
K-Means算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。
以彩色图像为例:基于彩色图像的RGB三通道为xyz轴建立空间直角坐标系,那么一副图像上的每个像素点与该空间直角坐标系建立了一 一映射(双射)的关系。
从空间直角坐标系中随机取 k 个点,作为 k 个簇的各自的中心。计算所有像素点到k个簇心的距离,并将所有像素点划分至与其距离最小的簇类。自此聚类完成。其中,距离定义为欧氏距离:
其中r,g,b分别表示红绿蓝三通道,r1,g1,b1为彩色图片中某像素点;r0,g0,b0表示某簇类的簇心。
二、基于Kmeans图像分割算法流程
Note:彩色图像的操作是基于一个三维空间
1、加载图像,获取图像的所有像素点并将其转换为样本数据。
2、开始迭代
a)、初始化簇心坐标。
a)、更新簇心坐标,遍历样本数据中的数据点并计算数据点与所有簇心的距离。对于某数据点计算得到的与所有簇类的欧氏距离中,取欧氏距离最小所对应的簇类作为该数据点对应的类。
c)、计算更新后的簇心坐标与更新前的簇心坐标的均方误差。若均方误差仍大于某阈值,则重复b),反之结束迭代。
Kmeans的详细算法流程参考博文:
https://www.cnblogs.com/pinard/p/6164214.html
三、代码主函数部分
1、加载图片
Mat src, dst;
src = imread("J20.jpg");
namedWindow("输入图像", WINDOW_AUTOSIZE);
imshow("输入图像", src);
2、初始化颜色(图像分割后需要上色)
Scalar colorTab[] = {
Scalar(0,0,255),
Scalar(0,255,0),
Scalar(255,0,0),
Scalar(0,255,255),
Scalar(255,0,255),
};
3、初始化簇类数,并将所有的像素点全部转换为样本数据
int sampleCount = width*height;
int clusterCount = 5;
Mat points(sampleCount, dims, CV_32F, Scalar(10));
//将彩色图像的像素点转换为样本数据
int index = 0;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
index = row*width + col;
Vec3b bgr = src.at<Vec3b>(row, col);
points.at<float>(index, 0) = static_cast<int>(bgr[0]);//b
points.at<float>(index, 1) = static_cast<int>(bgr[1]);//g
points.at<float>(index, 2) = static_cast<int>(bgr[2]);//r
}
}
4、利用Kmeans算法对样本数据分类
centers, feature = Kmeans(points, clusterCount, sampleCount);
5、
显示图像分割后的结果
//显示图像分割结果
Mat result = Mat::zeros(src.size(), src.type());
int index1 = 0;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
index1 = row*width + col;
int label = feature.at<float>(index1, 3);
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("基于Kmeans聚类的图像分割", result);
四、代码子函数部分
1、初始化聚类中心
//初始化簇心
Mat centers = Mat::zeros(clusterCount, 1, CV_32FC3);//4行3列
centers.at<float>(0, 0) = 150;
centers.at<float>(0, 1) = 180;
centers.at<float>(0, 2) = 200;
centers.at<float>(1, 0) = 20;
centers.at<float>(