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 мс для всех 600×800 = 480000 пикселей. я нашел predict функция является наиболее трудоемкой.

7

Решение

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

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

11

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]