OpenCV:基于高斯混合模型的颜色提取

Posted

技术标签:

【中文标题】OpenCV:基于高斯混合模型的颜色提取【英文标题】:OpenCV: color extraction based on Gaussian mixture model 【发布时间】:2012-09-11 11:58:33 【问题描述】:

我正在尝试使用 opencv EM 算法进行颜色提取。我正在使用基于 opencv 文档中的示例的以下代码:

cv::Mat capturedFrame ( height, width, CV_8UC3 );
int i, j;
int nsamples = 1000;
cv::Mat samples ( nsamples, 2, CV_32FC1 );
cv::Mat labels;
cv::Mat img = cv::Mat::zeros ( height, height, CV_8UC3 );
img = capturedFrame;
cv::Mat sample ( 1, 2, CV_32FC1 );
CvEM em_model;
CvEMParams params;
samples = samples.reshape ( 2, 0 );

    for ( i = 0; i < N; i++ )
               
        //from the training samples
        cv::Mat samples_part = samples.rowRange ( i*nsamples/N, (i+1)*nsamples/N);

        cv::Scalar mean (((i%N)+1)*img.rows/(N1+1),((i/N1)+1)*img.rows/(N1+1));
        cv::Scalar sigma (30,30);
        cv::randn(samples_part,mean,sigma);                     

           

    samples = samples.reshape ( 1, 0 );

    //initialize model parameters
    params.covs         = NULL;
    params.means        = NULL;
    params.weights      = NULL;
    params.probs        = NULL;
    params.nclusters    = N;
    params.cov_mat_type = CvEM::COV_MAT_SPHERICAL;
    params.start_step   = CvEM::START_AUTO_STEP;
    params.term_crit.max_iter = 300;
    params.term_crit.epsilon  = 0.1;
    params.term_crit.type   = CV_TERMCRIT_ITER|CV_TERMCRIT_EPS;     
    //cluster the data
    em_model.train ( samples, Mat(), params, &labels );     

    cv::Mat probs;
    probs = em_model.getProbs();

    cv::Mat weights;
    weights = em_model.getWeights();

cv::Mat modelIndex = cv::Mat::zeros ( img.rows, img.cols, CV_8UC3 );

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;     

    int response = cvRound ( em_model.predict ( sample ) ); 
    modelIndex.data [ modelIndex.cols*i + j] = response;

    

我的问题是:

首先,我想提取每个模型,这里一共五个,然后将这些对应的像素值存储在五个不同的矩阵中。在这种情况下,我可以分别有五种不同的颜色。这里我只获取了它们的索引,有什么方法可以在这里实现它们对应的颜色吗?为方便起见,我可以从根据这五个 GMM 找到主色开始。

其次,这里我的示例数据点是“100”,它们大约需要 3 秒。但我想在不超过 30 毫秒的时间内完成所有这些事情。我知道使用 GMM 的 OpenCV 背景提取执行得非常快,低于 20 毫秒,这意味着我必须有一种方法可以在 30 毫秒内完成所有 600x800=480000 像素的所有这些操作。我发现predict 函数是最耗时的一个。

【问题讨论】:

这个问题还有效吗?还是解决了there?问候 @remi:这个问题是一个老问题,但是在我问了你回答的另一个问题之后,我用颜色提取和计算速度更新了这个问题。你可以帮帮我吗?谢谢。 我不太明白这个问题。提取颜色对我来说没有意义。您是否尝试计算主色?或者量化颜色?你的代码对我帮助不大。关于速度问题,在大多数情况下使用 params.cov_mat_type = COV_MAT_DIAGONAL 就足够了,并且会加快您的进程 @remi 我正在尝试提取场景的每种颜色,从主要颜色开始。请在这个话题上帮助我。谢谢。 @remi 我试过“params.cov_mat_type = COV_MAT_DIAGONAL”,但没有什么大的不同。 【参考方案1】:

第一个问题:

为了进行颜色提取,您首先需要使用输入像素来训练 EM。之后,您只需再次遍历所有输入像素并使用 predict() 对每个像素进行分类。我附上了一个小例子,它利用 EM 基于颜色进行前景/背景分离。它向您展示了如何提取每个高斯的主色(均值)以及如何访问原始像素颜色。

#include <opencv2/opencv.hpp>

