• cvKMeans2函数用法概述


         一般情况下,我们通过C++/Matlab/Python等语言进行实现K-means算法,结合近期我刚刚学的C++,先从C++实现谈起,C++里面我们一般采用的是OpenCV库中写好的K-means函数,即cvKmeans2,首先来看函数原型:

         从OpenCV manual看到的是:
         int cvKMeans2(const CvArr* samples, int nclusters,
            CvArr* labels, CvTermCriteria termcrit,
            int attempts=1, CvRNG* rng=0,int flags=0,
            CvArr* centers=0,double* compactness=0);


        由于除去已经确定的参数,我们自己需要输入的为:
    void cvKMeans2( 
        const CvArr* samples, //输入样本的浮点矩阵,每个样本一行。 
        int cluster_count,  //所给定的聚类数目 
         * labels,    //输出整数向量:每个样本对应的类别标识 ,注意:该变量的行数必须与samples的行数是一致的
         CvTermCriteria termcrit //指定聚类的最大迭代次数和/或精度(两次迭代引起的聚类中心的移动距离)
     ); 

    一个例程如下所示:

     1 #ifdef _CH_
     2 #pragma package <opencv>
     3 #endif
     4 
     5 #define CV_NO_BACKWARD_COMPATIBILITY
     6 
     7 #ifndef _EiC
     8 #include "cv.h"
     9 #include "highgui.h"
    10 #include <stdio.h>
    11 #endif
    12 
    13 int main( int argc, char** argv )
    14 {
    15     #define MAX_CLUSTERS 5    //设置类别的颜色,个数(《=5)
    16     CvScalar color_tab[MAX_CLUSTERS];
    17     IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );
    18     CvRNG rng = cvRNG(-1);
    19     CvPoint ipt;
    20 
    21     color_tab[0] = CV_RGB(255,0,0);
    22     color_tab[1] = CV_RGB(0,255,0);
    23     color_tab[2] = CV_RGB(100,100,255);
    24     color_tab[3] = CV_RGB(255,0,255);
    25     color_tab[4] = CV_RGB(255,255,0);
    26 
    27     cvNamedWindow( "clusters", 1 );
    28 
    29     for(;;)
    30     {
    31         char key;
    32         int k, cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1;
    33         int i, sample_count = cvRandInt(&rng)%1000 + 1;
    34         CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 );//这里实际上的列数可以是很多列,并不一定仅仅是局限于这种两列(因为这里每列是一个二元数组)                             
    35 CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 ); 36 cluster_count = MIN(cluster_count, sample_count); 37 38 /** generate random sample from multigaussian distribution */ 39 for( k = 0; k < cluster_count; k++ ) 40 { 41 CvPoint center; 42 CvMat point_chunk; 43 center.x = cvRandInt(&rng)%img->width; 44 center.y = cvRandInt(&rng)%img->height; 45 cvGetRows( points, &point_chunk, k*sample_count/cluster_count, 46 k == cluster_count - 1 ? sample_count : 47 (k+1)*sample_count/cluster_count, 1 ); 48 49 cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL, 50 cvScalar(center.x,center.y,0,0), 51 cvScalar(img->width*0.1,img->height*0.1,0,0)); 52 } 53 54 /** shuffle samples */ 55 for( i = 0; i < sample_count/2; i++ ) 56 { 57 CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count; 58 CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count; 59 CvPoint2D32f temp; 60 CV_SWAP( *pt1, *pt2, temp ); 61 } 62 63 printf( "iterations=%d ", cvKMeans2( points, cluster_count, clusters, 64 cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 ), 65 5, 0, 0, 0, 0 ));//作者备注:cvKMeans2返回值为迭代次数?用户手册里面的意思应该是返回体现各个类别与类中心的差别的 compactness量度吧
    66 67 cvZero( img ); 68 69 for( i = 0; i < sample_count; i++ ) 70 { 71 int cluster_idx = clusters->data.i[i]; 72 ipt.x = (int)points->data.fl[i*2]; 73 ipt.y = (int)points->data.fl[i*2+1]; 74 cvCircle( img, ipt, 2, color_tab[cluster_idx], CV_FILLED, CV_AA, 0 ); 75 } 76 77 cvReleaseMat( &points ); 78 cvReleaseMat( &clusters ); 79 80 cvShowImage( "clusters", img ); 81 82 key = (char) cvWaitKey(0); 83 if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC' 84 break; 85 } 86 87 cvDestroyWindow( "clusters" ); 88 return 0; 89 } 90 91 #ifdef _EiC 92 main(1,"kmeans.c"); 93 #endif

    另外,关于KMeans与SIFT算子的一个结合应用,有一个很好的帖子就是参考[2]中的内容。

    此处为了说明其对KMeans使用的理解,将其第四部分step2内容备份于此。

    Step2——Kmeans应用

      Step1里面的feature只是“预备单词”,在成为单词之前还要通过Step2生成“单词表”和Step3将“文档”中的“预备单词”找到“单词表”中最相近的“单词”替换之(并不是真正操作上的替换,只是当成“单词表”中的“单词”统计出来而已)。

      在Step2中,关键操作如下:

        

    ...
    CvMat *samples=cvCreateMat(featureNum, dims, CV_32FC1); //包含所有图片的所有feature信息的矩阵,featureNum个feature,每个feature为dims(128)维向量,每一维的元素类型为32位浮点数
    CvMat *clusters=cvCreateMat(featureNum, 1, CV_32SC1); //每个feature所在“质心”的指针(实际上本例程中没有用到该信息)
    CvMat *centers=cvCreateMat(k, dims, CV_32FC1); //“质心”信息的数组,k个“质心”每个质心都是dims(128)维向量,每一维的元素类型为32位浮点数
    cvSetZero(clusters); //将矩阵初始化为0
    cvSetZero(centers); //将矩阵初始化为0
    while(file.ReadString(strLine)) 
    {
        ...
        n = import_features(CIni::CStrToChar(fileName), FEATURE_LOWE, &features); //导入feature文件,n为导入的feature个数
        ...
        //将feature文件内所有feature信息存入samples矩阵结构内
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < dims; j++)
            {
                samples->data.fl[temp++] = features[i].descr[j];
            }
        }
    }
    cvKMeans2(samples, k, clusters,cvTermCriteria(CV_TERMCRIT_EPS,10,1.0), 3, (CvRNG *)0, KMEANS_USE_INITIAL_LABELS, centers); //Kmeans聚类
    ...
    cvSave(CIni::CStrToChar(ini.getWordListFilePath()), centers); //保存单词表
    ...
    

     其中关键函数当然是import_features(...)和cvKMeans2(...),前者是sift源码里的方法,用来导入feature文件使之成为内存数据结构,后者是Opencv里的kmeans算法之一(cvKMeans2(...)内部调用了kmeans(...))。

    Reference

    [1]聚类算法——K-means(下) http://www.cnblogs.com/moondark/archive/2012/03/08/2385870.html

    [2]基于SIFT+Kmeans+LDA的图片分类器的实现 http://www.cnblogs.com/freedomshe/archive/2012/04/24/2468747.html

  • 相关阅读:
    44 软件开发中的一些思考
    43 编译原理及cmake使用手册学习
    42 github 开源代码 ——README.md语法/相关操作等
    41 软件工程实施-以数字化测量系统为例
    40 VSCode下.json文件的编写——(1) linux/g++ (2).json中参数与预定义变量的意义解释
    css3 3D盒子效果
    css3实现无缝滚动效果
    js一些方法的扩展
    扩展原生js的一些方法
    递归
  • 原文地址:https://www.cnblogs.com/jiayouwyhit/p/3670357.html
Copyright © 2020-2023  润新知