OpenCV: color extraction based on Gaussian mixture model

后端 未结 1 1071
后悔当初
后悔当初 2020-12-23 15:40

I am trying to use opencv EM algorithm to do color extraction.I am using the following code based on example in opencv documentation:

cv::Mat capturedFrame (         


        
相关标签:
1条回答
  • 2020-12-23 16:14

    First Question:

    In order to do color extraction you first need to train the EM with your input pixels. After that you simply loop over all the input pixels again and use predict() to classify each of them. I've attached a small example that utilizes EM for foreground/background separation based on colors. It shows you how to extract the dominant color (mean) of each gaussian and how to access the original pixel color.

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

    I've tested the code with the following image and it performs quite good.

    enter image description here

    Second Question:

    I've noticed that the maximum number of clusters has a huge impact on the performance. So it's better to set this to a very conservative value instead of leaving it empty or setting it to the number of samples like in your example. Furthermore the documentation mentions an iterative procedure to repeatedly optimize the model with less-constrained parameters. Maybe this gives you some speed-up. To read more please have a look at the docs inside the sample code that is provided for train() here.

    0 讨论(0)
提交回复
热议问题