zoukankan      html  css  js  c++  java
  • opencv3中的机器学习算法之:EM算法

    不同于其它的机器学习模型,EM算法是一种非监督的学习算法,它的输入数据事先不需要进行标注。相反,该算法从给定的样本集中,能计算出高斯混和参数的最大似然估计。也能得到每个样本对应的标注值,类似于kmeans聚类(输入样本数据,输出样本数据的标注)。实际上,高斯混和模型GMM和kmeans都是EM算法的应用。

    在opencv3.0中,EM算法的函数是trainEM,函数原型为:

     bool trainEM(InputArray samples, OutputArray logLikelihoods=noArray(),OutputArray labels=noArray(),OutputArray probs=noArray())

    四个参数:

     samples: 输入的样本,一个单通道的矩阵。从这个样本中,进行高斯混和模型估计。

    logLikelihoods: 可选项,输出一个矩阵,里面包含每个样本的似然对数值。

    labels: 可选项,输出每个样本对应的标注。

    probs: 可选项,输出一个矩阵,里面包含每个隐性变量的后验概率

    这个函数没有输入参数的初始化值,是因为它会自动执行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())
    trainEM函数的功能和kmeans差不多,都是实现自动聚类,输出每个样本对应的标注值。但它比kmeans还多出一个功能,就是它还能起到训练分类器的作用,用于后续新样本的预测。

    预测函数原型为:
    Vec2d predict2(InputArray sample, OutputArray probs) const

    sample: 待测样本

    probs : 和上面一样,一个可选的输出值,包含每个隐性变量的后验概率

    返回一个Vec2d类型的数,包括两个元素的double向量,第一个元素为样本的似然对数值,第二个元素为最大可能混和分量的索引值。

    在本文中,我们用两个实例来学习opencv中的EM算法的应用。

    一、opencv3.0中自带的例子,既包括聚类trianEM,也包括预测predict2

    代码:

    #include "stdafx.h"
    #include "opencv2/opencv.hpp"
    #include <iostream>
    using namespace std;
    using namespace cv;
    using namespace cv::ml;
    
    //使用EM算法实现样本的聚类及预测
    int main()
    {
        const int N = 4;    //分成4类
        const int N1 = (int)sqrt((double)N);
        //定义四种颜色,每一类用一种颜色表示
        const Scalar colors[] =
        {
            Scalar(0, 0, 255), Scalar(0, 255, 0),
            Scalar(0, 255, 255), Scalar(255, 255, 0)
        };
    
        int i, j;
        int nsamples = 100;   //100个样本点
        Mat samples(nsamples, 2, CV_32FC1);  //样本矩阵,100行2列,即100个坐标点    
        Mat img = Mat::zeros(Size(500, 500), CV_8UC3);  //待测数据,每一个坐标点为一个待测数据
        samples = samples.reshape(2, 0);
    
        //循环生成四个类别样本数据,共样本100个,每类样本25个
        for (i = 0; i < N; i++)
        {
            
            Mat samples_part = samples.rowRange(i*nsamples / N, (i + 1)*nsamples / N);
    
            //设置均值
            Scalar mean(((i%N1) + 1)*img.rows / (N1 + 1),
                ((i / N1) + 1)*img.rows / (N1 + 1));
            //设置标准差
            Scalar sigma(30, 30);
            randn(samples_part, mean, sigma);  //根据均值和标准差,随机生成25个正态分布坐标点作为样本
        }
        samples = samples.reshape(1, 0);
        // 训练分类器
        Mat labels;  //标注,不需要事先知道
        Ptr<EM> em_model = EM::create();
        em_model->setClustersNumber(N);
        em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
        em_model->setTermCriteria(TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 300, 0.1));
        em_model->trainEM(samples, noArray(), labels, noArray());
    
        //对每个坐标点进行分类,并根据类别用不同的颜色画出
        Mat sample(1, 2, CV_32FC1);
        for (i = 0; i < img.rows; i++)
        {
            for (j = 0; j < img.cols; j++)
            {
                sample.at<float>(0) = (float)j;
                sample.at<float>(1) = (float)i;
                //predict2返回的是double值,用cvRound进行四舍五入得到整型
                //此处返回的是两个值Vec2d,取第二个值作为样本标注
                int response = cvRound(em_model->predict2(sample, noArray())[1]);
                Scalar c = colors[response];  //为不同类别设定颜色
                circle(img, Point(j, i), 1, c*0.75, FILLED);
            }
        }
    
        //画出样本点
        for (i = 0; i < nsamples; i++)
        {
            Point pt(cvRound(samples.at<float>(i, 0)), cvRound(samples.at<float>(i, 1)));
            circle(img, pt, 2, colors[labels.at<int>(i)], FILLED);
        }
    
        imshow("EM聚类结果", img);
        waitKey(0);
    
        return 0;
    }
    View Code

    结果:

    二、只用trainEM实现自动聚类功能,进行图片中的目标检测

    代码:

    #include "stdafx.h"
    #include "opencv2/opencv.hpp"
    #include <iostream>
    using namespace std;
    using namespace cv;
    using namespace cv::ml;
    
    int main()
    {
        const int MAX_CLUSTERS = 5;
        Vec3b colorTab[] =
        {
            Vec3b(0, 0, 255),
            Vec3b(0, 255, 0),
            Vec3b(255, 100, 100),
            Vec3b(255, 0, 255),
            Vec3b(0, 255, 255)
        };
        Mat data, labels;
        Mat pic = imread("d:/woman.png");
        for (int i = 0; i < pic.rows; i++)
        for (int j = 0; j < pic.cols; j++)
        {
            Vec3b point = pic.at<Vec3b>(i, j);
            Mat tmp = (Mat_<float>(1, 3) << point[0], point[1], point[2]);
            data.push_back(tmp);
        }
    
        int N =3;  //聚成3类
        Ptr<EM> em_model = EM::create();
        em_model->setClustersNumber(N);
        em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);
        em_model->setTermCriteria(TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 300, 0.1));
        em_model->trainEM(data, noArray(), labels, noArray());
    
        int n = 0;
        //显示聚类结果,不同的类别用不同的颜色显示
        for (int i = 0; i < pic.rows; i++)
        for (int j = 0; j < pic.cols; j++)
        {
            int clusterIdx = labels.at<int>(n);
            pic.at<Vec3b>(i, j) = colorTab[clusterIdx];
            n++;
        }
        imshow("pic", pic);
        waitKey(0);
    
        return 0;
    }

    测试图片

    测试结果:

  • 相关阅读:
    hdu 2137
    hdu 2059
    hdu 2175
    hdu 1297
    hdu 1702
    hdu 1212
    hdu 1397
    [转]常见的碱性食品有哪些?
    [转]C#反射
    每个人都有自己的未来
  • 原文地址:https://www.cnblogs.com/denny402/p/5036288.html
Copyright © 2011-2022 走看看