K-means算法MacQueen在1967年提出的,是最简单与最常见数据分类方法之一并且最为一种常见数据分析技术在机器学习、数据挖掘、模式识别、图像分析等领域都用应用。从机器学习的角度看,K-means属于一种无监督的机器学习方法。无监督学习(Unsupervised Learning)简单的说就是在不给定正确答案(标签)的情况下,在数据中找到一些相似的特征用以分析(分类)数据的方法。而在K-means算法解决图像的问题中,就是在对图像的像素点进行分类,达到图像分割的目的。
K-means算法流程:
(1)K-means初始聚类中心的确定:初始的K个分类中每个分类的中心点选择,K-Means算法支持随机选择,人工指定与中心化算法三种方式。
(2)如何判断收敛:
其中i表示第i个数据点,j表示第j个聚类中心,表示第i个数据点的数据,表示第j个聚类中心的值。所以RSS表征的意义为:被归于一类的数据点距离与它对应的聚类中心的差值的平方和。K-means聚类算法依靠两次聚类后RSS的差值是否小于设定的阈值判断是否达到收敛。
(3)如何表征像素点(数据点)的特征:
多维数据支持,多数时候我们要分类的特征对象的描述数据不止一个数据特征,而是一个特征向量来表示,OpenCV中通过Mat对象构建实现对多维数据KMeans分类支持。
K-means的OpenCV实现:
函数定义:
CV_EXPORTS_W double kmeans(
InputArray data,
int K,
CV_OUT InputOutputArray bestLabels,
TermCriteria criteria,
int attempts,
int flags,
OutputArray centers=noArray() );
第一个参数:表示输入的数据集合,可以一维或者多维数据,类型是Mat类型,比如Mat points(count, 2, CV_32F)表示数据集合是二维,浮点数数据集;
第二个参数:表示分类的数目,K=2时即表示二分类;
第三个参数:表示计算之后各个数据点的最终的分类索引,是一个INT类型的Mat对象;
第四个参数:表示算法终止的条件,达到最大循环数目或者指定的精度阈值算法就停止继续分类迭代计算;
第五个参数:表示为了获得最佳的分类效果,算法要不同的初始分类尝试次数;
第六个参数:表示表示选择初始中心点选择方法用哪一种方法:
KMEANSRANDOMCENTERS 表示随机选择中心点
KMEANSPPCENTERS 基于中心化算法选择
KMEANSUSEINITIAL_LABELS第一次分类中心点用输入的中心点;
第七个参数:表示输出的每个分类的中心点数据;
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat SrcImage = imread("1.jpg");
imshow("原图", SrcImage);
int width = SrcImage.cols;
int height = SrcImage.rows;
int dims = SrcImage.channels();
int sampleCount = width*height;
int clusterCount = 2;
Mat points(sampleCount, dims, CV_32F, Scalar(10));
Mat labels;
Mat centers(clusterCount, 1, points.type());
int index = 0;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
index = row*width + col;
Vec3b rgb = SrcImage.at<Vec3b>(row, col);
points.at<float>(index, 0) = static_cast<int>(rgb[0]);
points.at<float>(index, 1) = static_cast<int>(rgb[1]);
points.at<float>(index, 2) = static_cast<int>(rgb[2]);
}
}
TermCriteria criteria = TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0);
kmeans(points, clusterCount, labels, criteria, 3, KMEANS_PP_CENTERS, centers);
Mat result = Mat::zeros(SrcImage.size(), CV_8UC3);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
index = row*width + col;
int label = labels.at<int>(index, 0);
if (label == 1) {
result.at<Vec3b>(row, col)[0] = 0;
result.at<Vec3b>(row, col)[1] = 0;
result.at<Vec3b>(row, col)[2] = 0;
}
else if (label == 0) {
result.at<Vec3b>(row, col)[0] = 255;
result.at<Vec3b>(row, col)[1] = 255;
result.at<Vec3b>(row, col)[2] = 255;
}
}
}
imshow("聚类结果", result);
waitKey(0);
return 0;
}
结果: