1,介绍
GMM(Gaussian mixture model) 高斯混合模型,高斯概率密度函数(二维时也称为:正态分布曲线)精确的量化事物,就是:某一区域的点数数量分布情况。如下图所示:
opencv 使用GMM对数据进行分类聚合,把靠近的数据,变成一个集合
1,代码如下
(1)前期准备,产生随机的点集
1): Mat(row,col,type) 定义一个sampleCount行 ,2列的数据
int sampleCount = rng.uniform(5, 100);
Mat points(sampleCount, 2, CV_32FC1,Scalar(10,1));
2):给这个point集合填充随机数据集,点集的范围要在图片的范围里面,
for (int k = 0; k < numCluster; k++) {
Point center;
center.x = rng.uniform(0, img.cols);
center.y = rng.uniform(0, img.rows);
Mat pointChunk = points.rowRange(k*sampleCount / numCluster,
k == numCluster - 1 ? sampleCount : (k + 1)*sampleCount / numCluster);
rng.fill(pointChunk, RNG::NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
}
3):打乱数据
randShuffle(points, 1, &rng);
这样我们就产生了一个随机的数据集合
2,接着开始使用GMM算法
先对函数进行解释
/* samples: 输入的样本,一个单通道的矩阵。从这个样本中,进行高斯混和模型估计。
logLikelihoods: 可选项,输出一个矩阵,里面包含每个样本的似然对数值。
labels: 可选项,输出每个样本对应的标注。
probs: 可选项,输出一个矩阵,里面包含每个隐性变量的后验概率
*/
bool trainEM(InputArray samples, OutputArray logLikelihoods=noArray(),OutputArray labels=noArray(),OutputArray probs=noArray())
2),使用GMM:EM算法技术
Ptr<EM> em_model = EM::create();
em_model->setClustersNumber(numCluster);
em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
em_model->setTermCriteria(TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 100, 0.1));
em_model->trainEM(points, noArray(), labels, noArray());
EM算法:期望最大(Expectation Maximization, 简称EM)算法,称为机器学习十大算法之一。
它是一种从不完全数据或有数据丢失的数据集(存在隐含变量)中求解概率模型参数的最大似然估计方法。
就比如:
某位同学与一位猎人一起外出打猎,一只野兔从前方窜过。只听一声枪响,野兔应声到下,如果要你推测,这一发命中的子弹是谁打的?你就会想,只发一枪便打中,由于猎人命中的概率一般大于这位同学命中的概率,看来这一枪是猎人射中的。
假设有一群兔子和一群猎人呢?其实GMM:EM探讨的就是这样一个问题而已,
一群兔子就判断兔子离哪个猎人比较近,一阵枪响之后一地的兔子,通过已知道的条件(猎人的个数,兔子位置)判断哪些兔子的哪个猎人打的。
那么我们就会想:传入的需要哪些条件呢?
1,传入的数据集(几只兔子),
2,传出的数据集(谁杀死了哪几家兔子)
3,要分成几个数据集(几个猎人)
对于的程序如下:
//创建一个算法空间
Ptr<EM> em_model = EM::create();
//设置有要分成的数据集合
em_model->setClustersNumber(numCluster);
//设置算法类型
em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
//设置退出循环的条件
em_model->setTermCriteria(TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 100, 0.1));
ermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 100, 0.1);
TermCriteria::EPS 表示: 迭代到阈值终止
TermCriteria::COUNT 表示: 最大迭代次数终止
100:最大迭代次数
0.1: 结果的精确性
typedef struct CvTermCriteria
{
int type; /* CV_TERMCRIT_ITER 和CV_TERMCRIT_EPS二值之一,或者二者的组合 */
int max_iter; /* 最大迭代次数 */
double epsilon; /* 结果的精确性 */
}
//放入数据进行计算
em_model->trainEM(points, noArray(), Result, noArray());
points:输入的数据集合(兔子)
Result:结果,输出的集合
最后画点显示数据
for (int i = 0; i < sampleCount; i++) {
Point p(cvRound(points.at<float>(i, 0)), points.at<float>(i, 1));
circle(img, p, 3, colorTab[labels.at<int>(i)], -1);
}
imshow("GMM-EM Demo", img);
显示结果
还可以通过模型预测如果(兔子出现在某一点)被哪个猎手。代码入下
遍历所有的点
// 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;
//cvRound():四舍五入;
// predict2:EM预言 sample:位置
int response = cvRound(em_model->predict2(sample, noArray())[1]);
/*typedef struct Scalar
{
double val[4];
}Scalar*/
Scalar c = colorTab[response];
circle(img, Point(col, row), 1, c*0.75, -1);
}
}
执行的结果如下:
所有代码如下:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace cv::ml;
using namespace std;
int main(int argc, char** argv) {
//产生随机的点集
Mat img(500, 500, CV_8UC3);
RNG rng(12345);
vector<int> vi = {1,2,3,4,5,6};
randShuffle(vi, 1, &rng);
for (size_t i = 0; i < vi.size(); i++)
{
cout << vi[i] << endl;
}
Scalar colorTab[] = {
Scalar(0, 0, 255),
Scalar(0, 255, 0),
Scalar(255, 0, 0),
Scalar(0, 255, 255),
Scalar(255, 0, 255)
};
int numCluster = rng.uniform(2, 10);
printf("number of clusters : %d\n", numCluster);
int sampleCount = rng.uniform(5, 100);
//1,Mat(row,col,type) 定义一个sampleCount行 ,2列的数据
Mat points(sampleCount, 2, CV_32FC1,Scalar(10,1));
Mat labels;
Mat centers;
cout << points << endl;
// 生成随机数
for (int k = 0; k < numCluster; k++) {
Point center;
center.x = rng.uniform(0, img.cols);
center.y = rng.uniform(0, img.rows);
//为矩阵的指定行区间创建一个矩阵头 参数1,从0开始的行间距索引; 参数2,终止索引(把points的第n,到n+1行数据放到这里)
Mat pointChunk = points.rowRange(k*sampleCount / numCluster,
k == numCluster - 1 ? sampleCount : (k + 1)*sampleCount / numCluster);
/*用随机数填充矩阵 ,
InputOutputArray 输入输出矩阵,最多支持4通道,超过4通道先用reshape()改变结构
int distType UNIFORM 或 NORMAL,表示均匀分布和高斯分布
InputArray a disType是UNIFORM,a表示为下限
InputArray b disType是UNIFORM,b表示为上限
bool saturateRange=false 只针对均匀分布有效。当为真的时候,会先把产生随机数的范围变换到数据类型的范围,再产生随机数;
如果为假,会先产生随机数,再进行截断到数据类型的有效区间。请看以下fillM1和fillM2的例子并观察结果 */
rng.fill(pointChunk, 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();
em_model->setClustersNumber(numCluster);
em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
em_model->setTermCriteria(TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 100, 0.1));
em_model->trainEM(points, noArray(), labels, noArray());
//em_model 模型
// 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;
//cvRound():四舍五入;
// predict2:EM预言 sample:位置
int response = cvRound(em_model->predict2(sample, noArray())[1]);
/*typedef struct Scalar
{
double val[4];
}Scalar*/
Scalar c = colorTab[response];
circle(img, Point(col, row), 1, c*0.75, -1);
}
}
// draw the clusters
for (int i = 0; i < sampleCount; i++) {
Point p(cvRound(points.at<float>(i, 0)), points.at<float>(i, 1));
circle(img, p, 3, colorTab[labels.at<int>(i)], -1);
}
imshow("GMM-EM Demo", img);
waitKey(0);
return 0;
}
创作不易,如有引用,请附上链接。2020年5月2日19:39:49
参考:http://blog.sina.com.cn/s/blog_a36a563e0102y2ec.html
https://blog.youkuaiyun.com/weixin_38206214/article/details/81064625
https://blog.youkuaiyun.com/ilyhlf5201314/article/details/8232746