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