K均值算法(代码)

来源:互联网 发布:易语言时时彩计划源码 编辑:程序博客网 时间:2024/05/29 15:02
  cvKMeans2在做聚类时,一般需要知道分类以后的类别中心。而在教材《学习opencv》和opencv中文文档中并没有给出完整的cvKMeans2的使用方法。

      如在文档中的说明如下:

void cvKMeans2( const CvArr* samples, int cluster_count,
                CvArr* labels, CvTermCriteria termcrit );
samples
输入样本的浮点矩阵,每个样本一行。
cluster_count
所给定的聚类数目
labels
输出整数向量:每个样本对应的类别标识
termcrit
指定聚类的最大迭代次数和/或精度(两次迭代引起的聚类中心的移动距离)
函数 cvKMeans2 执行 k-means 算法 搜索 cluster_count 个类别的中心并对样本进行分类,输出 labels(i) 为样本 i 的类别标识。

 

 而在cxcore.h文件中cvKMeans2的声明如下:

cvKMeans2( const CvArr* samples, int cluster_count, CvArr* labels,
                      CvTermCriteria termcrit, int attempts CV_DEFAULT(1),
                      CvRNG* rng CV_DEFAULT(0), int flags CV_DEFAULT(0),
                     CvArr* _centers CV_DEFAULT(0), double* compactness CV_DEFAULT(0) );

从声明中可以看出,cvKMeans2函数是可以得到类别中心的。只是被默认设置为0.

在文档中的代码进行适当的修改,可以获得类别中心。

#include "cv.h"
#include <iostream>
#include <time.h>
#include "highgui.h"
using namespace std;

 

int main( int argc, char** argv )
{
#define MAX_CLUSTERS 5
 CvScalar color_tab[MAX_CLUSTERS];
 IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );
 srand((unsigned)time(NULL));           //使用时间作为种子函数
 CvRNG rng = cvRNG(rand());           
// CvRNG rng = cvRNG(0xffffffff);       //文档中的方式

 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);

 cvNamedWindow( "clusters", 1 );

// for(;;)    //将死循环去掉,如果用死循环,关闭clusters窗口后,重新显示将出现错误
 {
  int k;// cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1;
  int cluster_count = 5;
  int i, sample_count = cvRandInt(&rng)00 + 1;
  CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 );
  CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 );
  CvMat* centers = cvCreateMat(cluster_count, 1, CV_32FC2);  

   //类别中心数目换成sample_count后得到的结果不正确

 

  
  for( k = 0; k < cluster_count; k++ )
  {
   CvPoint center;
   CvMat point_chunk;
   center.x = cvRandInt(&rng)%img->width;
   center.y = cvRandInt(&rng)%img->height;
   cvGetRows( points, &point_chunk, k*sample_count/cluster_count,
    k == cluster_count - 1 ? sample_count : (k+1)*sample_count/cluster_count );
   cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL,
    cvScalar(center.x,center.y,0,0),
    cvScalar(img->width/6, img->height/6,0,0) );
  }

  
  for( i = 0; i < sample_count/2; i++ )
  {
   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 );
  }

//上面的代码,对point赋值做的很复杂。可以做的简单,不知道这样做有什么好处。

 

  cvKMeans2( points, cluster_count, clusters,cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 2.0 ),
   0, 0, 0,centers
   );    //只是简单的加入类别中心的数组

 

  cvZero( img );

//测试centers大小是否被改变,测试结果是没有

  cout<<centers->cols<<"  "<<centers->rows<<"  "<<sample_count<<endl;

  
  double center_x[5]={0.0};  //自己算分类以后各个类的中心
  double center_y[5]={0.0};  //用于判断centers得到的结果是否正确
  int c_num[5]={0,0,0,0,0};

 

  for( i = 0; i < sample_count; i++ )
  {
   CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i];
   
   CvPoint2D32f pt1 = ((CvPoint2D32f*)centers->data.fl)[i];  //不知道源代码中centers是怎么赋值的

  //这里不是cluster_count大小的数目

   if (pt1.x>1&&pt1.y>1&&pt1.x<img->width&&pt1.y<img->height )
   {
    cout<<i<<"  "<<pt1.x<<"  "<<pt1.y<<endl;
   }
   
   int cluster_idx = clusters->data.i[i];
   
   center_x[cluster_idx] += pt.x;
   center_y[cluster_idx] += pt.y;
         c_num[cluster_idx]++;
   cvCircle( img, cvPointFrom32f(pt), 2, color_tab[cluster_idx], CV_FILLED );
   cvCircle(img,cvPointFrom32f(pt1), 5, color_tab[cluster_idx], CV_FILLED );
   
  }
  for (i=0; i<cluster_count; i++)
  {
   if (c_num[i] != 0)
   {
    center_x[i] = center_x[i]/c_num[i];
    center_y[i] = center_y[i]/c_num[i];
    cvCircle(img,cvPoint(center_x[i],center_y[i]),7, CV_RGB(255,255,255),1);
   }
  }
 // 
  cvReleaseMat( &points );
  cvReleaseMat( &clusters );
  cvReleaseMat(&centers);

  cvShowImage( "clusters", img );

  int key = cvWaitKey(10000000);//等待时间,显示cluster窗口的时间。
//  if( key == 27 ) // 'ESC'
//   break;
 }
 cvReleaseImage(&img);
 cvDestroyWindow("clusters");
}

测试的结果如下:

K均值算法(代码)

从图片中,我们可以看出,计算的中心和centers给出的图像中心基本吻合。

 

但是在不知道源码的实现原理的条件下,最好不要使用centers的方法。


原创粉丝点击