OPENCV KMeans算法

    KMeans算法作为数据聚类的重要算法之一,通过被广泛的应用。本文将调用OPENCV的工具包来对一些随机数进行聚类,并将结果通过OPENCV进行可视化。 本实验主要是生成一些二维样本数据,每个样本数据都服从高斯分布,我们将通过KMeans算法来对结果进行分类。

    KMeans算法的基本流程

    1.确定参数K,也就是需要将数据分成几个类别

    2随机.初始化K个中心坐标点

    3.根据每个数据样本与中心点的距离来判断,选择距离最近的中心类别,分配聚类的编号

   4.对于相同类别的样本数据重新计算每个样本到中心点的距离,取均值来调整中心坐标点的位置

    5.判断算法是否到达了最大迭代次数或者精度,如果到达说明数据聚类完成,没有到达条件时将会继续计算不停地调整中心坐标点


  KMeans算法的基本流程

了解完基本思想后我们将通过代码来演示KMeans算法对数据聚类的效果,同时对相关API函数进行介绍

第一步我们先使用了OPENCV的Mat数据结构来创建了一个画板,我们由下图的代码可以看到,创建了一个长500,宽500的画板,同时对图像的文件格式做了设定。在这里简单的提一下CvMat矩阵对应的参数类型。一般的图像文件格式使用的是 Unsigned 8bits吧,CvMat矩阵对应的参数类型就是CV_8UC1,CV_8UC2,CV_8UC3。(最后的1、2、3表示通道数,譬如RGB3通道就用CV_8UC3)。在这里我们使用了CV_8UC3创建了一张3通道的图像,下面的步骤中我们需要对不同的数据类别指定不同的颜色,因此采用了3通道的图像文件格式,当然使用灰度图也是没有问题的。


创建一个画板用来显示最后的结果

第二步生成随机样本,程序中采用RNG随机模块来进行数据的随机初始化,在此来介绍几个程序中用到的初始化模块。

(1)void RNG::fill(InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange=false )

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

(2)void randShuffle(InputOutputArray dst, double iterFactor=1., RNG* rng=0 )

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


初始化随机样本核心代码

int numCluster = 5;

int sampleCount = rng.uniform(5, 500);

Mat points(sampleCount, 1, CV_32FC2);

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 pointDome = points.rowRange(k*sampleCount / numCluster, (k + 1)*sampleCount / numCluster);

rng.fill(points,rng.uniform,0,500);

}

randShuffle(points,1,&rng);


第三步我们将对构建出来的随机样本进行聚类,采用KMeans算法,对此我们对KMeans算法用到的API函数进行介绍。

Class TermCriteria

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

double kmeans(InputArray data, int K, InputOutputArray bestLabels, TermCriteria criteria, int attempts, int flags, OutputArray centers=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表示的是聚类后的中心点存放矩阵。该函数返回的是聚类结果的紧凑性

第四步将对分类出来的进行显示

// 用不同颜色显示分类

img = Scalar::all(255);

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

int index = labels.at<int>(i);

Point p = points.at<Point2f>(i);

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

}

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

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

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

int y = centers.at<float>(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);


最终代码:

#include <opencv2/opencv.hpp>

#include <iostream>

using namespace cv;

using namespace std;

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

Mat img(500, 500, CV_8UC3);

RNG rng(12345);

Scalar colorTab[] = {

Scalar(0, 0, 255),

Scalar(0, 255, 0),

Scalar(255, 0, 0),

Scalar(0, 255, 255),

Scalar(255, 0, 255)

};

int numCluster = rng.uniform(2, 5);

printf("number of clusters : %d\n", numCluster);

int sampleCount = rng.uniform(5, 1000);

Mat points(sampleCount, 1, CV_32FC2);

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 = points.rowRange(k*sampleCount / numCluster,

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

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

}

randShuffle(points, 1, &rng);

// 使用KMeans

kmeans(points, 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<int>(i);

Point p = points.at<Point2f>(i);

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

}

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

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

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

int y = centers.at<float>(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;

}


运行效果:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容