opencv kmans 小结

K_means

参考自一个不错的博客
http://coolshell.cn/articles/7779.html
问题

K-Means算法主要解决的问题如下图所示。我们可以看到,在图的左边有一些点,我们用肉眼可以看出来有四个点群,但是我们怎么通过计算机程序找出这几个点群来呢?于是就出现了我们的K-Means算法(Wikipedia链接


算法概要

这个算法其实很简单,如下图所示:


从上图中,我们可以看到,A, B, C, D, E 是五个在图中点。而灰色的点是我们的种子点,也就是我们用来找点群的点。有两个种子点,所以K=2。

然后,K-Means的算法如下:

  1. 随机在图中取K(这里K=2)个种子点。
  2. 然后对图中的所有点求到这K个种子点的距离,假如点Pi离种子点Si最近,那么Pi属于Si点群。(上图中,我们可以看到A,B属于上面的种子点,C,D,E属于下面中部的种子点)
  3. 接下来,我们要移动种子点到属于他的“点群”的中心。(见图上的第三步)
  4. 然后重复第2)和第3)步,直到,种子点没有移动(我们可以看到图中的第四步上面的种子点聚合了A,B,C,下面的种子点聚合了D,E)。

这个算法很简单,但是有些细节我要提一下,求距离的公式我不说了,大家有初中毕业水平的人都应该知道怎么算的。我重点想说一下“求点群中心的算法”

求点群中心的算法

一般来说,求点群中心点的算法你可以很简的使用各个点的X/Y坐标的平均值。不过,我这里想告诉大家另三个求中心点的的公式:

1)Minkowski Distance 公式 —— λ 可以随意取值,可以是负数,也可以是正数,或是无穷大。



2)Euclidean Distance 公式 —— 也就是第一个公式 λ=2 的情况


 3)CityBlock Distance 公式 —— 也就是第一个公式 λ=1 的情况

 

这三个公式的求中心点有一些不一样的地方,我们看下图(对于第一个 λ 在 0-1之间)。


上面这几个图的大意是他们是怎么个逼近中心的,第一个图以星形的方式,第二个图以同心圆的方式,第三个图以菱形的方式。

K-Means的演示

如果你以”K Means Demo“为关键字到Google里查你可以查到很多演示。这里推荐一个演示

http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/AppletKM.html

操作是,鼠标左键是初始化点,右键初始化“种子点”,然后勾选“Show History”可以看到一步一步的迭代。

注:这个演示的链接也有一个不错的 K Means Tutorial 。

K-Means ++ 算法

K-Means主要有两个最重大的缺陷——都和初始值有关:

  •  K 是事先给定的,这个 K 值的选定是非常难以估计的。很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。( ISODATA 算法通过类的自动合并和分裂,得到较为合理的类型数目 K)
  • K-Means算法需要用初始随机种子点来搞,这个随机种子点太重要,不同的随机种子点会有得到完全不同的结果。(K-Means++算法可以用来解决这个问题,其可以有效地选择初始点)

我在这里重点说一下 K-Means++算法步骤:

  1. 先从我们的数据库随机挑个随机点当“种子点”。
  2. 对于每个点,我们都计算其和最近的一个“种子点”的距离D(x)并保存在一个数组里,然后把这些距离加起来得到Sum(D(x))。
  3. 然后,再取一个随机值,用权重的方式来取计算下一个“种子点”。这个算法的实现是,先取一个能落在Sum(D(x))中的随机值Random,然后用Random -= D(x),直到其<=0,此时的点就是下一个“种子点”。
  4. 重复第(2)和第(3)步直到所有的K个种子点都被选出来。
  5. 进行K-Means算法。

相关的代码你可以在这里找到“implement the K-means++ algorithm”(墙) 另,Apache 的通用数据学库也实现了这一算法

K-Means 算法应用

看到这里,你会说,K-Means算法看来很简单,而且好像就是在玩坐标点,没什么真实用处。而且,这个算法缺陷很多,还不如人工呢。是的,前面的例子只是玩二维坐标点,的确没什么意思。但是你想一下下面的几个问题:

1)如果不是二维的,是多维的,如5维的,那么,就只能用计算机来计算了。

2)二维坐标点的X, Y 坐标,其实是一种向量,是一种数学抽象。现实世界中很多属性是可以抽象成向量的,比如,我们的年龄,我们的喜好,我们的商品,等等,能抽象成向量的目的就是可以让计算机知道某两个属性间的距离。如:我们认为,18岁的人离24岁的人的距离要比离12岁的距离要近,鞋子这个商品离衣服这个商品的距离要比电脑要近,等等。

