牛骨文教育服务平台(让学习变的简单)
博文笔记

Opencv之Kmeans算法实现

创建时间:2016-09-22 投稿人: 浏览次数:866

      

在machine learning 中已经详细的介绍了,参http://blog.csdn.net/qq_29540745/article/details/52621383.在使用kmeans之前,必须先了解kmeans算法的2个缺点:第一是必须人为指定所聚的类的个数k;第二是如果使用欧式距离来衡量相似度的话,可能会得到错误的结果,因为没有考虑到属性的重要性和相关性。为了减少这种错误,在使用kmeans距离时,一定要使样本的每一维数据归一化,不然的话由于样本的属性范围不同会导致错误的结果。




<span style="font-family: Arial, Helvetica, sans-serif;">void RNG::fill(InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false )</span>

       这个函数是对矩阵mat填充随机数,随机数的产生方式有参数2来决定,如果为参数2的类型为RNG::UNIFORM,则表示产生均一分布的随机数,如果为RNG::NORMAL则表示产生高斯分布的随机数。对应的参数3和参数4为上面两种随机数产生模型的参数。比如说如果随机数产生模型为均匀分布,则参数a表示均匀分布的下限,参数b表示上限。如果随机数产生模型为高斯模型,则参数a表示均值,参数b表示方程。参数5只有当随机数产生方式为均匀分布时才有效,表示的是是否产生的数据要布满整个范围(没用过,所以也没仔细去研究)。另外,需要注意的是用来保存随机数的矩阵mat可以是多维的,也可以是多通道的,目前最多只能支持4个通道。

void randShuffle(InputOutputArray dst, double iterFactor=1., RNG* rng=0 )

       该函数表示随机打乱1D数组dst里面的数据,随机打乱的方式由随机数发生器rng决定。iterFactor为随机打乱数据对数的因子,总共打乱的数据对为:dst.rows*dst.cols*iterFactor,因此如果为0,表示没有打乱数据。

</pre><pre name="code" class="cpp">Class TermCriteria
       类TermCriteria 一般表示迭代终止的条件,如果为CV_TERMCRIT_ITER,则用最大迭代次数作为终止条件,如果为CV_TERMCRIT_EPS 则用精度作为迭代条件,如果为CV_TERMCRIT_ITER+CV_TERMCRIT_EPS则用最大迭代次数或者精度作为迭代条件,看哪个条件先满足。

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

      该函数为kmeans聚类算法实现函数。参数data表示需要被聚类的原始数据集合,一行表示一个数据样本,每一个样本的每一列都是一个属性;参数k表示需要被聚类的个数;参数bestLabels表示每一个样本的类的标签,是一个整数,从0开始的索引整数;参数criteria表示的是算法迭代终止条件;参数attempts表示运行kmeans的次数,取结果最好的那次聚类为最终的聚类,要配合下一个参数flages来使用;参数flags表示的是聚类初始化的条件。其取值有3种情况,如果为KMEANS_RANDOM_CENTERS,则表示为随机选取初始化中心点,如果为KMEANS_PP_CENTERS则表示使用某一种算法来确定初始聚类的点;如果为KMEANS_USE_INITIAL_LABELS,则表示使用用户自定义的初始点,但是如果此时的attempts大于1,则后面的聚类初始点依旧使用随机的方式;参数centers表示的是聚类后的中心点存放矩阵。

void circle( Arr* img, Point center, int radius, Scalar color, int thickness=1, int line_type=8, int shift=0 );

img 图像
center 圆心坐标
radius 圆形的半径
color 线条的颜色
thickness 如果是正数,表示组成圆的线条的粗细程度。否则,表示圆是否被填充
line_type 线条的类型。
shift 圆心坐标点和半径值的小数点位数




把原版的程序换汤不换药的改了下,不过注释很详细。

#include <opencv2/opencv.hpp>  
#include<iostream>  
using namespace std;
using namespace cv;

#define WINDOW_1 "聚类前"
#define WINDOW_2 "聚类后"

Mat dstIamge(500, 500, CV_8UC3);
Mat A_dstIamge = dstIamge/*.clone()*/;
RNG rng(12345); //随机数产生器

