0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

K-means算法原理理论+opencv实现

lviY_AI_shequ 来源:未知 作者:李倩 2018-04-08 09:49 次阅读

写在前面:之前想分类图像的时候有看过k-means算法,当时一知半解的去使用,不懂原理不懂使用规则。。。显然最后失败了,然后看了《机器学习》这本书对k-means算法有了理论的认识,现在通过贾志刚老师的视频有了实际应用的理解。

k-means算法原理

注:还是和之前一样,核心都是别人的,我只是知识的搬运工并且加上了自己的理解。弄完之后发现理论部分都是别人的~~没办法这算法太简单了。。。

k-means含义:无监督的聚类算法。

无监督:就是不需要人干预,拿来一大批东西直接放进算法就可以进行分类。SVM和神经网络都是需要提前训练好然后再进行分类这样就是监督学习。而k-means和K近邻都是无监督学习。

聚类:通过一个中心聚在一起的分类,比如给你一批数据让你分成三类,那就是三个中心,那这三个中心代表的意思就是三个类。

k-means步骤:

从上图中,我们可以看到,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坐标的平均值。不过,我这里想告诉大家另三个求中心点的的公式:

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

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

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

k-means的缺点:

在 K-means 算法中 K 是事先给定的,这个 K 值的选定是非常难以估计的。很多时候,事先并不知道给定的数据集应该分成多少个类别才最合适。这也是 K-means 算法的一个不足。

在 K-means 算法中,首先需要根据初始聚类中心来确定一个初始划分,然后对初始划分进行优化。这个初始聚类中心的选择对聚类结果有较大的影响,一旦初始值选择的不好,可能无法得到有效的聚类结果,这也成为 K-means算法的一个主要问题。

从 K-means 算法框架可以看出,该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。

opencv+K-means

没什么好写的,因为这个k-means比较简单,主要说的就是函数参数的应用而已:

voidRNG::fill(InputOutputArraymat, intdistType, InputArraya, InputArrayb, boolsaturateRange=false)

这个函数是对矩阵mat填充随机数,随机数的产生方式有参数2来决定,如果为参数2的类型为RNG::UNIFORM,则表示产生均一分布的随机数,如果为RNG::NORMAL则表示产生高斯分布的随机数。对应的参数3和参数4为上面两种随机数产生模型的参数。比如说如果随机数产生模型为均匀分布,则参数a表示均匀分布的下限,参数b表示上限。如果随机数产生模型为高斯模型,则参数a表示均值,参数b表示方程。参数5只有当随机数产生方式为均匀分布时才有效,表示的是是否产生的数据要布满整个范围(没用过,所以也没仔细去研究)。另外,需要注意的是用来保存随机数的矩阵mat可以是多维的,也可以是多通道的,目前最多只能支持4个通道。

voidrandShuffle(InputOutputArraydst, doubleiterFactor=1.,RNG*rng=0)

该函数表示随机打乱1D数组dst里面的数据,随机打乱的方式由随机数发生器rng决定。iterFactor为随机打乱数据对数的因子,总共打乱的数据对数为:dst.rows*dst.cols*iterFactor,因此如果为0,表示没有打乱数据。

Class TermCriteria

类TermCriteria 一般表示迭代终止的条件,如果为CV_TERMCRIT_ITER,则用最大迭代次数作为终止条件,如果为CV_TERMCRIT_EPS则用精度作为迭代条件,如果为CV_TERMCRIT_ITER+CV_TERMCRIT_EPS则用最大迭代次数或者精度作为迭代条件,看哪个条件先满足。

doublekmeans(InputArraydata, intK, InputOutputArraybestLabels, TermCriteriacriteria, intattempts, intflags, OutputArraycenters=noArray())

该函数为kmeans聚类算法实现函数。参数data表示需要被聚类的原始数据集合,一行表示一个数据样本,每一个样本的每一列都是一个属性;参数k表示需要被聚类的个数;参数bestLabels表示每一个样本的类的标签,是一个整数,从0开始的索引整数;参数criteria表示的是算法迭代终止条件;参数attempts表示运行kmeans的次数,取结果最好的那次聚类为最终的聚类,要配合下一个参数flages来使用;参数flags表示的是聚类初始化的条件。其取值有3种情况,如果为KMEANS_RANDOM_CENTERS,则表示为随机选取初始化中心点,如果为KMEANS_PP_CENTERS则表示使用某一种算法来确定初始聚类的点;如果为KMEANS_USE_INITIAL_LABELS,则表示使用用户自定义的初始点,但是如果此时的attempts大于1,则后面的聚类初始点依旧使用随机的方式;参数centers表示的是聚类后的中心点存放矩阵。该函数返回的是聚类结果的紧凑性,其计算公式为:

注意点一:

这是说个我自己不理解的地方:fill(InputOutputArraymat, intdistType, InputArraya, InputArrayb, boolsaturateRange=false)

这里的InputArraya, InputArrayb------>>>分别用了Scalar(center.x, center.y, 0, 0), Scalar(img.cols*0.05, img.rows*0.05, 0, 0)去替换