只要能把现实世界的物体的属性抽象成向量,就可以用K-Means算法来归类了

在 《k均值聚类(K-means)》 这篇文章中举了一个很不错的应用例子,作者用亚洲15支足球队的2005年到1010年的战绩做了一个向量表,然后用K-Means把球队归类,得出了下面的结果,呵呵。

  • 亚洲一流:日本,韩国,伊朗,沙特
  • 亚洲二流:乌兹别克斯坦,巴林,朝鲜
  • 亚洲三流:中国,伊拉克,卡塔尔,阿联酋,泰国,越南,阿曼,印尼

其实,这样的业务例子还有很多,比如,分析一个公司的客户分类,这样可以对不同的客户使用不同的商业策略,或是电子商务中分析商品相似度,归类商品,从而可以使用一些不同的销售策略,等等。

最后给一个挺好的算法的幻灯片:http://www.cs.cmu.edu/~guestrin/Class/10701-S07/Slides/clustering.pdf



k_means算法参考自博客

K均值聚类算法在cxcoer中, 因为它在ML库诞生之前就存在了.K均值尝试找到数据的自然类别.用户设置类别个数, K均值迅速地找到"好的"类别中心."好的"意味着聚类中心位于数据的自然类别中心.K均值是最常用的聚类计数之一, 与高斯混合中的期望最大化算法(在ML库中实现为CvEM)很相似, 也与均值漂移算法(在CV库中实现为cvMeanShift())相似.K均值是一个迭代算法, 在OpenCV中采用的是Lloyd算法, 也叫Voronoi迭代.算法运行如下.
1.输入数据集合和类别数K(由用户指定)
2.随机分配类别中心点的位置
3.将每个点放入离它最近的类别中心点所在的集合.
4.移动类别中心点到它所在集合的中心
5.转到第3步, 直到收敛.
图13 - 5展示了K均值是怎么工作的.在这个例子中, 只用两次迭代就达到了收敛.在现实数据中, 算法经常很快的收敛, 但是有的时候需要迭代的次数比较多.



问题和解决方案

K均值是一个及其高效的聚类算法,但是它也有以下3个问题.

1.它不保证能找到定位聚类中心的最佳方案,但是它能保证能收敛到某个解决方案(例如迭代不再无限继续下去).

2.K均值无法指出应该使用多少类别.在同意数据集中,例如对图13-5中的例子,选择两个类别和选择四个类别,得到的结果是不一样的,甚至是不合理的.

3.K均值假设空间的协方差矩阵不会影响结果,或者已经归一化(参考Mahalanobis距离的讨论).

这三个问题都有"解决办法".这些解决方法中最好的两种都是基于"劫色数据的方差".在K均值中,每个聚类中线拥有他的数据点,我们计算这些点的方差,最好的聚类在不引起太大的复杂度的情况下使方差达到最小.所以,列出的问题可以改进如下.

1.多进行几次K均值,每次初始的聚类中心点都不一样(很容易做到,因为OpenCV随机选择中心点),最后选择方差最小的那个结果

2.首先将类别数设为1,然后提高类别数(到达某个上限),每次聚类的时候使用前面提到的方法1.一般情况下,总方差会很快下降,直到到达一个拐点;这意味着再加一个新的聚类中心不会显著地减少总方差.在拐点处停止,保存此时的类别数.

3.将数据乘以逆协方差矩阵.例如:如果输入向量是按行排列,没行一个数据点,则通过计算新的数据向量D*,D* = D∑-1/2来归一化数据空间.

K均值代码

K均值函数的调用很简单:

void cvKMeans2(const CvArr* samples, int cluster_count,CvArr* labels,CvTrtmCriteria termcrit);

数组sample是一个多维的数据样本矩阵,每行一个数据样本.这里有一点点微妙,数据样本可以是浮点型CV_32FC1向量,也可以是CV_32FC2,CV_32FC3和CV_32FC(k)的多维向量.参数cluster_count指定类别数,返回向量包含每个点最后的类别索引.前面已经讨论了terncrit.


代码如下:


