я использую cv::EM
алгоритм классификации гауссовой смеси для потоков изображений. Однако при классификации пикселей на разные модели EM::prediction
Я обнаружил, что этот метод слишком медленный, использует около 3 секунд для одного изображения 600×800. С другой стороны, MOG background subtractor
что обеспечивается OpenCV, выполняет эту часть очень быстро, использует всего около 30 мс. Поэтому я решил использовать его метод выполнения, чтобы заменить EM::prediction
часть. Однако я не знаю, как это изменить.
Код, который я использую до prediction
часть выглядит следующим образом:
cv::Mat floatSource;
source.convertTo ( floatSource, CV_32F );
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];
}
}
cv::EMParams params(2); // num of mixture we use is 2 here
cv::ExpectationMaximization em ( samples, cv::Mat(), params );
cv::Mat means = em.getMeans();
cv::Mat weight = em.getWeights();
const int fgId = weights.at<float>(0) > weights.at<flaot>(1) ? 0:1;
idx = 0;
for ( int y = 0; y < source.rows; y ++ )
{
for ( int x = 0; x < source.cols; x ++ )
{
const int result = cvRound ( em.predict ( samples.row ( idx++ ), NULL );
}
}
Частичный код, который я нашел в «cvbgfg_gaussmix.cpp» для EM prediction
это так:
static void process8uC3 ( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fgmask, double learningRate )
{
int x, y, k, k1, rows = image.rows, cols = image.cols;
float alpha = (float)learningRate, T = (float)obj.backgroundRatio, vT = (float)obj.varThreshold;
int K = obj.nmixtures;
const float w0 = (float)CV_BGFG_MOG_WEIGHT_INIT;
const float sk0 = (float)(CV_BGFG_MOG_WEIGHT_INIT/CV_BGFG_MOG_SIGMA_INIT);
const float var0 = (float) (CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT);
for ( y = 0; y < rows; y ++ )
{
const uchar* src = image.ptr<uchar>(y);
uchar* dst = fgmask.ptr<uchar>(y);
MixData<Vec3f>* mptr = (MixData<Vec3f>*)obj.bgmodel.ptr(y);
for ( x = 0; x < cols; x++, mptr += K )
{
float wsum = 0, dw = 0;
Vec3f pix ( src [x*3], src[x*3+1], src[x*3+2]);
for ( k = 0; k < K; k ++ )
{
float w = mptr[k].weight;
Vec3f mu = mptr[k].mean[0];
Vec3f var = mptr[k].var[0];
Vec3f diff = pix - mu;
float d2 = diff.dot(diff);
if ( d2 < vT * (var[0] +var[1] + var[2] )
{
dw = alpha * ( 1.f - w );
mptr[k].weight = w + dw;
mptr[k].mean = mu + alpha * diff;
var = Vec3f ( max ( var[0] + alpha * ( diff[0] * diff[1] - var[0] ), FLT_EPSILON),
max ( var[1] + alpha * ( diff[1]*diff[1] - var[1] ), FLT_EPSILON,
max ( var[2] + alpha * ( diff[2]*diff[2] - var[2] ), FLT_EPSILON ));
mptr[k].var = var;
mptr[k].sortKey = w/sqrt ( var[0] + var[1] + var[2] );
for ( k1 = k-1; k1 >= 0; k1-- )
{
if ( mptr[k1].sortKey > mptr[k1+1].sortKey)
break;
std::swap ( mptr[k1],mptr[k1+1]);
}
break;
}
wsum += w;
}dst[x] = (uchar) (-(wsum >= T ));
wsum += dw;
if ( k == K )
{
wsum += w0 - mptr[K-1].weight;
mptr[k-1].weight = w0;
mptr[K-1].mean = pix;
mptr[K-1].var = Vec3f ( var0, var0, var0 );
mptr[K-1].sortKey = sk0;
}
else
for ( ; k < K; k ++ )
wsum += mptr[k].weight;
dw = 1.f/wsum;
for ( k = 0; k < K; k ++ )
{
mptr[k].weight *= dw;
mptr[k].sortKey *= dw;
}
}
}
}
Как я могу изменить этот частичный код, чтобы он мог использоваться в моем первом коде, чтобы em.predict
часть? Заранее спасибо.
Обновить
Я сделал это сам для того, чтобы использовать process8uC3
функция в моем коде:
cv::Mat fgImg ( 600, 800, CV_8UC3 );
cv::Mat bgImg ( 600, 800, CV_8UC3 );
double learningRate = 0.001;
int x, y, k, k1;
int rows = sourceMat.rows; //source opencv matrix
int cols = sourceMat.cols; //source opencv matrix
float alpha = (float) learningRate;
float T = 2.0;
float vT = 0.30;
int K = 3;
const float w0 = (float) CV_BGFG_MOG_WEIGTH_INIT;
const float sk0 = (float) (CV_BGFG_MOG_WEIGHT_INIT/CV_BGFG_MOG_SIGMA_INIT);
const float var0 = (float) (CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT);
const float minVar = FLT_EPSILON;
for ( y = 0; y < rows; y ++ )
{
const char* src = source.ptr < uchar > ( y );
uchar* dst = fgImg.ptr < uchar > ( y );
uchar* tmp = bgImg.ptr ( y );
MixData<cv::Vec3f>* mptr = (MixData<cv::Vec3f>*)tmp;
for ( x = 0; x < cols; x ++, mptr += K )
{
float w = mptr[k].weight;
cv::Vec3f mu = mpptr[k].mean[0];
cv::Vec3f var = mptr[k].var[0];
cv::Vec3f diff = pix - mu;
float d2 = diff.dot ( diff );
if ( d2 < vT * ( var[0] + var[1] + var[2] ) )
{
dw = alpha * ( 1.f - w );
mptr[k].weight = w + dw;
mptr[k].mean = mu + alpha * diff;
var = cv::Vec3f ( max ( var[0] + alpha*(diff[0]*diff[0]-var[0]),minVar),
max ( var[1]+ alpha*(diff[1]*diff[1]-var[1]),minVar),
max ( var[2] + alpha*(diff[2]*diff[2]-var[2]),minVar) );
mptr[k].var = var;
mptr[k].sortKey = w/sqrt ( var[0] + var[1] + var[2] );
for ( k1 = k-1; k1 >= 0; k1 -- )
{
if ( mptr[k1].sortKey > mptr[k1+1].sortKey )
break;
std::swap ( mptr[k1], mptr[k1+1] );
}
break;
}
wsum += w;
}
dst[x] = (uchar) (-(wsum >= T ));
wsum += dw;
if ( k == K )
{
wsum += w0 - mptr[k-1].weight;
mptr[k-1].weight = w0;
mptr[k-1].mean = pix;
mptr[k-1].var = cv::Vec3f ( var0, var0, var0 );
mptr[k-1].sortKey = sk0;
}
else
for ( ; k < K; k ++ )
{
mptr[k].weight *= dw;
mptr[k].sortKey *= dw;
}
}
}
}
Он скомпилирован без ошибок, но в результате получается масса. Я сомневаюсь, может быть, это что-то связано с ценностями T
а также vT
и изменил их несколькими другими значениями, но это не имело никакого значения. Поэтому я считаю, что даже если он скомпилирован без ошибок, я использовал его неправильно.
Не прямой ответ на ваши вопросы, а несколько комментариев к вашему коду:
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];
}
}
Я предполагаю, что здесь вы хотите создать матрицу со строками по строкам и столбцам с 3 столбцами, сохраняя значения пикселей RGB (или любое другое цветовое пространство, которое вы можете использовать). Во-первых, ваша матрица сэмплов неправильно инициализирована, так как вы забыли цикл на каналах изображения. Только первый канал заполняется в вашем коде. Но в любом случае, вы можете сделать то же самое, позвонив reshape
:
cv::Mat samples = floatSource.reshape(1, source.rows*source.cols)
Это не только исправит вашу ошибку, но и ускорит ваш процесс доступа к пикселям с помощью Mat.at<> на самом деле не так быстро, и изменение формы — это операция O (1), так как данные базовой матрицы не изменяются, а только количество строк / столбцов / каналов.
Во-вторых, вы можете сэкономить время, передав полную матрицу выборки в em :: предикат вместо каждой выборки. В данный момент вы делаете рядовые вызовы em :: предиката, в то время как вы можете сделать только один, плюс рядочный вызов столбца для mat.row (), который создает временную матрицу (заголовок).
Одним из способов ускорения этого дополнительно было бы распараллеливание вызовов для прогнозирования, например, используя TBB, который используется OpenCV (вы включили TBB при компиляции OpenCV? Возможно предикат уже многопоточный, не проверял это).
Взгляните на исходный код GrabCut в OpenCV: Модули / imgproc / SRC / grabcut.cpp.
Есть закрытый класс GMM (реализует обучающую модель гауссовой смеси и классификацию образцов) в этом модуле. Для инициализации GMM используется k-средство. Если вам нужна еще более быстрая инициализация, вы можете попробовать K-средства ++ алгоритм (см. generateCentersPP функция в Модули / ядро / SRC / matrix.cpp модуль).