使用K-means聚类方法分割图像

使用C++、opencv中的Kmeans进行图像分割

K-means算法的原理

K-means算法是基于距离相似性的聚类算法,通过比较样本之间的相似性,将形式的样本划分到同一个类别中,K-Means算法的基本过程为:

  • 初始化常数 ,随机初始化k个聚类中心。
  • 重复计算以下过程,直到聚类中心不再改变:

               计算每个样本与每个聚类中心之间的相似度,将样本划分到最相似的类别中;

               计算划分到每个类别中的所有样本特征的均值,并将该均值作为每个类新的聚类中心;

  • 输出最终的聚类中心以及每个样本所属的类别。

        在K-Means算法中,需要随机初始化k个聚类中心,而K-Means算法对初始聚类中心的选取较为敏感,若选择的聚类中心不好,则得到的聚类结果会非常差,因此,对K-Means算法提出了很多的改进的方法,如K-Means++算法,在K-Means++算法中,希望初始化的k个聚类中心之间的距离尽可能的大,其具体过程为:

  • 在数据集中随机选择一个样本点作为第一个初始化的聚类中心
  • 选择出其余的聚类中心: 

                    计算样本中的每一个样本点与已经初始化的聚类中心之间的距离,并选择其中最短的距离
                    以概率选择距离最大的样本作为新的聚类中心,重复上述过程,直到 个聚类中心都被确定 

  • 对k个初始化的聚类中心,利用K-Means算法计算最终的聚类中心。

更多关于 K-Means算法的原理可查看:https://www.cnblogs.com/pinard/p/6164214.html

https://blog.csdn.net/loveliuzz/article/details/78783773


opencv中关于Kmeans的API:

double kmeans(InputArray data, int K, InputOutputArray bestLabels, TermCriteria criteria, int attempts, int flags, OutputArray centers=noArray() ) 

data:  需要自动聚类的数据,一般是一个Mat。浮点型的矩阵,每行为一个样本。

k: 取成几类,比较关键的一个参数。

bestLabels:  返回的类别标记,整型数字。

criteria: 算法结束的标准,获取期望精度的迭代最大次数

attempts:  判断某个样本为某个类的最少聚类次数,比如值为3时,则某个样本聚类3次都为同一个类,则确定下来。

flags:  确定簇心的计算方式。有三个值可选:KMEANS_RANDOM_CENTERS 表示随机初始化簇心。KMEANS_PP_CENTERS 表示用kmeans++算法来初始化簇心,KMEANS_USE_INITIAL_LABELS 表示第一次聚类时用用户给定的值初始化聚类,后面几次的聚类,则自动确定簇心。

centers: 用来初始化簇心的。与前一个flags参数的选择有关。如果选择KMEANS_RANDOM_CENTERS随机初始化簇心,则这个参数可省略。


使用聚类的方法分割图像,即将图像的像素点值(通常用彩色图像,像素点值为一个三元数组(b,g,r))作为聚类的元素,从而将图像中所有的点分为n类,达到分割的效果。这里只是展示代码效果,实际上可以根据需要,将其他颜色参数(如H、S、I、L、a、b等)、颜色参数的组合(NRI、NGI、NBI等)作为分类的数据点传入训练器,这样可能会对复杂的分割有帮助。 

代码如下:

#include "stdafx.h"
#include   
#include   
#include   
#include   
#include   

#include   
#include  
#include  
#include 

//聚类数目宏定义
#define ClusterNum 2  

using namespace cv;
using namespace std;

