Opencv之GMM(高斯混合模型)图像分割

int imageSeg_GMM()
{
	string image_path = "D:/vs2019Proj/ConsoleApplication1/timg.jpg";
	Mat src_image = imread(image_path);
	if (src_image.empty())
	{
		cout << "could not load image.." << endl;
		return -1;
	}
	//颜色板
	vector colorTab = {
		{0,0,255},
		{0,255,0},
		{255,0,0},
		{255,255,0},
		{0,255,255},
		{255,0,255}
	};
	//获取输入图像的尺寸
	int width = src_image.cols;
	int height = src_image.rows;
	int chnes = src_image.channels();
	//把彩色图像中的数据点提取出来放入points里面
	//points的类型是CV_64FC1
	//为什么这里使用的是CV_64FC1,因为GMM中需要计算概率,协方差等,需要大量的浮点运算
	//下面的代码同时适用于灰度图和3-d,4-d彩色图的遍历
	Mat points(width * height, chnes, CV_64FC1, Scalar(10));
	for (int row = 0; row < height; row++)
	{
		uchar* ptr = src_image.ptr(row);
		for (int col = 0; col < width * chnes; col += chnes)
		{
			int index = row * width + col / chnes;
			double* ptr_points = points.ptr(index);
			//ptr_points[0] = static_cast(ptr[col + 0]);//B
			//ptr_points[1] = static_cast(ptr[col + 1]);//G
			//ptr_points[2] = static_cast(ptr[col + 2]);//R
			for (int i = 0; i < chnes; i++)
				ptr_points[i] = static_cast(ptr[col + i]);
		}
	}
	//和K-means的一样,表示图片数据要分为几类,高斯模型是由clusters个高斯分布合成的
	int clusters = 2;
	//协方差矩阵的类型
	int covariancematrixtype = EM::Types::COV_MAT_SPHERICAL;
	//建立高斯模型的终止条件
	//最大迭代次数(全部数据处理的次数),终止要达到的精度要求
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
	//高斯模型建立的同时,也会预测每一个像素点属于哪一个高斯分布,所以label用来记录分布结果
	Mat label;
	//初始化参数
	Ptr em_model = EM::create();
	em_model->setClustersNumber(clusters);
	em_model->setCovarianceMatrixType(covariancematrixtype);
	em_model->setTermCriteria(criteria);
	//训练建立模型,并得到结果(label)
	//第四个参数是每个像素在每个高斯分布的后验概率
	//第二个参数,每个像素的似然对数值
	em_model->trainEM(points, noArray(), label, noArray());
	//显示结果

	Mat result = Mat::zeros(src_image.size(), CV_8UC3);
    //Mat sample(chnes, 1, CV_64FC1);
	for (int row = 0; row < height; row++)
	{
		uchar* ptr_result = result.ptr(row);
        uchar* ptr_srcImage = src_image.ptr(row);
		for (int col = 0; col < width*chnes; col+=chnes)
		{
			//第col/chnes个像素点的像素起点位置为col(col为3的倍数)
			//计算位置为(row,col/chnes)的像素点属于的类别
			int index = label.at(row * width + col / chnes, 0);
			for (int i = 0; i < chnes; i++)
				ptr_result[col + i] = colorTab[index][i];
            			/*
			这里还有一种写法,是把src_image中的每一个像素放到高斯模型里面预测,这样也可以得到
			每一个像素的分类结果,这里没有这样写是因为我们在建立高斯模型时,用的就是src_image的图像数据,
			所以在建立高斯模型的时候就已经计算好了像素的分类结果,保存在label里面。

			这里写一下:
			//获取原图中某个像素点的像素值
			sample.at(0, 0) = ptr_srcImage[col];
			sample.at(1, 0) = ptr_srcImage[col + 1];
			sample.at(2, 0) = ptr_srcImage[col + 2];
			//预测像素值属于哪一类
			//predict2 第一参数必须是像素值,尺寸是(1,dims)或者(dims,1),dims是通道数,这里就是3.
			//返回值是Vec2d类型, 第一个值是似然对数值,第二个值是最可能的高斯分布类别
			int index = cvRound(em_model->predict2(sample, noArray())[1]);
			//分割图像,给结果图像相对应的像素点着上颜色
			for (int i = 0; i < chnes; i++)
				ptr_result[col + i] = colorTab[index][i];
			*/
		}
	}
	imshow("GMM result", result);
	imshow("src_image", src_image);
}

 感觉和之前做的k-means相比,GMM-EM的分割效果要好一些。因为在GMM里面需要计算协方差和概率,需要用到很多浮点运算,所以GMM的速度要比K-means要慢一些。

大佬关于EM的讲解 

补充一下EM::Types

COV_MAT_SPHERICAL 表示协方差矩阵是一个标量乘以单位矩阵,即sk×I,所以对协方差矩阵的估计只需要确定sk即可
COV_MAT_DIAGONAL 表示协方差矩阵是一个对角线元素为正数的对角矩阵,这时只需要对d个参数进行估计即可,d为样本特征属性的数量
COV_MAT_GENERIC 表示协方差矩阵是一个对称的正数矩阵,很显然,此时需要估计d2/2个参数,除非样本数据庞大,否则不建议设置该参数。

你可能感兴趣的:(图像分割,opencv)