opencv中的Kmeans使用示例

来源:互联网 发布:洛瑞数据 编辑:程序博客网 时间:2024/06/07 00:48

kmeans是非常经典的聚类算法,至今也还保留着较强的生命力,图像处理中经常用到kmeans算法或者其改进算法进行图像分割操作,在数据挖掘中kmeans经常用来做数据预处理。opencv中提供了完整的kmeans算法,其函数原型为:

double kmeans( InputArray data, int K, InputOutputArray bestLabels, TermCriteria criteria, int attempts, int flags, OutputArray centers = noArray() );

其中data表示用于聚类的数据,是N维的数组类型(Mat型),必须浮点型;

K表示需要聚类的类别数;

bestLabels聚类后的标签数组,Mat型;

criteria迭代收敛准则(MAX_ITER最大迭代次数,EPS最高精度);

attemps表示尝试的次数,防止陷入局部最优;

flags 表示聚类中心的选取方式(KMEANS_RANDOM_CENTERS 随机选取,KMEANS_PP_CENTERS使用Arthur提供的算法,KMEANS_USE_INITIAL_LABELS使用初始标签);

centers 表示聚类后的类别中心。

关于kmeans的理论可以参考:基本Kmeans算法介绍及其实现

下面给出一个关于图像聚类的示例:

#include <opencv.hpp>using namespace cv;Scalar colorTab[] =     //10个颜色{Scalar(0, 0, 255),Scalar(0, 255, 0),Scalar(255, 100, 100),Scalar(255, 0, 255),Scalar(0, 255, 255),Scalar(255, 0, 0),Scalar(255, 255, 0),Scalar(255, 0, 100),Scalar(100, 100, 100),Scalar(50, 125, 125)};class ClusterPixels{private:Mat image;//待聚类图像Mat labels;//聚类后的标签int clusterCounts;//分类数,不得大于10,只是颜色定义只有10类,并不是算法限制public:ClusterPixels() :clusterCounts(0){}ClusterPixels(const Mat& src, int clusters = 5) :clusterCounts(clusters){ image = src.clone(); }void setImage(const Mat& src){ image = src.clone(); };void setClusters(int clusters){ clusterCounts = clusters; }Mat getLabels(){return labels;};//返回聚类后的标签Mat clusterGrayImageByKmeans(){//转换成灰度图if (image.channels() != 1)cvtColor(image, image, COLOR_BGR2GRAY);int rows = image.rows;int cols = image.cols;//保存聚类后的图片Mat clusteredMat(rows, cols, CV_8UC3);clusteredMat.setTo(Scalar::all(0));Mat pixels(rows*cols, 1, CV_32FC1);//pixels用于保存所有的灰度像素for (int i = 0; i < rows;++i){const uchar *idata = image.ptr<uchar>(i);float *pdata = pixels.ptr<float>(0);for (int j = 0; j < cols;++j){pdata[i*cols + j] = idata[j];}}kmeans(pixels, clusterCounts, labels, TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 10, 0), 5, KMEANS_PP_CENTERS);for (int i = 0; i < rows;++i){for (int j = 0; j < cols;++j){circle(clusteredMat, Point(j,i), 1, colorTab[labels.at<int>(i*cols + j)]);//标记像素点的类别,颜色区分}}return clusteredMat;}Mat clusterColorImageByKmeans(){assert(image.channels() != 1);int rows = image.rows;int cols = image.cols;int channels = image.channels();//保存聚类后的图片Mat clusteredMat(rows, cols, CV_8UC3);clusteredMat.setTo(Scalar::all(0));Mat pixels(rows*cols, 1, CV_32FC3);//pixels用于保存所有的灰度像素pixels.setTo(Scalar::all(0));for (int i = 0; i < rows; ++i){const uchar *idata = image.ptr<uchar>(i);float *pdata = pixels.ptr<float>(0);for (int j = 0; j < cols*channels; ++j){pdata[i*cols*channels + j] = saturate_cast<float>(idata[j]);}}kmeans(pixels, clusterCounts, labels, TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 0), 5, KMEANS_PP_CENTERS);for (int i = 0; i < rows; ++i){for (int j = 0; j < cols*channels; j += channels){circle(clusteredMat, Point(j/channels,i), 1, colorTab[labels.at<int>(i*cols + (j/channels))]);//标记像素点的类别,颜色区分}}return clusteredMat;}};


主函数:

#include "clusterImagePixels.hpp"int main(){Mat testImage = imread("E:\\testImage\\board.jpg");if (testImage.empty()){return -1;}ClusterPixels clusterPix(testImage,3);Mat colorResults = clusterPix.clusterColorImageByKmeans();Mat grayResult = clusterPix.clusterGrayImageByKmeans();if (!colorResults.empty()){hconcat(testImage, colorResults, colorResults);imshow("clusterImage", colorResults);}if (!grayResult.empty()){hconcat(testImage, grayResult, grayResult);imshow("grayCluster", grayResult);}if (waitKey() == 27)return 0;}


效果图:

采用灰度聚类的结果:


采用RGB颜色聚类得到的效果图:


0 0