2012-10-24 2 views
2

cv::EM 알고리즘을 사용하여 이미지 스트림에 대한 가우스 혼합 모델 분류를 수행합니다. 그러나 EM::prediction 방법을 사용하여 픽셀을 다른 모델로 분류하는 동안 너무 느리고 한 개의 600x800 이미지에 약 3 초를 사용합니다. 반면에 OpenCV에서 제공하는 MOG background subtractor은이 부분을 매우 빠르게 수행하며 약 30ms 만 사용합니다. 그래서 perform 메서드를 사용하여 EM::prediction 부분을 대체하기로 결정했습니다. 그러나, 나는 그것을 어떻게 바꿀 지 모른다.OpenCV : 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; 
      } 
    } 
} 
} 

:

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); 
    } 
} 

내가 EM prediction은 "cvbgfg_gaussmix.cpp"에서 발견 된 일부 코드는 다음과 같다 이 부분 코드를 어떻게 내 코드에서 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; 
      } 
     } 
    } 
} 

그것은 오류없이 컴파일하지만, 결과는 완전히 질량이다. 아마도 값이 TvT과 관련이 있으며 다른 값으로 변경했을 수도 있지만 차이는 없습니다. 그래서 나는 그것이 오류없이 컴파일 되어도 틀린 방식으로 사용했다고 믿습니다.

답변

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]; 
    } 
} 

내 생각 엔 픽셀을 저장, 당신은 행별로 COLS 행과 3 열 행렬을 만들려면 여기에 있다는 것입니다 RGB (또는 사용하고있는 다른 색상 공간) 값. 먼저 이미지 채널에서 루프를 잊어 버린 이후 샘플 행렬이 잘못 초기화됩니다. 코드에는 첫 번째 채널 만 채워집니다. 어쨌든, 당신은 reshape를 호출하여 그냥 같은 작업을 수행 할 수 있습니다

cv::Mat samples = floatSource.reshape(1, source.rows*source.cols) 

이 버그가 해결됩니다뿐만 아니라, 정말 빠르지 Mat.at <>를 사용하여 픽셀에 액세스하기로 프로세스의 속도를, 기본 행렬 데이터가 변경되지 않았으므로 행/열/채널 수만큼 O (1) 연산입니다.

둘째, 전체 샘플 행렬을 각 샘플 대신 em :: predict에 전달하여 시간을 절약 할 수 있습니다. 현재, em :: predict에 대한 rows-by-col 호출은 하나만 할 수 있으며 mat.row()에 대한 rows-by-cols 호출은 임시 매트릭스 (헤더)를 생성합니다.

이 속도를 더 높이는 한 가지 방법은 호출을 병렬화하여 예측을 수행하는 것입니다. OpenCV에서 사용하는 TBB를 사용합니다 (OpenCV 컴파일시 TBB를 켜셨습니까? 예측은 이미 다중 스레드이며 확인되지 않았을 수 있습니다).

+0

답변 해 주셔서 대단히 감사합니다. 나는 당신이 말한 것처럼 변형을 사용하여 그것을 한 다음 각 표본 대신 전체 표본을 사용했습니다. 그러나 그 결과는 큰 차이를 만들어 내지 못합니다. –

+0

OpenCV를 최적화하여 컴파일 했습니까? 이제는 모든 것을 다시 쓸 필요없이 할 수있는 가장 좋은 방법은 당신의 제안을 위해 – remi

+0

을 parellelize하는 것입니다. opencv에서 제공 한 배경 빼기가 왜 그렇게 빨리 작동하는지 궁금합니다. 이렇게 빨리 할 수있는 방법이 있어야한다고 생각합니다. 그래서 나는이 질문을했습니다. parellelize를 제외하고 나는이 "process8uC3"함수를 내 자신의 코드로 변경할 수 있다면 달성 할 수 있다고 생각한다. 그렇게 생각하지 않아? –

1

OpenCV에서 GrabCut의 소스 코드를 살펴보십시오 : modules/imgproc/src/grabcut.cpp. 이 모듈에는 개인 등급 GMM (학습 가우스 혼합 모델 및 샘플 분류 구현)이 있습니다.GMM을 초기화하기 위해 k-means가 사용됩니다. 더 빠른 초기화가 필요한 경우 k-means++ 알고리즘을 시도 할 수 있습니다 (generateCentersPP 함수는 modules/core/src/matrix.cpp 모듈에 있습니다).

+0

답변 해 주셔서 감사합니다. "Grabcut.cpp", 특히 언급 한 GMM 클래스를 살펴 보았지만 나에게 도움이되지 못하거나 잘못했을 수도 있습니다. 게다가, 나는 또한 Kmeans 알고리즘을 시도했지만 EM 알고리즘만큼 좋은 음영 지역의 색상을 처리 할 수없는 것처럼 보입니다. –

+0

그렇습니다. 혼합물을 분리 할 수없는 경우 초기화를 위해 k- 수단을 사용하는 것이 좋지 않습니다. ( – Vlad

+0

다윈과 같은 다른 라이브러리를 사용해보십시오. http://drwn.anu.edu.au/classdrwnGaussianMixture.html 그렇지 않습니다. 그래서 OpenCV로 인기가 있지만 컴퓨터 비전 및 기계 학습 알고리즘의 좋은 구현의 숫자가 있습니다 – Vlad