#include <QCoreApplication>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
#define MAX_CLUSTER 5
    CvScalar color_table[MAX_CLUSTER];
    IplImage* img = cvCreateImage(cvSize(500, 500), 8, 3);
    CvRNG rng = cvRNG(0xffffffff);
    color_table[0] = CV_RGB(255, 0, 0);
    color_table[1] = CV_RGB(0, 255, 0);
    color_table[2] = CV_RGB(100, 100, 255);
    color_table[3] = CV_RGB(255, 0, 255);
    color_table[4] = CV_RGB(255, 255, 0);
    cvNamedWindow("clusters", 1);
    for (;;)
    {
        int k, cluster_count = cvRandInt(&rng) % MAX_CLUSTER + 1; //类别个数 1~5
        int i, sample_count = cvRandInt(&rng) % 1000 + 1;  //样本个数 1~1000
        CvMat* points = cvCreateMat(sample_count, 1, CV_32FC2); //创建sample_count行1列的双通道矩阵 用于存储数据样本
        CvMat* clusters = cvCreateMat(sample_count, 1, CV_32SC1);//创建sample_count行1列的矩阵 用来存储数据标签
        //随机生成样本多元高斯分布
        //样本总数为sample_count 类别总数为cluster_count
        //样本矩阵为points,先按类别分成cluster_count份 每一份数据的个数为sample_count/cluster_count
        //然后按类别随机矩阵填充样本矩阵,第一类填充矩阵的0~sample_count/cluster_count行
        //第二类填充样本矩阵的sample_count/cluster_count~sample_count/cluster_count*2行,以此类推直到填充满所有矩阵,每一类的样本个数是一样的
        for (k = 0; k < cluster_count; k++)
        {
            CvPoint center;  //为图像中的随机点
            CvMat point_chunk;
            center.x = cvRandInt(&rng) % img->width;
            center.y = cvRandInt(&rng) % img->height;
            //返回数组在一定跨度的行
            //points为输入数组,point_chunk返回数组
            //开始行为k*sample_count/cluster_count
            //结束行为 当k== cluster-1时 为 第samplecount行 否则为(k+1)*sample_count/cluster_count
            cvGetRows(points, &point_chunk, k*sample_count / cluster_count,
                k == cluster_count - 1 ? sample_count :
                (k + 1)*sample_count / cluster_count);
            //point_chunk输出数组,CV_RAND_NORMAL分布类型为正态分布或者高斯分布
            //cvScalar(center.x,center.y,0,0)随机数的平均值
            // cvScalar(img->width/6,img->height/6,0,0)如果是正态分布它是随机数的标准差
            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++)
        {
            //在points样本矩阵中随机取两个样本交换位置
            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);
        }
        //points样本矩阵,cliuster_count分类数,输出向量clusters,最后一个参数指定精度
        cvKMeans2(points, cluster_count, clusters, cvTermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0));
        cvZero(img);
        for (i = 0; i < sample_count; i++)
        {
            CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i];
            int cluster_idx = clusters->data.i[i];
            cvCircle(img, cvPointFrom32f(pt), 2, color_table[cluster_idx], CV_FILLED);
        }
        cvReleaseMat(&points);
        cvReleaseMat(&clusters);
        cvShowImage("clusters", img);
        int key = cvWaitKey(0);
        if (key == 27)
            break;
    }
    return a.exec();
}


