4

我一直在尝试实现用于跟踪对象的 meanshift 算法,并且已经了解了所涉及的概念。

按照现在,我已经成功地从我的相机生成了一个反向投影流,它带有一个单通道色调 roi 直方图和一个单通道色调视频流,看起来不错,我知道 opencv 库中有一个 meanshift 函数,但我正在尝试实现我自己使用opencv中提供的数据结构,计算矩并计算搜索窗口的平均质心。

但由于某种原因,我无法在我的代码中找到问题,因为它不断收敛到我的视频流的左上角,以便跟踪任何输入 roi(感兴趣区域)。以下是用于计算搜索窗口质心的函数的代码片段,我觉得问题出在哪里,但不确定是什么,如果有人能指出我正确的方向,我将不胜感激:

void moment(Mat &backproj, Rect &win){

    int x_c, y_c, x_c_new, y_c_new;    
    int idx_row, idx_col;
    double m00 = 0.0 , m01 = 0.0 , m10 = 0.0 ;
    double res = 1.0, TOL = 0.003 ;

    //Set the center of search window as the center of the probabilistic image:
    y_c =  (int) backproj.rows / 2 ; 
    x_c =  (int) backproj.cols / 2 ; 

    //Centroid search solver until residual below certain tolerance:
    while (res > TOL){

        win.width = (int) 80; 
        win.height = (int) 60; 

        //First array element at position (x,y) "lower left corner" of the search window:
        win.x = (int) (x_c - win.width / 2) ;
        win.y = (int) (y_c - win.height / 2); 

        //Modulo correction since modulo of negative integer is negative in C:
        if (win.x < 0)
                win.x = win.x % backproj.cols + backproj.cols ;

        if (win.y < 0)
                win.y = win.y % backproj.rows + backproj.rows ;   

        for (int i = 0; i < win.height; i++ ){  

                //Traverse along y-axis (height) i.e. rows ensuring wrap around top/bottom boundaries:                  
                idx_row = (win.y + i) % (int)backproj.rows ;

                for (int j = 0; j < win.width; j++ ){

                        //Traverse along x-axis (width) i.e. cols ensuring wrap around left/right boundaries:
                        idx_col = (win.x + j) % (int)backproj.cols ;    
                        //Compute Moments:                            
                        m00 += (double) backproj.at<uchar>(idx_row, idx_col) ;
                        m10 += (double) backproj.at<uchar>(idx_row, idx_col) * i ;
                        m01 += (double) backproj.at<uchar>(idx_row, idx_col) * j ;
                }
        }

        //Compute new centroid coordinates of the search window:
        x_c_new = (int) ( m10 / m00 ) ;
        y_c_new = (int) ( m01 / m00 );

        //Compute the residual:
        res = sqrt( pow((x_c_new - x_c), 2.0) + pow((y_c_new - y_c), 2.0) ) ;

        //Set new search window centroid coordinates:
        x_c = x_c_new;
        y_c = y_c_new;
    }
}

这是我对 stackoverflow 的第二次查询,所以请原谅我忘记遵循的任何准则。

编辑

将 m00 , m01 , m10 更改为 WHILE-LOOP 中的块级变量而不是函数级变量,感谢 Daniel Strul 指出,但问题仍然存在。现在搜索窗口跳过帧边界而不是关注 roi。

    void moment(Mat &backproj, Rect &win){

    int x_c, y_c, x_c_new, y_c_new;    
    int idx_row, idx_col;
    double m00 , m01 , m10 ;
    double res = 1.0, TOL = 0.003 ;

    //Set the center of search window as the center of the probabilistic image:
    y_c =  (int) backproj.rows / 2 ; 
    x_c =  (int) backproj.cols / 2 ; 

    //Centroid search solver until residual below certain tolerance:
    while (res > TOL){

        m00 = 0.0 , m01 = 0.0 , m10 = 0.0
        win.width = (int) 80; 
        win.height = (int) 60; 

        //First array element at position (x,y) "lower left corner" of the search window:
        win.x = (int) (x_c - win.width / 2) ;
        win.y = (int) (y_c - win.height / 2); 

        //Modulo correction since modulo of negative integer is negative in C:
        if (win.x < 0)
                win.x = win.x % backproj.cols + backproj.cols ;

        if (win.y < 0)
                win.y = win.y % backproj.rows + backproj.rows ;   

        for (int i = 0; i < win.height; i++ ){  

                //Traverse along y-axis (height) i.e. rows ensuring wrap around top/bottom boundaries:                  
                idx_row = (win.y + i) % (int)backproj.rows ;

                for (int j = 0; j < win.width; j++ ){

                        //Traverse along x-axis (width) i.e. cols ensuring wrap around left/right boundaries:
                        idx_col = (win.x + j) % (int)backproj.cols ;    
                        //Compute Moments:                            
                        m00 += (double) backproj.at<uchar>(idx_row, idx_col) ;
                        m10 += (double) backproj.at<uchar>(idx_row, idx_col) * i ;
                        m01 += (double) backproj.at<uchar>(idx_row, idx_col) * j ;
                }
        }

        //Compute new centroid coordinates of the search window:
        x_c_new = (int) ( m10 / m00 ) ;
        y_c_new = (int) ( m01 / m00 );

        //Compute the residual:
        res = sqrt( pow((x_c_new - x_c), 2.0) + pow((y_c_new - y_c), 2.0) ) ;

        //Set new search window centroid coordinates:
        x_c = x_c_new;
        y_c = y_c_new;
    }
}
4

1 回答 1

1

您的算法始终独立于输入数据而收敛到左上角的原因是m00m10并且m01永远不会重置为零:

  • 在第 0 次迭代中,对于每个矩变量和m00,您计算正确的值 m 0m10m01
  • 在迭代 0 和迭代 1 之间,矩变量不会重置并保持其先前的值
  • 因此,在迭代 1 中,对于每个矩变量和m00,您实际上将新矩与旧矩相加并获得 ( m 0 + m 1 )m10m01
  • 在第 2 次迭代中,您继续将新时刻与之前的时刻相加并获得 ( m 0 + m 1 + m 2 )
  • 以此类推,逐次迭代。

至少,矩变量应该在每次迭代开始时重置。

理想情况下,它们不应该是函数级变量,而应该是块级变量,因为它们在循环迭代之外没有用处(调试目的除外):

while (res > TOL){
    ...
    double m00 = 0.0, m01 = 0.0, m10 = 0.0;
    for (int i = 0; i < win.height; i++ ){
        ...

编辑 1

您遇到的第二个问题(ROI 在各处跳跃)的原因是矩的计算是基于相对坐标ij.

因此,您计算的是 [ avg(j) , avg(i) ],而您真正想要的是 [ avg(y) , avg(x) ]。为了解决这个问题,我提出了第一个解决方案。我已经用下面更简单的解决方案替换了它。

编辑 2 最简单的解决方案是在每次迭代结束时添加 ROI 角的坐标:

    x_c_new = win.x + (int) ( m10 / m00 ) ;
    y_c_new = win.y + (int) ( m01 / m00 );
于 2015-10-26T21:44:32.017 回答