问题
K-Means算法主要解决的问题如下图所示。我们可以看到,在图的左边有一些点,我们用肉眼可以看出来有四个点群,但是我们怎么通过计算机程序找出这几个点群来呢?于是就出现了我们的K-Means算法(Wikipedia链接)
![]()
算法概要
这个算法其实很简单,如下图所示:
从上图中,我们可以看到,A, B, C, D, E 是五个在图中点。而灰色的点是我们的种子点,也就是我们用来找点群的点。有两个种子点,所以K=2。
然后,K-Means的算法如下:
- 随机在图中取K(这里K=2)个种子点。
- 然后对图中的所有点求到这K个种子点的距离,假如点Pi离种子点Si最近,那么Pi属于Si点群。(上图中,我们可以看到A,B属于上面的种子点,C,D,E属于下面中部的种子点)
- 接下来,我们要移动种子点到属于他的“点群”的中心。(见图上的第三步)
- 然后重复第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++算法步骤:
- 先从我们的数据库随机挑个随机点当“种子点”。
- 对于每个点,我们都计算其和最近的一个“种子点”的距离D(x)并保存在一个数组里,然后把这些距离加起来得到Sum(D(x))。
- 然后,再取一个随机值,用权重的方式来取计算下一个“种子点”。这个算法的实现是,先取一个能落在Sum(D(x))中的随机值Random,然后用Random -= D(x),直到其<=0,此时的点就是下一个“种子点”。
- 重复第(2)和第(3)步直到所有的K个种子点都被选出来。
- 进行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均值是怎么工作的.在这个例子中, 只用两次迭代就达到了收敛.在现实数据中, 算法经常很快的收敛, 但是有的时候需要迭代的次数比较多.
![](https://img-blog.csdn.net/20130608095415906)
问题和解决方案
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.cols, CV_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.cols, CV_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
![]()
![]()