用于跟踪对象问题的 Meanshift 算法计算搜索的质心更新 window

Meanshift algorithm for tracking objects issue computing centroid update of search window

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

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

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

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

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

编辑

将 m00 、 m01 、 m10 更改为 WHILE-LOOP 中的块级变量而不是函数级变量,感谢 Daniel Strul 指出,但问题仍然存在。现在搜索 window 绕着帧边界跳转,而不是关注 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;
    }
}

你的算法总是独立于输入数据收敛到左上角的原因是 m00m10m01 永远不会重置为零:

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

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

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

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