int clusterCount; int sampleCount;
int MIN_SAMPLECOUNT = 400, MIN_SLUSTERCOUNTS = 5;
static void ShowHelpText();
void cluster(int, void*)
{
	Scalar colorTab[] =     //最多显示7类有颜色的,所以最多也就给7个颜色
	{
		Scalar(0, 0, 255),
		Scalar(0, 255, 0),
		Scalar(0, 255, 255),

		Scalar(255, 0, 0),
		Scalar(255, 255, 0),
		Scalar(255, 0, 255),
		Scalar(255, 255, 255),

		Scalar(200, 200, 255),
	};
	clusterCount = rng.uniform(2, MIN_SLUSTERCOUNTS + 1);//产生之间的整数个类别!
	sampleCount = rng.uniform(1, MIN_SAMPLECOUNT + 1);//产生1到1001个整数样本数,也就是一千个样本
	Mat points(sampleCount, 1, CV_32FC2), labels;   //产生的样本数,实际上为2通道的列向量,元素类型为Point2f

	clusterCount = MIN(clusterCount, sampleCount);
	Mat centers(clusterCount, 1, points.type());    //用来存储聚类后的中心点

	/* generate random sample from multigaussian distribution */
	for (int k = 0; k < clusterCount; k++) // Generate random points  
	{
		Point center;// Random point coordinate  
		center.x = rng.uniform(0, dstIamge.cols);
		center.y = rng.uniform(0, dstIamge.rows);
		Mat pointChunk = points.rowRange(k*sampleCount / clusterCount,
			k == clusterCount - 1 ? sampleCount : (k + 1)*sampleCount / clusterCount);   //最后一个类的样本数不一定是平分的,
		//剩下的一份都给最后一类
		// Each of the classes is the same variance, but the mean is different.  
		rng.fill(pointChunk, CV_RAND_NORMAL, Scalar(center.x, center.y),//the mean
			Scalar(dstIamge.cols*0.05, dstIamge.rows*0.05)); //the same variance
	}
	randShuffle(points, 1, &rng);   //因为要聚类,所以先随机打乱points里面的点,注意points和pointChunk是共用数据的。
	dstIamge = Scalar::all(0);

	for (int i = 0; i < sampleCount; i++)
	{

		Point p = points.at<Point2f>(i);// Coordinates of corresponding points  
		circle(A_dstIamge, p, 1, Scalar::all(255), CV_FILLED, CV_AA);
	}
	imshow(WINDOW_1, A_dstIamge);

	kmeans(points, clusterCount, labels,//labels表示每一个样本的类的标签,是一个整数,从0开始的索引整数,是簇数.
		TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0),//用最大迭代次数或者精度作为迭代条件,看哪个条件先满足
		3,  //聚类3次,取结果最好的那次,
		KMEANS_PP_CENTERS,//则表示为随机选取初始化中心点,聚类的初始化采用PP特定的随机算法。
		centers);  //参数centers表示的是聚类后的中心点存放矩阵。
	// Traverse each point 
	for (int i = 0; i < sampleCount; i++)
	{
		int clusterIdx = labels.at<int>(i);// A label has been completed by clustering  
		Point p = points.at<Point2f>(i);// Coordinates of corresponding points  
		circle(dstIamge, p, 1, colorTab[clusterIdx], CV_FILLED, CV_AA);
	}

	imshow(WINDOW_2, dstIamge);
}
int main()
{
	ShowHelpText();
	while (1)
	{
	
	namedWindow(WINDOW_1, WINDOW_AUTOSIZE);
	createTrackbar("samleCounts: ", WINDOW_1, &MIN_SAMPLECOUNT, 1000, cluster);
	cluster(0, 0);

	createTrackbar("clusterCounts: ", WINDOW_1, &MIN_SLUSTERCOUNTS, 10, cluster);
	cluster(0, 0);


		char key = (char)waitKey(); //wait forever
		if (key == 27 || key == "q" || key == "Q") 
			break;
	}
	return 0;
}
static void ShowHelpText()
{
	cout << "




"<< "                    " << " Kmeans algorithm completion process  ! " << endl;
}
结果:

希望能帮到在图像处理门口徘徊的新手们,共勉!





1.http://www.cnblogs.com/tornadomeet/archive/2012/11/23/2783709.html

2.http://baike.baidu.com/link?url=6K1GCwyJsv1CAf2tDkpiGIXc8oLe2I7VWjGPQcL5qD26Fh7divPacCAhiOXPLF2yfbwVGrs9rZ6tQ6skLcf5dK

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。