[opencv]goodFeaturesToTrack函数详细注释

来源:互联网 发布:外汇模拟软件下载 编辑:程序博客网 时间:2024/05/22 10:51

本文全篇转自 http://blog.csdn.net/xdfyoga1/article/details/44175637

OpenCV中的goodFeaturesToTrack函数可以计算Harris角点和shi-tomasi角点,但默认情况下计算的是shi-tomasi角点,函数原型如下:

[cpp] view plain copy
  1. void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,  
  2.                               int maxCorners, double qualityLevel, double minDistance,  
  3.                               InputArray _mask, int blockSize,  
  4.                               bool useHarrisDetector, double harrisK )  

_image:8位或32位浮点型输入图像,单通道

_corners:保存检测出的角点

maxCorners:角点数目最大值,如果实际检测的角点超过此值,则只返回前maxCorners个强角点

qualityLevel:角点的品质因子

minDistance:对于初选出的角点而言,如果在其周围minDistance范围内存在其他更强角点,则将此角点删除

_mask:指定感兴趣区,如不需在整幅图上寻找角点,则用此参数指定ROI

blockSize:计算协方差矩阵时的窗口大小

useHarrisDetector:指示是否使用Harris角点检测,如不指定,则计算shi-tomasi角点

harrisK:Harris角点检测需要的k值


goodFeaturesToTrack函数的定义在imgproc文件的featureselect.cpp中,下面给出了goodFeaturesToTrack函数的详细注释。