//K-means聚类
Mat clustering(Mat src) {

	int row = src.rows;
	int col = src.cols;
	unsigned long int size = row * col;

	//定义聚类图像,用来存储每个位置的类标签
	Mat clusters(size, 1, CV_32SC1); 
	//将源图转化为数据点
	Mat srcPoint(size, 1, CV_32FC3);

	Vec3f* srcPoint_p = (Vec3f*)srcPoint.data;
	Vec3f* src_p = (Vec3f*)src.data;

	unsigned long int i;
	for (i = 0; i < size; i++)
	{
		*srcPoint_p = *src_p;
		srcPoint_p++;
		src_p++;
	}

	Mat center(ClusterNum, 1, CV_32FC3);
	//用不同的标记度量聚类中心距离和的紧密度
	double compactness; 
	compactness = kmeans(srcPoint, ClusterNum, clusters,
		cvTermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 0.1), ClusterNum,
		KMEANS_PP_CENTERS, center);
	cout << "center row:" << center.rows << " col:" << center.cols << endl;

	for (int y = 0; y < center.rows; y++)
	{
		Vec3f* imgData = center.ptr(y);
		for (int x = 0; x < center.cols; x++)
		{
			cout << imgData[x].val[0] << " " << imgData[x].val[1] << " " << imgData[x].val[2] << endl;
		}
		cout << endl;
	}

	double minH, maxH;
	minMaxLoc(clusters, &minH, &maxH); 
	cout << "H-channel min:" << minH << " max:" << maxH << endl;

	int* clusters_p = (int*)clusters.data;
  
	Mat label(src.size(), CV_32SC1);
	int* label_p = (int*)label.data;
	//将类别分配给Mat标签   
	for (i = 0; i < size; i++)
	{
		*label_p = *clusters_p;
		label_p++;
		clusters_p++;
	}

	Mat label_show;
	label.convertTo(label_show, CV_8UC1);
	normalize(label_show, label_show, 255, 0, CV_MINMAX);
	imshow("label", label_show);

	map count;    
	map avg; 

	for (int y = 0; y < row; y++)
	{
		const Vec3f* imgData = src.ptr(y);
		int* idx = label.ptr(y);
		for (int x = 0; x < col; x++)
		{
			avg[idx[x]] += imgData[x];
			count[idx[x]] ++;
		}
	}

	//output the average value (clustering center)    
	//计算所得的聚类中心与kmean函数中center的第一列一致,    
	//以后可以省去后面这些繁复的计算,直接利用center,    
	//但是仍然不理解center的除第一列以外的其他列所代表的意思  
	for (i = 0; i < ClusterNum; i++)
	{
		avg[i] /= count[i];
		if (avg[i].val[0]>0 && avg[i].val[1]>0 && avg[i].val[2]>0)
		{
			cout << i << ": " << avg[i].val[0] << " " << avg[i].val[1] << " " << avg[i].val[2] << " count:" << count[i] << endl;
		}
	}

	//显示聚类图像
	Mat showImg(src.size(), CV_32FC3);
	for (int y = 0; y < row; y++)
	{
		Vec3f* imgData = showImg.ptr(y);
		int *idx = label.ptr(y);
		for (int x = 0; x < col; x++)
		{
			int id = idx[x];
			imgData[x].val[0] = avg[id].val[0];
			imgData[x].val[1] = avg[id].val[1];
			imgData[x].val[2] = avg[id].val[2];
		}
	}
	normalize(showImg, showImg, 1, 0, CV_MINMAX);//标准化
	imshow("show", showImg);
	waitKey();
	return label;//返回聚类后的标签
}

int main() 
{
	//读取图像
	Mat img = imread("D:\\1.jpg", 1);
	if (!img.data)
	{
		cout << "src image load failed!" << endl;
		return -1;
	}
	imshow("src", img);
	//高斯滤波降噪
	GaussianBlur(img, img, Size(3, 3), 0);
	//转换图片类型
	img.convertTo(img, CV_32FC3);
	//聚类操作
	Mat pixId = clustering(img);
}

 源图:

使用K-means聚类方法分割图像_第1张图片

标签图(分类后的结果,代码中分为2类):

使用K-means聚类方法分割图像_第2张图片

结果图:

使用K-means聚类方法分割图像_第3张图片

你可能感兴趣的:(C++,图像处理,opencv,C++,opencv,聚类分割,图像聚类,kmeans)