算法介绍:
k-means 算法接受参数 k ,然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高,而不同聚类中的对象相似度较小。
聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。
K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。
K-means算法的基本思想是:
以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
假设要把样本集分为c个类别,算法描述如下:
(1)适当选择c个类的初始中心;
(2)在第k次迭代中,对任意一个样本,求其到c个中心的距离,将该样本归到距离最短的中心所在的类;
(3)利用均值等方法更新该类的中心值;
(4)对于所有的c个聚类中心,如果利用(2)(3)的迭代法更新后,值保持不变,则迭代结束,否则继续迭代。
该算法的最大优势在于简洁和快速。算法的关键在于初始中心的选择和距离公式。
//opencv库函数
CvRNG cvRNG( int64 seed CV_DEFAULT(-1)) //产生随机数种子,返回CvRNG
unsigned cvRandInt( CvRNG* rng );//产生随机的unsigned int值
void cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, CvScalar param1, CvScalar param2 );//用随机数填充数组并更新 RNG 状态
参考:http://www.opencv.org.cn/index.php/Cxcore%E6%95%B0%E7%BB%84%E6%93%8D%E4%BD%9C
void cvKMeans2( const CvArr* samples, int cluster_count,CvArr* labels, CvTermCriteria termcrit );//按照给定的类别数目对样本集合进行聚类
参考:http://www.opencv.org.cn/index.php/Cxcore%E5%85%B6%E5%AE%83%E6%B7%B7%E5%90%88%E5%87%BD%E6%95%B0
CvTermCriteria //迭代算法的终止准则
typedef struct CvTermCriteria
{
int type; /* CV_TERMCRIT_ITER 和CV_TERMCRIT_EPS二值之一,或者二者的组合 */
int max_iter; /* 最大迭代次数 */
double epsilon; /* 结果的精确性 */
}
CvTermCriteria;
精简的测试代码
#include<cv.h>
#include<highgui.h>
#include<stdio.h>
#include<malloc.h>
#pragma comment(lib,"cv.lib")
#pragma comment(lib,"cxcore.lib")
#pragma comment(lib,"highgui.lib")
#pragma comment(lib,"cvaux.lib")
/********************************************************************************
*
*
* 本程序是精简的聚类算法演示例程
* 作者:xlh145
* 博客:http://blog.csdn.net/xlh145/
*
*
********************************************************************************/
#define MAX_POINTS 1000 /*最多产生1000个样本点*/
#define CLASS_COUNT 5 /*生成5个分类*/
int main()
{
CvRNG rng = cvRNG(-1);//产生随机数种子
CvScalar color_tab[5];//定义5种颜色
color_tab[0] = CV_RGB(255,0,0);
color_tab[1] = CV_RGB(0,255,0);
color_tab[2] = CV_RGB(100,100,255);
color_tab[3] = CV_RGB(255,0,255);
color_tab[4] = CV_RGB(255,255,0);
IplImage* img = cvCreateImage(cvSize(500,500),8,3);//用于显示结果
cvNamedWindow("result",1);//产生窗体
//定义数组
int sample_count = cvRandInt(&rng)%MAX_POINTS+1;//样本的数量
int clusters_count = cvRandInt(&rng)%CLASS_COUNT+1;//分类的数量
CvMat* points = cvCreateMat(sample_count,1,CV_32FC2);//双通道的数组,储存点的坐标
CvMat * clusters = cvCreateMat(sample_count,1,CV_32SC1);//单通道的数组,储存分类的标识
//创建样本数据
for(int i=0;i<clusters_count;i++)
{
CvPoint center; //每一个分类样本的中心
CvMat ptemp;//临时的数组对象
center.x = cvRandInt(&rng)%img->width;
center.y = cvRandInt(&rng)%img->height;
int count = sample_count/clusters_count;//均分每一个分类包含的样本
cvGetRows(points,&ptemp,i*count,(i==clusters_count-1)?sample_count:(i+1)*count);//获取数组中的指定行数的指针 存储到ptemp中
//按照正态分布随机化数组数据
cvRandArr(&rng,&ptemp,CV_RAND_NORMAL,cvScalar(center.x,center.y,0,0),cvScalar(img->width*0.1,img->height*0.1,0,0));
}
//随机洗牌
for(i = 0; i<sample_count/2;i++)
{
CvPoint2D32f* p = (CvPoint2D32f*)points->data.fl;//数组的数据存放指针
CvPoint2D32f* p1 = p+cvRandInt(&rng)%sample_count;
CvPoint2D32f* p2 = p+cvRandInt(&rng)%sample_count;
CvPoint2D32f t;
CV_SWAP(*p1,*p2,t);
}
//Kmeans聚类
cvKMeans2(points,clusters_count,clusters,cvTermCriteria(CV_TERMCRIT_NUMBER|CV_TERMCRIT_ITER,10,1.0));
cvZero(img);
//在图像上显示分类的点
for(i=0;i<sample_count;i++)
{
int id = clusters->data.i[i];//得到点的分类聚合ID
CvPoint pt;
pt.x = (int)points->data.fl[i*2+0];
pt.y = (int)points->data.fl[i*2+1];
cvCircle( img, pt, 2, color_tab[id], CV_FILLED, CV_AA, 0 );
}
cvShowImage("result",img);
cvWaitKey(0);
//释放存储空间
cvReleaseImage(&img);
cvReleaseMat(&points);
cvReleaseMat(&clusters);
cvDestroyWindow("result");
return 0;
}