Meanshift算法用于跟踪物体的问题:计算搜索窗口的质心更新

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上的第二个查询,请原谅我可能忘记遵循的任何指南。

编辑

在WHILE-LOOP中将m00、m01、m10更改为块级变量,而不是函数级变量,感谢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;
    }
}
1个回答

1
你的算法总是独立于输入数据而收敛于左上角的原因在于,m00m10m01从未被重置为零:
  • 在第0次迭代中,对于每个时刻变量m00m10m01,计算正确的值m0
  • 在第0次迭代和第1次迭代之间,时刻变量不会被重置,并保持其先前的值
  • 因此,在第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 );

感谢您指出另一个错误,我以为在那之后它应该可以工作了,但现在搜索窗口跳到边缘而不是聚焦于ROI。好吧,算法应该在反向投影图像中找到概率分布模式,并将搜索窗口居中放置在此模式上。 - Ragesam
糟糕,我的错:P 谢谢你的指导,Daniel。我应该把它现在保留还是重新编辑一下呢?另外,我不确定我是否可以在这里发布整个项目的git链接?因为我觉得对于任何想要运行整个代码的人来说会很有帮助。 - Ragesam
@Ragesam 如果您重新编辑一下会更好。目前,我还没有检查新问题,只是澄清了我的先前回答。至于发布github链接的规则,我不太清楚。实际上,我需要更多信息才能继续,但不需要整个项目:- MatRect的结构是什么?是否可以显示backproj的小数据样本(大约10个点)以及win的值?谢谢 - Daniel Strul
@Ragesam 是的,谢谢!我已经更新了我的答案来解释你的“窗口到处跳动”的行为,一个简单的修正应该可以解决它。 - Daniel Strul
1
非常感谢,我想不到我竟然忽略了在全局图像坐标中工作。无论如何,你帮助我找到了不止一个漏洞,而是三个 :) 现在它运行得非常好。 - Ragesam
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接