修改成opencv3.0代码
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/core/types.hpp>
#include <time.h>
#include <iostream>
using namespace cv;
using namespace std;
#define MAX_CLUSTER 5
int main()
{
    Scalar color_table[MAX_CLUSTER];
    Mat img = Mat(Size(500, 500), CV_8UC3);
    srand((unsigned)time(NULL));
    RNG rng;
    Mat centers;
    color_table[0] = Scalar(255, 0, 0);
    color_table[1] = Scalar(0, 255, 0);
    color_table[2] = Scalar(100, 100, 255);
    color_table[3] = Scalar(255, 0, 255);
    color_table[4] = Scalar(255, 255, 0);
    namedWindow("clusters", 1);
    for (;;)
    {
        int k, cluster_count = rand() % MAX_CLUSTER + 1; //类别个数 1~5
        int i, sample_count = rand() % 1000 + 1;  //样本个数 1~1000
        Mat points = Mat(sample_count, 1, CV_32FC2); //创建sample_count行1列的双通道矩阵 用于存储数据样本
        Mat clusters = Mat(sample_count, 1, CV_32SC1);//创建sample_count行1列的矩阵 用来存储数据标签
        //随机生成样本多元高斯分布
        //样本总数为sample_count 类别总数为cluster_count
        //样本矩阵为points,先按类别分成cluster_count份 每一份数据的个数为sample_count/cluster_count
        //然后按类别随机矩阵填充样本矩阵,第一类填充矩阵的0~sample_count/cluster_count行
        //第二类填充样本矩阵的sample_count/cluster_count~sample_count/cluster_count*2行,以此类推直到填充满所有矩阵,每一类的样本个数是一样的
        for (k = 0; k < cluster_count; k++)
        {
            Point center;  //为图像中的随机点
            
            center.x = rand() % img.cols;
            center.y = rand() % img.rows;
            //返回数组在一定跨度的行
            //points为输入数组,point_chunk返回数组
            //开始行为k*sample_count/cluster_count
            //结束行为 当k== cluster-1时 为 第samplecount行 否则为(k+1)*sample_count/cluster_count
            Mat point_chunk = points(Range(k*sample_count / cluster_count,
                k == cluster_count - 1 ? sample_count :
                (k + 1)*sample_count / cluster_count), Range(points.cols - 1, points.cols));
            //point_chunk输出数组,CV_RAND_NORMAL分布类型为正态分布或者高斯分布
            //cvScalar(center.x,center.y,0,0)随机数的平均值
            // cvScalar(img->width/6,img->height/6,0,0)如果是正态分布它是随机数的标准差
            rng.fill(point_chunk, RNG::NORMAL,
                Scalar(center.x, center.y),
                Scalar(img.cols / 6, img.rows / 6));
        }
        //样本重新排序
        for (i = 0; i < sample_count / 2; i++)
        {
            //在points样本矩阵中随机取两个样本交换位置
            swap(points.at<Point2f>(rand() % sample_count), points.at<Point2f>(rand() % sample_count));
        }
        //points样本矩阵,cliuster_count分类数,输出向量clusters,最后一个参数指定精度
        kmeans(points, cluster_count, clusters, TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 10, 1.0), 3, KMEANS_PP_CENTERS, centers);
        img = Mat::zeros(img.rows, img.colsCV_8UC3);
        
        for (i = 0; i < sample_count; i++)
        {
            Point pt = points.at<Point2f>(i);
            int cluster_idx = clusters.at<int>(i);
            circle(img, pt, 2, color_table[cluster_idx], FILLED, LINE_AA);
        }
        points.release();
        clusters.release();
        imshow("clusters", img);
        int key = waitKey(0);
        if (key == 27)
            break;
    }
    return 0;
}

效果:



opencv自带的源码:
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
// static void help()
// {
//     cout << "\nThis program demonstrates kmeans clustering.\n"
//             "It generates an image with random points, then assigns a random number of cluster\n"
//             "centers and uses kmeans to move those cluster centers to their representitive location\n"
//             "Call\n"
//             "./kmeans\n" << endl;
// }
int main(int /*argc*/, char** /*argv*/)
{
    const int MAX_CLUSTERS = 5;
    Scalar colorTab[] =
    {
        Scalar(0, 0, 255),
        Scalar(0, 255, 0),
        Scalar(255, 100, 100),
        Scalar(255, 0, 255),
        Scalar(0, 255, 255)
    };
    Mat img(500, 500, CV_8UC3);
    RNG rng(12345);
    for (;;)
    {
        int k, clusterCount = rng.uniform(2, MAX_CLUSTERS + 1);
        int i, sampleCount = rng.uniform(1, 1001);
        Mat points(sampleCount, 1, CV_32FC2), labels;
        clusterCount = MIN(clusterCount, sampleCount);
        Mat centers;
        /* generate random sample from multigaussian distribution */
        for (k = 0; k < clusterCount; k++)
        {
            Point center;
            center.x = rng.uniform(0, img.cols);
            center.y = rng.uniform(0, img.rows);
            Mat pointChunk = points.rowRange(k*sampleCount / clusterCount,
                k == clusterCount - 1 ? sampleCount :
                (k + 1)*sampleCount / clusterCount);
            rng.fill(pointChunk, RNG::NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
        }
        randShuffle(points, 1, &rng);
        kmeans(points, clusterCount, labels,
            TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0),
            3, KMEANS_PP_CENTERS, centers);
        img = Scalar::all(0);
        for (i = 0; i < sampleCount; i++)
        {
            int clusterIdx = labels.at<int>(i);
            Point ipt = points.at<Point2f>(i);
            circle(img, ipt, 2, colorTab[clusterIdx], FILLED, LINE_AA);
        }
        imshow("clusters", img);
        char key = (char)waitKey();
        if (key == 27 || key == 'q' || key == 'Q') // 'ESC'
            break;
    }
    return 0;
}

在图像处理中的一个简单例子:

代码是opencv 1.0的

OpenCV 函数使用

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

