opencv图像分割之GMM(高斯混合模型)方法

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;
}

效果演示

在这里插入图片描述


  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值