机器学习初探:OpenCV K均值代码笔记

   K均值聚类算法在Opencv的cxcore中,它可以找到数据的自然类别。最为常用的聚类技术之一,它可以迅速找到“类别”中心,将数据进行聚类。

 

 

 

    我们可以从图中了解K均值算法的聚类过程:

    (a) 随机放置聚类中心(圆),将数据样本(小方块)聚到离它最近的中心(即连线)

    (b) 数据中心移到它所在类别的中心

    (c) 数据点根据最近邻规则重新聚到类别中心(如b中最右圆的一个样本,在c中被聚到了下方圆中)

    (d) 聚类中心再次移动到它所在的类别中心

    持续运行直到收敛,我们便将样本点(小方块)聚为三类(圆)。

 

    OpenCV Sample中的kmeans代码,随机产生不超过5种的类别,以及1000个点。

[cpp]  view plain copy
  1. #include "cv.h"  
  2. #include "highgui.h"  
  3. #include <stdio.h>  
  4.   
  5. int main( int argc, char** argv )  
  6. {  
  7.     #define MAX_CLUSTERS 5  
  8.     CvScalar color_tab[MAX_CLUSTERS];  
  9.     IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );  
  10.     CvRNG rng = cvRNG(-1);  
  11.     CvPoint ipt;  
  12.   
  13.     color_tab[0] = CV_RGB(255,0,0);  
  14.     color_tab[1] = CV_RGB(0,255,0);  
  15.     color_tab[2] = CV_RGB(100,100,255);  
  16.     color_tab[3] = CV_RGB(255,0,255);  
  17.     color_tab[4] = CV_RGB(255,255,0);  
  18.   
  19.     cvNamedWindow( "clusters", 1 );  
  20.   
  21.     for(;;)  
  22.     {  
  23.         char key;  
  24.         int k, cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1;  
  25.         int i, sample_count = cvRandInt(&rng)%1000 + 1;  
  26.         CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 );  
  27.         CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 ); //创建sample_count行1列的矩阵 用来存储数据标签   
  28.         cluster_count = MIN(cluster_count, sample_count);  
  29.   
  30.         /* generate random sample from multigaussian distribution */  
    1. //随机生成样本多元高斯分布  
    2.         //样本总数为sample_count 类别总数为cluster_count  
    3.         //样本矩阵为points,先按类别分成cluster_count份 每一份数据的个数为sample_count/cluster_count  
    4.         //然后按类别随机矩阵填充样本矩阵,第一类填充矩阵的0~sample_count/cluster_count行  
    5.         //第二类填充样本矩阵的sample_count/cluster_count~sample_count/cluster_count*2行,以此类推直到填充满所有矩阵,每一类的样本个数是一样的  

  31.         for( k = 0; k < cluster_count; k++ )  
  32.         {  
  33.             CvPoint center;  
  34.             CvMat point_chunk;  
  35.             center.x = cvRandInt(&rng)%img->width;  
  36.             center.y = cvRandInt(&rng)%img->height;  
  37.             cvGetRows( points, &point_chunk, k*sample_count/cluster_count,  
  38.                        k == cluster_count - 1 ? sample_count :  
  39.                        (k+1)*sample_count/cluster_count, 1 );  
  40.   
    1. //point_chunk输出数组,CV_RAND_NORMAL分布类型为正态分布或者高斯分布  
    2.             //cvScalar(center.x,center.y,0,0)随机数的平均值  
    3.             // cvScalar(img->width/6,img->height/6,0,0)如果是正态分布它是随机数的标准差
  41.             cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL,  
  42.                        cvScalar(center.x,center.y,0,0),  
  43.                        cvScalar(img->width*0.1,img->height*0.1,0,0));  
  44.         }  
  45.   
  46.         /* shuffle samples */     //样本重新排序
  47.         for( i = 0; i < sample_count/2; i++ )  
  48.         {  
  49.             //在points样本矩阵中随机取两个样本交换位置  
  50.   CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count;  
  51.             CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count;  
  52.             CvPoint2D32f temp;  
  53.             CV_SWAP( *pt1, *pt2, temp );  
  54.         }  
  55.   //points样本矩阵,cliuster_count分类数,输出向量clusters,最后一个参数指定精度 
  56.         printf( "iterations=%d\n", cvKMeans2( points, cluster_count, clusters,  
  57.                 cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 ),  
  58.                 5, 0, 0, 0, 0 ));  
  59.   
  60.         cvZero( img );  
  61.   
  62.         for( i = 0; i < sample_count; i++ )  
  63.         {  
  64.             int cluster_idx = clusters->data.i[i];  
  65.             ipt.x = (int)points->data.fl[i*2];  
  66.             ipt.y = (int)points->data.fl[i*2+1];  
  67.             cvCircle( img, ipt, 2, color_tab[cluster_idx], CV_FILLED, CV_AA, 0 );  
  68.         }  
  69.   
  70.         cvReleaseMat( &points );  
  71.         cvReleaseMat( &clusters );  
  72.   
  73.         cvShowImage( "clusters", img );  
  74.   
  75.         key = (char) cvWaitKey(0);  
  76.         if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'  
  77.             break;  
  78.     }  
  79.   
  80.     cvDestroyWindow( "clusters" );  
  81.     return 0;  
  82. }  

 

    在最外层for循环之前,我们定义了5种颜色color_tab,类别的上届MAX_CLUSTERS。

在循环中,随机产生了cluster_count个类别,以及sample_count个样本点。

同时,建立了points来存放样本点,clusters来存放每个样本点的类别。

[cpp]  view plain copy
  1. CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 );CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 );  
 

    其中cvCreateMat的第一个参数为矩阵的行数,第二个为列数。CV_32FC2表示矩阵的元素为32位浮点二元组,即我们的Point。

    在数据生成的for循环中,point存放所有样本的信息,我们需要将将样本分类并随机赋值。

[cpp]  view plain copy
  1. cvGetRows( points, &point_chunk, k*sample_count/cluster_count,k == cluster_count - 1 ?sample_count :(k+1)*sample_count/cluster_count, 1 );cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL,cvScalar(center.x,center.y,0,0),cvScalar(img->width*0.1,img->height*0.1,0,0));  

    cvGetRows根据该次迭代的当前类别k,得到points中的对应子矩阵point_chunk。每个类别行数为sample_count/cluster_count。

    接着,使用cvRandArr为该子矩阵使用正态分布随机赋值。

 

    下一个for循环中,打乱了数据样本的顺序。

[cpp]  view plain copy
  1. CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count;    CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count;    CvPoint2D32f temp;    CV_SWAP( *pt1, *pt2, temp );  

 

    我们可以看到,pt1,pt2是points中随机选取的样本点,并进行了交换。

    然后,使用了cvKmeans2()进行聚类,知道聚类中心的最大移动小于1停止。最后用for循环画出结果。

 

    让我们执行程序,看一下聚类结果吧!

 

 

 

----------------------------------

作者:小斤(陈忻)

本文属于原创文章,如需转载引用请注明原文作者和链接,谢谢。

你可能感兴趣的:(机器学习初探:OpenCV K均值代码笔记)