GMM简介
不同于其它的机器学习模型,EM算法是一种非监督的学习算法,它的输入数据事先不需要进行标注。相反,该算法从给定的样本集中,能计算出高斯混和参数的最大似然估计。也能得到每个样本对应的标注值,类似于kmeans聚类(输入样本数据,输出样本数据的标注)。实际上,高斯混和模型GMM和kmeans都是EM算法的应用
理部分请自行百度学习,本人不擅长
相关函数api
函数api
bool trainEM(InputArray samples,
OutputArray logLikelihoods=noArray(),
OutputArray labels=noArray(),
OutputArray probs=noArray())
参数说明
- samples: 输入的样本,一个单通道的矩阵。从这个样本中,进行高斯混和模型估计。
- logLikelihoods: 可选项,输出一个矩阵,里面包含每个样本的似然对数值。
- labels: 可选项,输出每个样本对应的标注。
- probs: 可选项,输出一个矩阵,里面包含每个隐性变量的后验概率
这个函数没有输入参数的初始化值,是因为它会自动执行kmeans算法,将kmeans算法得到的结果作为参数初始化。
trainEM函数的功能和kmeans差不多,都是实现自动聚类,输出每个样本对应的标注值。但它比kmeans还多出一个功能,就是它还能起到训练分类器的作用,用于后续新样本的预测。
这个trainEM函数实际把E步骤和M步骤都包含进去了,我们也可以对两个步骤分开执行,OPENCV3.0中也提供了分别执行的函数:
bool trainE(InputArray samples,
InputArray means0,
InputArray covs0=noArray(),
InputArray weights0=noArray(),
OutputArray logLikelihoods=noArray(),
OutputArray labels=noArray(),
OutputArray probs=noArray())
bool trainM(InputArray samples,
InputArray probs0,
OutputArray logLikelihoods=noArray(),
OutputArray labels=noArray(),
OutputArray probs=noArray())
函数api
Vec2d predict2(InputArray sample, OutputArray probs) const
参数说明
-
sample: 待测样本
-
probs : 和上面一样,一个可选的输出值,包含每个隐性变量的后验概率
-
返回值:返回一个
Vec2d
类型的数,包括两个元素的double
向量,第一个元素为样本的似然对数值,第二个元素为最大可能混和分量的索引值。
代码演示(聚落)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
using namespace cv::ml;
int main(void)
{
//创建图片
Mat img(500,500,CV_8UC3);
//定义随机数产生器
RNG rng(12345);
//定义5种颜色 最大分类不超过5
Scalar color_tab[]={
Scalar(0,0,255),
Scalar(0,255,0),
Scalar(255,0,0),
Scalar(0,255,255),
Scalar(255,0,255)
};
//定义分类 即函数K值 多少个分类点
int num_cluster = rng.uniform(2,5);
printf("num of num_cluster is %d\n",num_cluster);
//一共产生多少点数供我们测试
int sample_count = rng.uniform(2,1000);
printf("num of sample_count is %d\n",sample_count);
Mat points(sample_count,2,CV_32FC1); //产生的样本数,实际上为2通道的列向量,元素类型为Point2f
Mat lables; //聚类标签 每个点归哪个类
Mat centers;
//生成随机数
for(int i=0;i<num_cluster;i++)
{
Point center;
center.x = rng.uniform(0,img.cols);
center.y = rng.uniform(0,img.rows);
//获取填充区域 只是获取到区域 没有填充数值
Mat point_chunk = points.rowRange(i*sample_count/num_cluster,i == num_cluster-1 ? sample_count:
(i+1)*sample_count/num_cluster);
//向获取到的填充区域随机数填充 高斯分布 满足(均值为Scalar(center.x,center.y) 方程为Scalar(img.cols*0.05,img.rows*0.05))
rng.fill(point_chunk,RNG::NORMAL,Scalar(center.x,center.y),Scalar(img.cols*0.05,img.rows*0.05));
}
//打乱
randShuffle(points,1,&rng);
Ptr<EM> em_model = EM::create();
//设置K
em_model->setClustersNumber(num_cluster);
//设置协方差矩阵类型
em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
//收敛条件
em_model->setTermCriteria(TermCriteria(TermCriteria::EPS + TermCriteria::COUNT,100,0.1));
//训练
em_model->trainEM(points,noArray(),lables,noArray());
// classify every image pixels
Mat sample(1, 2, CV_32FC1);
for (int row = 0; row < img.rows; row++) {
for (int col = 0; col < img.cols; col++) {
sample.at<float>(0) = (float)col;
sample.at<float>(1) = (float)row;
//int response = cvRound(em_model->predict2(sample, noArray())[1]);
Vec2d predict = em_model->predict2(sample, noArray()); // 预言
int response = cvRound(predict[1]); // response 就是给出的当前的分类
Scalar c = color_tab[response];
circle(img, Point(col, row), 1, c*0.75, -1);
}
}
// draw the clusters
for (int i = 0; i < sample_count; i++) {
Point p(cvRound(points.at<float>(i, 0)), cvRound(points.at<float>(i, 1)));
circle(img, p, 1, color_tab[lables.at<int>(i)], -1);
}
imshow("GMM-EM Demo", img);
waitKey(0);
destroyAllWindows();
return 0;
}
效果演示
–
代码演示(图片分割)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
using namespace cv::ml;
#define PIC_PATH "/work/opencv_pic/"
#define PIC_NAME "kmeans.jpeg"
int main(void)
{
Mat src;
//获取完整的图片路径及名称
string pic = string(PIC_PATH)+string(PIC_NAME);
//打印图片路径
cout << "pic path is :"<<pic<<endl;
//读取图片
src = imread(pic);
//判断图片是否存在
if(src.empty())
{
cout<<"pic is not exist!!!!"<<endl;
return -1;
}
//显示图片
namedWindow("src pic",WINDOW_AUTOSIZE);
imshow("src pic",src);
//定义5种颜色 最大分类不超过5
Scalar color_tab[]={
Scalar(0,0,255),
Scalar(0,255,0),
Scalar(255,0,0),
Scalar(0,255,255),
Scalar(255,0,255)
};
int width = src.cols;
int height = src.rows;
int dims = src.channels();
int nsamples = width*height;
Mat points(nsamples, dims, CV_64FC1);
Mat labels;
Mat result = Mat::zeros(src.size(), CV_8UC3);
//定义分类 即函数K值 多少个分类点
int num_cluster = 3;
printf("num of num_cluster is %d\n",num_cluster);
// 图像RGB像素数据转换为样本数据
int index = 0;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
index = row*width + col;
Vec3b rgb = src.at<Vec3b>(row, col);
points.at<double>(index, 0) = static_cast<int>(rgb[0]);
points.at<double>(index, 1) = static_cast<int>(rgb[1]);
points.at<double>(index, 2) = static_cast<int>(rgb[2]);
}
}
// EM Cluster Train
Ptr<EM> em_model = EM::create();
//分区个数
em_model->setClustersNumber(num_cluster);
//设置协方差矩阵类型
em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
//设置收敛条件
em_model->setTermCriteria(TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 100, 0.1));
//根据样本训练 将概率分区存储到lables EM
em_model->trainEM(points, noArray(), labels, noArray());
// 对每个像素标记颜色与显示
Mat sample(1, dims, CV_64FC1);//
int r = 0, g = 0, b = 0;
//将每个像素放到样本中
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
index = row*width + col;
//获取各个通道的颜色
b = src.at<Vec3b>(row, col)[0];
g = src.at<Vec3b>(row, col)[1];
r = src.at<Vec3b>(row, col)[2];
//将像素放到样本数据中
sample.at<double>(0, 0) = static_cast<double>(b);
sample.at<double>(0, 1) = static_cast<double>(g);
sample.at<double>(0, 2) = static_cast<double>(r);
//四舍五入
int response = cvRound(em_model->predict2(sample, noArray())[1]);
Scalar c = color_tab[response];
result.at<Vec3b>(row, col)[0] = c[0];
result.at<Vec3b>(row, col)[1] = c[1];
result.at<Vec3b>(row, col)[2] = c[2];
}
}
imshow("EM-Segmentation", result);
waitKey(0);
destroyAllWindows();
return 0;
}