下面还用到了函数CvMat* cvReshape( const CvArr* arr, CvMat* header, int new_cn, int new_rows=0 );

arr 输入的数组. header 被添充的矩阵头 new_cn 新的通道数.new_cn = 0 意味着不修改通道数 new_rows 新的行数。

 如果new_rows = 0保持原行数不修改否则根据 new_cn 值修改输出数组 函数 cvReshape 初始化 CvMat 头header 以便于让头指向修改后的形状(但数据保持原样)-也就是说修改通道数,修改行数或者两者者改变。


代码如下:

#include "cxcore.h"

#include "highgui.h"


void main()

{

IplImage* img = cvLoadImage( "test.jpg", 1);//三通道图像

int total= img->height*img->width;

int cluster_num = 2;

CvMat *row = cvCreateMat( img->height,img->width,CV_32FC3 );

cvConvert(img,row);//转一下类型!

CvMat *clusters = cvCreateMat( total, 1, CV_32SC1 );

cvReshape(row,row,0,total);//修改矩阵的形状,每个数据一行,使row指向修改后的数据,不修改通道数

cvKMeans2( row, cluster_num, clusters,cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 ));

cvReshape(clusters,clusters,0,img->width);//聚类完的结果再reshape回来方便看


int i=0,j=0;

CvScalar s;

IplImage* resImg = cvCreateImage( cvSize(img->width,img->height), 8, 1 );//生成用来显示结果的图像

s=cvGet2D(img,i,j);

for(i=0;i<img->height;i++)

{

for (j=0;j<img->width;j++)

{

if (clusters->data.i[i*img->width+j]==1)

{

s.val[0]=255;

s.val[1]=255;

s.val[2]=255;

cvSet2D(resImg,i,j,s);//注意循环顺序

}

else

{

s.val[0]=0;

s.val[1]=0;

s.val[2]=0;

cvSet2D(resImg,i,j,s);

}

}

}

cvShowImage( "original", img );

cvShowImage( "clusters", resImg );


int key = cvWaitKey(0);


cvReleaseImage(&img);//记得释放内存

cvReleaseImage (&resImg);

cvReleaseMat(&row);

cvReleaseMat(&clusters);

}


改成opencv2.4.9版本

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/core/types.hpp>
#include <time.h>
#include <iostream>
using namespace std;
using namespace cv;
void main()
{
    Mat img = imread("test.jpg", 1);//三通道图像
    int total = img.rows*img.cols;
    int cluster_num = 2;
    Mat row = Mat(img.rows, img.colsCV_32FC3);
    img.convertTo(row, CV_32FC3);//转一下类型!
    Mat clusters = Mat(total, 1, CV_32SC1);
    row = Mat(row).reshape(0, total);//修改矩阵的形状,每个数据一行,使row指向修改后的数据,不修改通道数
    kmeans(row, cluster_num, clusters, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0), 3, KMEANS_RANDOM_CENTERS, noArray());
    clusters = Mat(clusters).reshape(0, img.rows);//聚类完的结果再reshape回来方便看
    int i = 0, j = 0;
    Point3f s;
    Mat resImg = Mat(Size(img.cols, img.rows), CV_8UC1);//生成用来显示结果的图像
    //s = img.at<Point3f>(i, j);
    for (i = 0; i < img.rows; i++)
    {
        for (j = 0; j < img.cols; j++)
        {
            if (clusters.at<__int32>(i,j) == 1)
            {
                resImg.at<uchar>(i, j) = 255;
            }
            else
            {
                resImg.at<uchar>(i, j) = 0;
            }
        }
    }
    imshow("original", img);
    imshow("clusters", resImg);
    int key = waitKey(0);
    img.release();//记得释放内存
    resImg.release();
    row.release();
    clusters.release();
}


效果


k_means另一图像处理例子

参考自:http://blog.csdn.net/yangtrees/article/details/7971405

代码如下:

