opencv中的Kmeans使用示例

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颜色聚类得到的效果图:


猜你喜欢

转载自blog.csdn.net/owen7500/article/details/51604906