[cpp] view plain copy
  1. void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,  
  2.                               int maxCorners, double qualityLevel, double minDistance,  
  3.                               InputArray _mask, int blockSize,  
  4.                               bool useHarrisDetector, double harrisK )  
  5. {  
  6.     //如果需要对_image全图操作,则给_mask传入cv::Mat(),否则传入感兴趣区域  
  7.     Mat image = _image.getMat(), mask = _mask.getMat();    
  8.   
  9.     CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 );  //对参数有一些基本要求  
  10.     CV_Assert( mask.empty() || (mask.type() == CV_8UC1 && mask.size() == image.size()) );  
  11.   
  12.     Mat eig, tmp;   //eig存储每个像素协方差矩阵的最小特征值,tmp用来保存经膨胀后的eig  
  13.     if( useHarrisDetector )  
  14.         cornerHarris( image, eig, blockSize, 3, harrisK ); //blockSize是计算2*2协方差矩阵的窗口大小,sobel算子窗口为3,harrisK是计算Harris角点时需要的值  
  15.     else  
  16.         cornerMinEigenVal( image, eig, blockSize, 3 );  //计算每个像素对应的协方差矩阵的最小特征值,保存在eig中  
  17.   
  18.     double maxVal = 0;  
  19.     minMaxLoc( eig, 0, &maxVal, 0, 0, mask );   //maxVal保存了eig的最大值  
  20.     threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO );  //阈值设置为maxVal乘以qualityLevel,大于此阈值的保持不变,小于此阈值的都设为0  
  21.       
  22.     //默认用3*3的核膨胀,膨胀之后,除了局部最大值点和原来相同,其它非局部最大值点被    
  23.     //3*3邻域内的最大值点取代,如不理解,可看一下灰度图像的膨胀原理    
  24.     dilate( eig, tmp, Mat());  //tmp中保存了膨胀之后的eig  
  25.   
  26.     Size imgsize = image.size();   
  27.   
  28.     vector<const float*> tmpCorners;  //存放粗选出的角点地址  
  29.   
  30.     // collect list of pointers to features - put them into temporary image   
  31.     forint y = 1; y < imgsize.height - 1; y++ )  
  32.     {  
  33.         const float* eig_data = (const float*)eig.ptr(y);  //获得eig第y行的首地址  
  34.         const float* tmp_data = (const float*)tmp.ptr(y);  //获得tmp第y行的首地址  
  35.         const uchar* mask_data = mask.data ? mask.ptr(y) : 0;  
  36.   
  37.         forint x = 1; x < imgsize.width - 1; x++ )  
  38.         {  
  39.             float val = eig_data[x];  
  40.             if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) )  //val == tmp_data[x]说明这是局部极大值  
  41.                 tmpCorners.push_back(eig_data + x);  //保存其位置  
  42.         }  
  43.     }  
  44.   
  45.     //-----------此分割线以上是根据特征值粗选出的角点,我们称之为弱角点----------//  
  46.     //-----------此分割线以下还要根据minDistance进一步筛选角点,仍然能存活下来的我们称之为强角点----------//  
  47.   
  48.     sort( tmpCorners, greaterThanPtr<float>() );  //按特征值降序排列,注意这一步很重要,后面的很多编程思路都是建立在这个降序排列的基础上  
  49.     vector<Point2f> corners;  
  50.     size_t i, j, total = tmpCorners.size(), ncorners = 0;  
  51.   
  52.     //下面的程序有点稍微难理解,需要自己仔细想想  
  53.     if(minDistance >= 1)    
  54.     {  
  55.          // Partition the image into larger grids  
  56.         int w = image.cols;  
  57.         int h = image.rows;  
  58.   
  59.         const int cell_size = cvRound(minDistance);   //向最近的整数取整  
  60.   
  61.     //这里根据cell_size构建了一个矩形窗口grid(虽然下面的grid定义的是vector<vector>,而并不是我们这里说的矩形窗口,但为了便于理解,还是将grid想象成一个grid_width * grid_height的矩形窗口比较好),除以cell_size说明grid窗口里相差一个像素相当于_image里相差minDistance个像素,至于为什么加上cell_size - 1后面会讲  
  62.         const int grid_width = (w + cell_size - 1) / cell_size;   
  63.         const int grid_height = (h + cell_size - 1) / cell_size;  
  64.   
  65.         std::vector<std::vector<Point2f> > grid(grid_width*grid_height);  //vector里面是vector,grid用来保存获得的强角点坐标  
  66.   
  67.         minDistance *= minDistance;  //平方,方面后面计算,省的开根号  
  68.   
  69.         for( i = 0; i < total; i++ )     // 刚刚粗选的弱角点,都要到这里来接收新一轮的考验  
  70.         {  
  71.             int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  //tmpCorners中保存了角点的地址,eig.data返回eig内存块的首地址  
  72.             int y = (int)(ofs / eig.step);   //角点在原图像中的行  
  73.             int x = (int)((ofs - y*eig.step)/sizeof(float));  //在原图像中的列  
  74.   
  75.             bool good = true;  //先认为当前角点能接收考验,即能被保留下来  
  76.   
  77.             int x_cell = x / cell_size;  //x_cell,y_cell是角点(y,x)在grid中的对应坐标  
  78.             int y_cell = y / cell_size;  
  79.   
  80.             int x1 = x_cell - 1;  // (y_cell,x_cell)的4邻域像素  
  81.             int y1 = y_cell - 1;  //现在知道为什么前面grid_width定义时要加上cell_size - 1了吧,这是为了使得(y,x)在grid中的4邻域像素都存在,也就是说(y_cell,x_cell)不会成为边界像素  
  82.             int x2 = x_cell + 1;    
  83.             int y2 = y_cell + 1;  
  84.   
  85.             // boundary check,再次确认x1,y1,x2或y2不会超出grid边界  
  86.             x1 = std::max(0, x1);  //比较0和x1的大小  
  87.             y1 = std::max(0, y1);  
  88.             x2 = std::min(grid_width-1, x2);  
  89.             y2 = std::min(grid_height-1, y2);  
  90.   
  91.             //记住grid中相差一个像素,相当于_image中相差了minDistance个像素  
  92.             forint yy = y1; yy <= y2; yy++ )  // 行  
  93.             {  
  94.                 forint xx = x1; xx <= x2; xx++ )  //列  
  95.                 {  
  96.                     vector <Point2f> &m = grid[yy*grid_width + xx];  //引用  
  97.   
  98.                     if( m.size() )  //如果(y_cell,x_cell)的4邻域像素,也就是(y,x)的minDistance邻域像素中已有被保留的强角点  
  99.                     {                 
  100.                         for(j = 0; j < m.size(); j++)   //当前角点周围的强角点都拉出来跟当前角点比一比  
  101.                         {  
  102.                             float dx = x - m[j].x;  
  103.                             float dy = y - m[j].y;  
  104.                //注意如果(y,x)的minDistance邻域像素中已有被保留的强角点,则说明该强角点是在(y,x)之前就被测试过的,又因为tmpCorners中已按照特征值降序排列(特征值越大说明角点越好),这说明先测试的一定是更好的角点,也就是已保存的强角点一定好于当前角点,所以这里只要比较距离,如果距离满足条件,可以立马扔掉当前测试的角点  
  105.                             if( dx*dx + dy*dy < minDistance )  
  106.                             {                                                         
  107.                 good = false;  
  108.                                 goto break_out;  
  109.                             }  
  110.                         }  
  111.                     }  
  112.                 }   // 列  
  113.             }    //行  
  114.   
  115.             break_out:  
  116.   
  117.             if(good)  
  118.             {  
  119.                 // printf("%d: %d %d -> %d %d, %d, %d -- %d %d %d %d, %d %d, c=%d\n",  
  120.                 //    i,x, y, x_cell, y_cell, (int)minDistance, cell_size,x1,y1,x2,y2, grid_width,grid_height,c);  
  121.                 grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));  
  122.   
  123.                 corners.push_back(Point2f((float)x, (float)y));  
  124.                 ++ncorners;  
  125.   
  126.                 if( maxCorners > 0 && (int)ncorners == maxCorners )  //由于前面已按降序排列,当ncorners超过maxCorners的时候跳出循环直接忽略tmpCorners中剩下的角点,反正剩下的角点越来越弱  
  127.                     break;  
  128.             }  
  129.         }  
  130.     }  
  131.     else    //除了像素本身,没有哪个邻域像素能与当前像素满足minDistance < 1,因此直接保存粗选的角点  
  132.     {  
  133.         for( i = 0; i < total; i++ )  
  134.         {  
  135.             int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  
  136.             int y = (int)(ofs / eig.step);   //粗选的角点在原图像中的行  
  137.             int x = (int)((ofs - y*eig.step)/sizeof(float));  //在图像中的列  
  138.   
  139.             corners.push_back(Point2f((float)x, (float)y));  
  140.             ++ncorners;  
  141.             if( maxCorners > 0 && (int)ncorners == maxCorners )    
  142.                 break;  
  143.         }  
  144.     }  
  145.   
  146.     Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);  
  147.   
  148.     /* 
  149.     for( i = 0; i < total; i++ ) 
  150.     { 
  151.         int ofs = (int)((const uchar*)tmpCorners[i] - eig.data); 
  152.         int y = (int)(ofs / eig.step); 
  153.         int x = (int)((ofs - y*eig.step)/sizeof(float)); 
  154.  
  155.         if( minDistance > 0 ) 
  156.         { 
  157.             for( j = 0; j < ncorners; j++ ) 
  158.             { 
  159.                 float dx = x - corners[j].x; 
  160.                 float dy = y - corners[j].y; 
  161.                 if( dx*dx + dy*dy < minDistance ) 
  162.                     break; 
  163.             } 
  164.             if( j < ncorners ) 
  165.                 continue; 
  166.         } 
  167.  
  168.         corners.push_back(Point2f((float)x, (float)y)); 
  169.         ++ncorners; 
  170.         if( maxCorners > 0 && (int)ncorners == maxCorners ) 
  171.             break; 
  172.     } 
  173. */  
  174. }  


原创粉丝点击