#include <string>  
#include <iostream>  
#include <math.h>  
#include <vector>  
#include <map>  
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>  
#include <opencv2/core/core.hpp> 
#define ClusterNum (6)  
using namespace cv;
using namespace std;
string filename = "test.jpg";
Mat clustering(Mat src)
{
    int row = src.rows;
    int col = src.cols;
    unsigned long int size = row*col;
    Mat clusters(size, 1, CV_32SC1);    //clustering Mat, save class label at every location;  
    //convert src Mat to sample srcPoint.  
    Mat srcPoint(size, 1, CV_32FC3);
    Vec3f* srcPoint_p = (Vec3f*)srcPoint.data;//  
    Vec3f* src_p = (Vec3f*)src.data;
    unsigned long int i;
    for (i = 0; i < size; i++)
    {
        *srcPoint_p = *src_p;
        srcPoint_p++;
        src_p++;
    }
    Mat center(ClusterNum, 1, CV_32FC3);
    double compactness;//compactness to measure the clustering center dist sum by different flag  
    compactness = kmeans(srcPoint, ClusterNum, clusters,
        TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1), ClusterNum,
        KMEANS_PP_CENTERS, center);
    cout << "center row:" << center.rows << " col:" << center.cols << endl;
    for (int y = 0; y < center.rows; y++)
    {
        Vec3f* imgData = center.ptr<Vec3f>(y);
        for (int x = 0; x < center.cols; x++)
        {
            cout << imgData[x].val[0] << " " << imgData[x].val[1] << " " << imgData[x].val[2] << endl;
        }
        cout << endl;
    }
    double minH, maxH;
    minMaxLoc(clusters, &minH, &maxH);          //remember must use "&"  
    cout << "H-channel min:" << minH << " max:" << maxH << endl;
    int* clusters_p = (int*)clusters.data;
    //show label mat  
    Mat label(src.size(), CV_32SC1);
    int* label_p = (int*)label.data;
    //assign the clusters to Mat label  
    for (i = 0; i < size; i++)
    {
        *label_p = *clusters_p;
        label_p++;
        clusters_p++;
    }
    Mat label_show;
    label.convertTo(label_show, CV_8UC1);
    normalize(label_show, label_show, 255, 0, NORM_MINMAX);
    imshow("label", label_show);
    map<int, int> count;       //map<id,num>  
    map<int, Vec3f> avg;       //map<id,color>  
    //compute average color value of one label  
    for (int y = 0; y < row; y++)
    {
        const Vec3f* imgData = src.ptr<Vec3f>(y);
        int* idx = label.ptr<int>(y);
        for (int x = 0; x < col; x++)
        {
            avg[idx[x]] += imgData[x];
            count[idx[x]] ++;
        }
    }
    //output the average value (clustering center)  
    //计算所得的聚类中心与kmean函数中center的第一列一致,  
    //以后可以省去后面这些繁复的计算,直接利用center,  
    //但是仍然不理解center的除第一列以外的其他列所代表的意思  
    for (i = 0; i < ClusterNum; i++)
    {
        avg[i] /= count[i];
        if (avg[i].val[0] > 0 && avg[i].val[1] > 0 && avg[i].val[2] > 0)
        {
            cout << i << ": " << avg[i].val[0] << " " << avg[i].val[1] << " " << avg[i].val[2] << " count:" << count[i] << endl;
        }
    }
    //show the clustering img;  
    Mat showImg(src.size(), CV_32FC3);
    for (int y = 0; y < row; y++)
    {
        Vec3f* imgData = showImg.ptr<Vec3f>(y);
        int* idx = label.ptr<int>(y);
        for (int x = 0; x < col; x++)
        {
            int id = idx[x];
            imgData[x].val[0] = avg[id].val[0];
            imgData[x].val[1] = avg[id].val[1];
            imgData[x].val[2] = avg[id].val[2];
        }
    }
    normalize(showImg, showImg, 1, 0, NORM_MINMAX);
    imshow("show", showImg);
    waitKey();
    return label;
}
int main()
{
    Mat img = imread(filename, 1);
    GaussianBlur(img, img, Size(3, 3), 0);
    img.convertTo(img, CV_32FC3);
    Mat pixId = clustering(img);
}


关于opencv 1.0 和 opencv2.4.9一些函数和变量的变化

cvSize -> Size

cvScalar -> Scalar 可以用point代替

cvNameWindows -> nameWindow新建窗

cvShow -> imShow

cvLoadImage -> imRead读取图像

cvRNG -> RNG 随机数产生器

cvRandInt(&rng) -> rng.uniform(min, max)产生均匀分布随机数

cvGetRow->Mat.rowRang获取一行

cvRangeArr -> rng.fill给使用随机数给叔祖父赋值

CV_SWAP -> swap交换像素的值

打乱数组的值可以使用rangshuffle

Mat.convertTo矩阵类型转换

normalize归一化


cvZero -> Mat::zeros产生0矩阵

mat::ones产生1矩阵

mat::eyes产生单位矩阵

cvReleaseMat(&mat) -> mat.ralease()

CvPoint2D32f -> point2f









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值