OpenCV: извлечение цвета на основе модели смеси Гаусса

Я пытаюсь использовать алгоритм 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 миллисекунд. Я знаю, что извлечение фона OpenCV, в котором используется GMM, выполняется очень быстро, менее 20 мс, это означает, что у меня должен быть способ сделать все это в течение 30 мс для всех 600x800 = 480000 пикселей. Я обнаружил, что функция predict занимает больше всего времени.


person E_learner    schedule 19.09.2012    source источник
comment
Этот вопрос все еще активен? Или это было решено здесь? С уважением   -  person remi    schedule 19.10.2012
comment
@remi: этот вопрос был старым, но после того, как я задал другой вопрос, на который вы ответили, я обновил этот, указав скорость извлечения цвета и вычислений. Не могли бы вы помочь мне? Спасибо.   -  person E_learner    schedule 19.10.2012
comment
Я не очень понимаю этот вопрос. Извлечение цветов не имеет для меня смысла. Вы пытаетесь вычислить доминирующие цвета? Или квантовать цвета? Ваш код мне не очень помогает. Что касается скорости, то в большинстве случаев достаточно использовать params.cov_mat_type = COV_MAT_DIAGONAL, и это ускорит ваш процесс.   -  person remi    schedule 20.10.2012
comment
@remi Я пытаюсь извлечь каждый цвет сцены, начиная с доминирующего. Пожалуйста, помогите мне в этой теме. Спасибо.   -  person E_learner    schedule 20.10.2012
comment
@remi Я пробовал params.cov_mat_type = COV_MAT_DIAGONAL, но это не имело большого значения.   -  person E_learner    schedule 21.10.2012


Ответы (1)


Первый вопрос:

Чтобы сделать извлечение цвета, вам сначала нужно обучить EM с вашими входными пикселями. После этого вы просто снова перебираете все входные пиксели и используете pred() для классификации каждого из них. Я прикрепил небольшой пример, в котором используется 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() здесь.

person AD-530    schedule 22.10.2012
comment
Я попробовал ваш код, и он работает отлично, за исключением скорости расчета. Во всяком случае, я постараюсь справиться с этим. Большое спасибо за ваш ответ. - person E_learner; 23.10.2012
comment
Хорошо, правильно ли я думаю, что вы хотите применить алгоритм в реальном времени к какому-то потоку изображений? Если да, возможно, вам не нужно тренировать EM в каждом кадре, а тренировать его с первым изображением, а затем просто прогнозировать в последовательных кадрах или, если вам нужно тренировать каждое изображение, тогда начните со значений из предыдущего поезда и COV_MAT_DIAGONAL (пожалуйста, обратитесь к документу кода внутри примера, приведенного в документации OpenCV для метода обучения) - person AD-530; 23.10.2012
comment
это не часть обучения, которая отнимает много времени, а часть прогнозирования. Я раздаю кадры видео, а для предсказания одного кадра размером 600х800 требуется порядка 3 секунд! Есть ли у вас какие-либо другие идеи для ускорения? - person E_learner; 23.10.2012
comment
Что ж, если вы попытаетесь отследить объект, вы можете использовать пространственную когерентность и применить метод predict() только к небольшому участку вокруг последнего известного положения объекта. Например, патч может быть кругом, расположенным на последнем известном центроиде объекта и имеющим радиус, немного превышающий последний известный радиус. Конечно, вы можете использовать более сложные подходы, такие как Kalman-Filtering, но для быстрого старта этого должно быть достаточно. - person AD-530; 23.10.2012
comment
Большое спасибо за ваше предложение. Между тем, мне интересно, как OpenCV MoG может так быстро вычитать фон с помощью GMM? Я просмотрел код, похоже, он использует алгоритм k-средних, но не уверен, но все еще не мог полностью его понять. Для того же видеокадра требуется всего около 30 мс. - person E_learner; 23.10.2012
comment
Этот подход описан в этой статье: personal. ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/ - person AD-530; 23.10.2012