OpenCV:加速EM算法预测

时间:2012-10-24 14:03:44

标签: c++ image opencv gaussian image-segmentation

我正在使用cv::EM算法对图像流进行高斯混合模型分类。但是,在使用EM::prediction方法将像素分类到不同模型时,我发现它太慢了,对于一个600x800图像使用大约3秒。另一方面,OpenCV提供的MOG background subtractor非常快速地执行此部分,仅使用约30ms。所以我决定使用它的perform方法来替换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 );
    }
}

我从“{1}}的”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有关,并且使用其他几个值进行了更改,但它没有任何区别。所以我相信即使它编译没有错误,我也以错误的方式使用它。

2 个答案:

答案 0 :(得分:1)

不直接回答您的问题,而是对您的代码提出一些意见:

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&lt;&gt;访问像素的过程。实际上并不那么快,重塑是O(1)操作,因为底层矩​​阵数据没有改变,只有行/列/通道的数量。

其次,您可以通过将完整的样本矩阵传递给em :: predict而不是每个样本来节省一些时间。此时,您可以对em :: predict进行逐行调用,而只能执行一次,再加上逐行调用mat.row(),这将创建一个临时矩阵(标题)。

进一步加快速度的一种方法是并行调用预测,例如,使用OpenCV使用的TBB(你在编译OpenCV时是否打开TBB?也许预测已经是多线程的,没有检查过)。

答案 1 :(得分:1)

在OpenCV中查看GrabCut的源代码: modules / imgproc / src / grabcut.cpp 。 在该模块中有私有类 GMM (实现训练高斯混合模型和样本分类)。为了初始化GMM,使用k均值。如果您需要更快的初始化,可以尝试k-means++算法(请参阅 modules / core / src / matrix.cpp 模块中的 generateCentersPP 函数)。