去查了一下手册:InputArray这个接口类可以是Mat、Mat_、Mat_、vector、vector>、vector。没有提到Scalar()可以使用

特意定义了一个:InputArray test = Scalar(1,1);这个又是可以的,定义Mat不行,Vector也不行,这个真的不知道什么原因,有时间得去看源码a,b的使用。

//----下面的定义都是错误的,运行的结果都不对,原因暂时不知道

Mat a = (Mat_(1, 2) << center.x, center.y);

Mat b = (Mat_(1, 2) << img.cols*0.05, img.rows*0.05);

InputArray a1 = Scalar(center.x, center.y);

InputArray b1 = Scalar(img.cols*0.05, img.rows*0.05);

Mat a2 = a1.getMat();

Mat b2 = b1.getMat();

Mat c(1, 2, CV_8UC1);

c = Scalar(center.x, center.y);

Mat c1(1, 2, CV_8UC1);

c1 = Scalar(img.cols*0.05, img.rows*0.05);

rng.fill(pointChunk, RNG::NORMAL, a, b, 0);

注意点二:

kmeans()函数的输入只接受data0.dims <= 2 && type == CV_32F && K > 0 ,

第一个dims一般都不会越界(三维不行)

第二个参数CV_32F == float,千万别带入CV_8U == uchar

第三个参数不用说了,设置的种类肯定是大于0的

注意点三:

opencv里面k-means函数的样本数据、标签、中心点的存储:

#include

#include

using namespace cv;

using namespace std;

int main(int argc, char** argv) {

Mat img(500, 500, CV_8UC3);

RNG rng(12345);

const int Max_nCluster = 5;

Scalar colorTab[] = {

Scalar(0, 0, 255),

Scalar(0, 255, 0),

Scalar(255, 0, 0),

Scalar(0, 255, 255),

Scalar(255, 0, 255)

};

//InputArray a = Scalar(1,1);

int numCluster = rng.uniform(2, Max_nCluster + 1);//随机类数

int sampleCount = rng.uniform(5, 1000);//样本点数量

Mat matPoints(sampleCount, 1, CV_32FC2);//样本点矩阵:sampleCount X 2

Mat labels;

Mat centers;

// 生成随机数

for (int k = 0; k < numCluster; k++) {

Point center;//随机产生中心点

center.x = rng.uniform(0, img.cols);

center.y = rng.uniform(0, img.rows);

Mat pointChunk = matPoints.rowRange( k*sampleCount / numCluster,

(k + 1)*sampleCount / numCluster);

//-----这句话的意思我不明白作用是什么,没意义啊!

/*Mat pointChunk = matPoints.rowRange(k*sampleCount / numCluster,

k == numCluster - 1 ? sampleCount : (k + 1)*sampleCount / numCluster);*/

//-----符合高斯分布的随机高斯

rng.fill(pointChunk, RNG::NORMAL, Scalar(center.x, center.y, 0, 0), Scalar(img.cols*0.05, img.rows*0.05, 0, 0));

}

randShuffle(matPoints, 1, &rng);//打乱高斯生成的数据点顺序

// 使用KMeans

kmeans(matPoints, numCluster, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1), 3, KMEANS_PP_CENTERS, centers);

// 用不同颜色显示分类

img = Scalar::all(255);

for (int i = 0; i < sampleCount; i++) {

int index = labels.at(i);

Point p = matPoints.at(i);

circle(img, p, 2, colorTab[index], -1, 8);

}

// 每个聚类的中心来绘制圆

for (int i = 0; i < centers.rows; i++) {

int x = centers.at(i, 0);

int y = centers.at(i, 1);

printf("c.x= %d, c.y=%d", x, y);

circle(img, Point(x, y), 40, colorTab[i], 1, LINE_AA);

}

imshow("KMeans-Data-Demo", img);

waitKey(0);

return 0;

}

分类代码:

#include

#include

using namespace cv;

using namespace std;

RNG rng(12345);

const int Max_nCluster = 5;

int main(int argc, char** argv) {

//Mat img(500, 500, CV_8UC3);

Mat inputImage = imread("1.jpg");

assert(!inputImage.data);

Scalar colorTab[] = {

Scalar(0, 0, 255),

Scalar(0, 255, 0),

Scalar(255, 0, 0),

Scalar(0, 255, 255),

Scalar(255, 0, 255)

};

Mat matData = Mat::zeros(Size(inputImage.channels(), inputImage.rows*inputImage.cols), CV_32FC1);

int ncluster = 5; //rng.uniform(2, Max_nCluster + 1);//聚类数量

Mat label;//聚类标签

Mat centers(ncluster, 1, matData.type());

for (size_t i = 0; i < inputImage.rows; i++)//把图像存储到样本容器

{

uchar* ptr = inputImage.ptr(i);

for (size_t j = 0; j < inputImage.cols; j++)

{

matData.at(i*inputImage.cols + j, 0) = ptr[j*inputImage.channels()];

matData.at(i*inputImage.cols + j, 1) = ptr[j*inputImage.channels() +1];

matData.at(i*inputImage.cols + j, 2) = ptr[j*inputImage.channels() +2];

}

}

Mat result = Mat::zeros(inputImage.size(), inputImage.type());

TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 20, 0.1);

