1.GMM(高斯混合模型)

1.1GMM概述

         1

高斯混合分布模型案例 高斯混合模型图像分类_高斯混合

  •  GMM与K-means相比较属于软分类
  • 实现的方法是期望最大化(E-M算法)
  • 停止的条件:达到收敛
  • 主要分为两个步骤:训练与预言

1.2原理讲解

        高斯混合模型(Gaussian Mixed Model)指的是多个高斯分布函数的线性组合,理论上GMM可以拟合出任意类型的分布,通常用于解决同一集合下的数据包含多个不同的分布的情况(或者是同一类分布但参数不一样,或者是不同类型的分布,比如正态分布和伯努利分布)。

        如图1,图中的点在我们看来明显分成两个聚类。这两个聚类中的点分别通过两个不同的正态分布随机生成而来。但是如果没有GMM,那么只能用一个的二维高斯分布来描述图1中的数据。图1中的椭圆即为二倍标准差的正态分布椭圆。这显然不太合理,毕竟肉眼一看就觉得应该把它们分成两类。

高斯混合分布模型案例 高斯混合模型图像分类_高斯混合_02

高斯混合分布模型案例 高斯混合模型图像分类_GMM_03

高斯混合分布模型案例 高斯混合模型图像分类_高斯混合_04

高斯混合分布模型案例 高斯混合模型图像分类_GMM_05

高斯混合分布模型案例 高斯混合模型图像分类_高斯混合_06

 2.GMM图像分割案例

void MyApi::GMMCluster(Mat& src_image)
{
	//颜色板
	vector<Scalar> 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<uchar>(row);
		for (int col = 0; col < width * chnes; col += chnes)
		{
			int index = row * width + col / chnes;
			double* ptr_points = points.ptr<double>(index);
			//ptr_points[0] = static_cast<int>(ptr[col + 0]);//B
			//ptr_points[1] = static_cast<int>(ptr[col + 1]);//G
			//ptr_points[2] = static_cast<int>(ptr[col + 2]);//R
			for (int i = 0; i < chnes; i++)
				ptr_points[i] = static_cast<int>(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> 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<uchar>(row);
		uchar* ptr_srcImage = src_image.ptr<uchar>(row);
		for (int col = 0; col < width * chnes; col += chnes)
		{
			//第col/chnes个像素点的像素起点位置为col(col为3的倍数)
			//计算位置为(row,col/chnes)的像素点属于的类别
			int index = label.at<int>(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<double>(0, 0) = ptr_srcImage[col];
sample.at<double>(1, 0) = ptr_srcImage[col + 1];
sample.at<double>(2, 0) = ptr_srcImage[col + 2];
//预测像素值属于哪一类
//predict2 第一参数必须是(1,dims)或者(dims,1)
//第二个参数返回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);
}

 利用GMM对图像二分类分割如下:

高斯混合分布模型案例 高斯混合模型图像分类_高斯混合_07