int main(int argc, char** argv) 

    cv::Mat source = cv::imread("test.jpg");

    //ouput images
    cv::Mat meanImg(source.rows, source.cols, CV_32FC3);
    cv::Mat fgImg(source.rows, source.cols, CV_8UC3);
    cv::Mat bgImg(source.rows, source.cols, CV_8UC3);

    //convert the input image to float
    cv::Mat floatSource;
    source.convertTo(floatSource, CV_32F);

    //now convert the float image to column vector
    cv::Mat samples(source.rows * source.cols, 3, CV_32FC1);
    int idx = 0;
    for (int y = 0; y < source.rows; y++) 
        cv::Vec3f* row = floatSource.ptr<cv::Vec3f > (y);
        for (int x = 0; x < source.cols; x++) 
            samples.at<cv::Vec3f > (idx++, 0) = row[x];
        
    

    //we need just 2 clusters
    cv::EMParams params(2);
    cv::ExpectationMaximization em(samples, cv::Mat(), params);

    //the two dominating colors
    cv::Mat means = em.getMeans();
    //the weights of the two dominant colors
    cv::Mat weights = em.getWeights();

    //we define the foreground as the dominant color with the largest weight
    const int fgId = weights.at<float>(0) > weights.at<float>(1) ? 0 : 1;

    //now classify each of the source pixels
    idx = 0;
    for (int y = 0; y < source.rows; y++) 
        for (int x = 0; x < source.cols; x++) 

            //classify
            const int result = cvRound(em.predict(samples.row(idx++), NULL));
            //get the according mean (dominant color)
            const double* ps = means.ptr<double>(result, 0);

            //set the according mean value to the mean image
            float* pd = meanImg.ptr<float>(y, x);
            //float images need to be in [0..1] range
            pd[0] = ps[0] / 255.0;
            pd[1] = ps[1] / 255.0;
            pd[2] = ps[2] / 255.0;

            //set either foreground or background
            if (result == fgId) 
                fgImg.at<cv::Point3_<uchar> >(y, x, 0) = source.at<cv::Point3_<uchar> >(y, x, 0);
             else 
                bgImg.at<cv::Point3_<uchar> >(y, x, 0) = source.at<cv::Point3_<uchar> >(y, x, 0);
            
        
    

    cv::imshow("Means", meanImg);
    cv::imshow("Foreground", fgImg);
    cv::imshow("Background", bgImg);
    cv::waitKey(0);

    return 0;

我已经用下图测试了代码,它的表现相当不错。

第二个问题:

我注意到最大集群数对性能有很大影响。因此,最好将其设置为一个非常保守的值,而不是将其留空或将其设置为示例中的样本数。此外,文档还提到了一个迭代过程,以使用较少约束的参数重复优化模型。也许这会给你一些加速。要了解更多信息,请查看为 train() here 提供的示例代码中的文档。

【讨论】:

我试过你的代码,它工作得很好,除了它的计算速度。无论如何,我会尽力处理。非常感谢您的回答。 好吧,我认为您想将算法实时应用于某些图像流是对的吗?如果是,也许你不需要在每一帧中训练 EM,而是用第一张图像训练它,然后只在连续的帧中进行预测,或者如果你需要在每张图像中训练,那么从前一帧和 COV_MAT_DIAGONAL 的值开始(请请参阅 OpenCV 文档中给出的示例中的代码文档以了解 train 方法) 耗时的不是训练部分,而是“预测”部分。我正在处理视频帧,预测一个 600x800 大小的帧大约需要 3 秒!您还有其他加快速度的想法吗? 好吧,如果您尝试跟踪一个对象,您可以利用空间相干性并将 predict() 应用于对象最后一个已知位置周围的小块。例如,补丁可以是一个圆,它位于对象的最后一个已知质心上,并且半径比最后一个已知半径大一点。当然,您可以使用更复杂的方法,例如卡尔曼滤波,但对于快速入门,这应该足够了。 非常感谢您的建议。同时,我想知道 OpenCV MoG 怎么能如此快地使用 GMM 减去背景?我查看了代码,似乎它使用了 k-means 算法,但不确定,但仍然无法完全理解它。对于相同的视频帧,它只使用了大约 30 毫秒。

以上是关于OpenCV:基于高斯混合模型的颜色提取的主要内容,如果未能解决你的问题,请参考以下文章

opencv:使用高斯混合模型(GMM)源码对视频进行背景差分法

2020/02/28 高斯混合模型以及GMM聚类

高斯混合模型(理论+opencv实现)

高斯混合模型

05 EM算法 - 高斯混合模型 - GMM

语音识别基于高斯混合模型(GMM)的语音识别matlab源码