kmeans(matData, ncluster, label, criteria, 3, KMEANS_PP_CENTERS, centers);

for (size_t i = 0; i < inputImage.rows; i++)

{

for (size_t j = 0; j < inputImage.cols; j ++)

{

int index = label.at(i*inputImage.cols + j,0);

result.at(i, j)[0] = colorTab[index][0];

result.at(i, j)[1] = colorTab[index][1];

result.at(i, j)[2] = colorTab[index][2];

}

}

imshow("12", result);

waitKey(0);

return 0;

}

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 机器学习
    +关注

    关注

    66

    文章

    8105

    浏览量

    130542
  • K-means
    +关注

    关注

    0

    文章

    28

    浏览量

    11225

原文标题:K-means算法(理论+opencv实现)

文章出处:【微信号:AI_shequ,微信公众号:人工智能爱好者社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于距离的聚类算法K-means的设计实现

    K-means 算法是典型的基于距离的聚类算法,采用距离作为相似性的评价指标,两个对象的距离越近,其相似度就越大。而簇是由距离靠近的对象组成的,因此算法目的是得到紧凑并且独立的簇。
    的头像 发表于 07-18 09:19 1815次阅读
    基于距离的聚类<b class='flag-5'>算法</b><b class='flag-5'>K-means</b>的设计<b class='flag-5'>实现</b>

    数字信号处理理论算法实现

    系统介绍数字信号处理理论、相应的算法,以及实现这些算法的软件和硬件
    发表于 05-04 21:29

    使用K-means压缩图像

    山东大学机器学习(实验六内容)—— K-Means
    发表于 08-28 09:25

    舆情分析的理论基础和算法

    [源码和文档分享]JAVA实现基于k-means聚类算法实现微博舆情热点分析系统
    发表于 06-04 08:21

    调用sklearn使用的k-means模型

    【python】调用sklearn使用k-means模型
    发表于 06-12 13:33

    K-Means有什么优缺点?

    K-Means的主要优点是什么?K-Means的主要缺点是什么?
    发表于 06-10 06:14

    改进的k-means聚类算法在供电企业CRM中的应用

    针对k-means算法存在的不足,提出了一种改进算法。 针对目前供电企业CRM系统的特点提出了用聚类分析方法进行客户群细分模型设计,通过实验验证了本文提出的k-means改进
    发表于 03-01 15:28 15次下载

    Web文档聚类中k-means算法的改进

    Web文档聚类中k-means算法的改进 介绍了Web文档聚类中普遍使用的、基于分割的k-means算法,分析了k-means
    发表于 09-19 09:17 977次阅读
    Web文档聚类中<b class='flag-5'>k-means</b><b class='flag-5'>算法</b>的改进

    基于密度的K-means算法在聚类数目中应用

    密度和增加轨迹数据关键点密度权值的方式选取高密度的轨迹数据点作为初始聚类中心进行K-means聚类,然后结合聚类有效函数类内类外划分指标对聚类结果进行评价,最后根据评价确定最佳聚类数目和最优聚类划分。理论研究与实验结果表明,该算法
    发表于 11-25 11:35 0次下载

    K-Means算法改进及优化

    传统的k-means算法采用的是随机数初始化聚类中心的方法,这种方法的主要优点是能够快速的产生初始化的聚类中心,其主要缺点是初始化的聚类中心可能会同时出现在同一个类别中,导致迭代次数过多,甚至陷入
    发表于 12-05 18:32 0次下载
    <b class='flag-5'>K-Means</b><b class='flag-5'>算法</b>改进及优化

    基于布谷鸟搜索的K-means聚类算法

    针对原始K-means聚类算法受初始聚类中心影响过大以及容易陷入局部最优的不足,提出一种基于改进布谷鸟搜索(cs)的K-means聚类算法(ACS-K-
    发表于 12-13 17:24 3次下载

    熵加权多视角核K-means算法

    在基于视角加权的多视角聚类中,每个视角的权重取值对聚类结果的精度都有着重要的影V向。针对此问题,提出熵加权多视角核K-means( EWKKM)算法,通过给每个视角分配一个合理的权值来降低噪声视角
    发表于 12-17 09:57 1次下载

    K-Means算法的简单介绍

    K-Means是十大经典数据挖掘算法之一。K-Means和KNN(K邻近)看上去都是K打头,但却是不同种类的算法。kNN是监督学习中的分类算法
    发表于 07-05 14:18 4606次阅读

    K-MEANS聚类算法概述及工作原理

    K-means 是一种聚类算法,且对于数据科学家而言,是简单且热门的无监督式机器学习(ML)算法之一。
    的头像 发表于 06-06 11:53 3086次阅读

    K-means聚类算法指南

    在聚类技术领域中,K-means可能是最常见和经常使用的技术之一。K-means使用迭代细化方法,基于用户定义的集群数量(由变量K表示)和数据集来产生其最终聚类。例如,如果将K设置为3,则数据集将分组为3个群集,如果将K设置为4,则将数据分组为4个群集,依此类推。
    的头像 发表于 10-28 14:25 831次阅读