1.GMM(高斯混合模型)
1.1GMM概述
1
- GMM与K-means相比较属于软分类
- 实现的方法是期望最大化(E-M算法)
- 停止的条件:达到收敛
- 主要分为两个步骤:训练与预言
1.2原理讲解
高斯混合模型(Gaussian Mixed Model)指的是多个高斯分布函数的线性组合,理论上GMM可以拟合出任意类型的分布,通常用于解决同一集合下的数据包含多个不同的分布的情况(或者是同一类分布但参数不一样,或者是不同类型的分布,比如正态分布和伯努利分布)。
如图1,图中的点在我们看来明显分成两个聚类。这两个聚类中的点分别通过两个不同的正态分布随机生成而来。但是如果没有GMM,那么只能用一个的二维高斯分布来描述图1中的数据。图1中的椭圆即为二倍标准差的正态分布椭圆。这显然不太合理,毕竟肉眼一看就觉得应该把它们分成两类。
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对图像二分类分割如下: