上面一篇博客分析了HARRIS和ShiTomasi角点检测的源代码。而为了提取更准确的角点,OpenCV中提供了goodFeaturesToTrack()这个API函数,来获取更加准确的角点位置。这篇博客主要分析goodFeaturesToTrack()的源代码。

  函数原型如下:

  1. void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,
  2. int maxCorners, double qualityLevel, double minDistance,
  3. InputArray _mask, int blockSize, int gradientSize,
  4. bool useHarrisDetector, double harrisK )

  _image为输入的单通道图像;_corners为输出提取的角点坐标;maxCorners为设置的最大角点个数,程序中会按照角点强度降序排序,超过maxCorners的角点将被舍弃;qualityLevel为角点强度的阈值系数,如果检测出所有角点中的最大强度为max,则强度值<max*qualityLevel得角点会被舍弃;minDistance为角点与其邻域强角点之间的欧式距离,与邻域强角点距离小于minDistance的角点将被舍弃;_mask为设定的感兴趣区域,通常可不设置;blockSize是协方差矩阵滤波的窗口大小;gradientSize为sobel算子求微分的窗口的大小;useHarrisDetector为是否使用Harris角点检测;harrisK为Harris角点检测特征表达式中的常数k值。

  接下来看源代码,首先根据useHarrisDetector这个flg值的设定选取不同的方法提取初始角点:

  1. //提取初始角点
      if( useHarrisDetector )
  2. cornerHarris( image, eig, blockSize, gradientSize, harrisK );
  3. else
  4. cornerMinEigenVal( image, eig, blockSize, gradientSize );

  然后选取出所有角点特征值的最大值maxVal,并进行阈值化,将小于maxVal*qulityLevel的特征值舍弃掉(置为0),其余保持不变。并用3x3的方形膨胀核对特征值矩阵进行膨胀操作,膨胀的目的是使孤立的局部最大值扩大化,膨胀过后3x3邻域的非局部最大值点被局部最大值取代。膨胀过后的特征值图像为tmp,后面会用到。

  1. //获取最大角点特征值
  2. double maxVal = ;
  3. minMaxLoc( eig, , &maxVal, , , _mask );
  4. threshold( eig, eig, maxVal*qualityLevel, , THRESH_TOZERO );
  5. dilate( eig, tmp, Mat());

  取出局部最大值点,并将其地址保存到tmpCorners这个容器中。(val==tem_data[x]表示该点未被膨胀操作影响,就是局部极大值点)

  1. // collect list of pointers to features - put them into temporary image
  2. Mat mask = _mask.getMat();
  3. for( int y = ; y < imgsize.height - ; y++ )
  4. {
  5. const float* eig_data = (const float*)eig.ptr(y);
  6. const float* tmp_data = (const float*)tmp.ptr(y);
  7. const uchar* mask_data = mask.data ? mask.ptr(y) : ;
  8.  
  9. for( int x = ; x < imgsize.width - ; x++ )
  10. {
  11. float val = eig_data[x];
  12. if( val != && val == tmp_data[x] && (!mask_data || mask_data[x]) )
  13. tmpCorners.push_back(eig_data + x);
  14. }
  15. }

  然后将tmpCorners中指针指向的值进行降序排列,(其实是只改变指针指向的位置):

  1. std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr() ); 

  接下来是不太好理解的地方,首先进行minDistance的判断。如果minDistance<1,也就是不进行minDistance的条件限制,直接保存前maxCorners个角点并返回;如果minDistance>=1,则执行if{}中的代码段。

  首先弄清楚grid这个是什么东西,grid容器中元素个数是图像像素点个数的1/(cell_size*cell_size)倍,但是它是一个vector<vector<Point2f>>,也就是指grid中的每一个元素是一个vetcor<Point2f>(意思就是一个元素对应n个2维坐标点)。这个理解起来就是grid中相差一个像素,相当于原图像中相差minDistance个像素。原图像中以当前像素为中心的minDistance范围内的点在grid中其实视为一个像素的3x3邻域(一对多的关系)。

  接下来的分析见注释:

  1. if (minDistance >= )
  2. {
  3. // Partition the image into larger grids
  4. int w = image.cols;
  5. int h = image.rows;
  6.  
  7. const int cell_size = cvRound(minDistance);
        
         //grid中元素(vector)个数为grid_width*grid_height个,+cell_size-1的目的是保证能够覆盖所有的像素点
  8. const int grid_width = (w + cell_size - ) / cell_size;
  9. const int grid_height = (h + cell_size - ) / cell_size;
  10.  
  11. std::vector<std::vector<Point2f> > grid(grid_width*grid_height);
  12.  
  13. minDistance *= minDistance;
  14.  
  15. for( i = ; i < total; i++ )
  16. {
  17. int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
  18. int y = (int)(ofs / eig.step);
  19. int x = (int)((ofs - y*eig.step)/sizeof(float));
  20. //假设开始所有的角点是good
  21. bool good = true;
  22. //将原图中角点坐标归一化到mindistance邻域
  23. int x_cell = x / cell_size;
  24. int y_cell = y / cell_size;
  25. //取归一化后点的邻域4个点坐标(不是4邻域,而是4个角),在grid中的坐标
  26. int x1 = x_cell - ;
  27. int y1 = y_cell - ;
  28. int x2 = x_cell + ;
  29. int y2 = y_cell + ;
  30.  
  31. // boundary check 边界判断
  32. x1 = std::max(, x1);
  33. y1 = std::max(, y1);
  34. x2 = std::min(grid_width-, x2);
  35. y2 = std::min(grid_height-, y2);
  36. //遍历邻域4个点, grid中的邻域坐标
  37. for( int yy = y1; yy <= y2; yy++ )
  38. {
  39. for( int xx = x1; xx <= x2; xx++ )
  40. {
    //取出grid中一个元素对应的所有强角点坐标位置
  41. std::vector <Point2f> &m = grid[yy*grid_width + xx];
  42. //如果某元素对应的原图像角点容器中有已经保存的强角点,则需要进行距离判断。否则指定原图像中该角点就是强角点
  43. if( m.size() )
  44. {
    //遍历其对应容器内的其他强角点,并依次判断原图像中当前角点与其邻域内其他强角点之间的欧式距离,如果欧式距离小于minDistance,则将当前角点标志置为good=false(抛弃),并跳出
  45. for(j = ; j < m.size(); j++)
  46. {
  47. float dx = x - m[j].x;
  48. float dy = y - m[j].y;
  49.  
  50. if( dx*dx + dy*dy < minDistance )
  51. {
  52. good = false;
  53. goto break_out;
  54. }
  55. }
  56. }
  57. }
  58. }
  59.  
  60. break_out:
  61.        //如果角点为good,则将该角点保存在当前grid中一个元素对应的容器中,同时保存在输出容器corners中,并累加计数器ncorners。由于已经进行过降序排序,前面保存的都是强角点。
  62. if (good)
  63. {
  64. grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));
  65.  
  66. corners.push_back(Point2f((float)x, (float)y));
  67. ++ncorners;
  68.  
  69. if( maxCorners > && (int)ncorners == maxCorners )
  70. break;
  71. }
  72. }
  73. }
  74. else
  75. {
  76. for( i = ; i < total; i++ )
  77. {
  78. int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
  79. int y = (int)(ofs / eig.step);
  80. int x = (int)((ofs - y*eig.step)/sizeof(float));
  81.  
  82. corners.push_back(Point2f((float)x, (float)y));
  83. ++ncorners;
  84. if( maxCorners > && (int)ncorners == maxCorners )
  85. break;
  86. }
  87. }
  88.  
  89. Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);

  结束!

OpenCV角点检测goodFeaturesToTrack()源代码分析的更多相关文章

  1. opencv: 角点检测源码分析;

    以下6个函数是opencv有关角点检测的函数 ConerHarris, cornoerMinEigenVal,CornorEigenValsAndVecs, preConerDetect, coner ...

  2. OpenCV角点检测源代码分析(Harris和ShiTomasi角点)

    OpenCV中常用的角点检测为Harris角点和ShiTomasi角点. 以OpenCV源代码文件 .\opencv\sources\samples\cpp\tutorial_code\Trackin ...

  3. opencv 角点检测+相机标定+去畸变+重投影误差计算

    https://blog.csdn.net/u010128736/article/details/52875137 https://blog.csdn.net/h532600610/article/d ...

  4. Opencv角点检测

    #include "stdafx.h" #define max_corners 20 int main() { int cornerNum = max_corners; vecto ...

  5. OpenCV探索之路(十五):角点检测

    角点检测是计算机视觉系统中用来获取图像特征的一种方法.我们都常说,这幅图像很有特点,但是一问他到底有哪些特点,或者这幅图有哪些特征可以让你一下子就识别出该物体,你可能就说不出来了.其实说图像的特征,你 ...

  6. opencv学习之路(32)、角点检测

    一.角点检测的相关概念 二.Harris角点检测——cornerHarris() 参考网址: http://www.cnblogs.com/ronny/p/4009425.html #include ...

  7. Shi-Tomasi角点检测

    代码示例: #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #inc ...

  8. OpenCV3入门(十二)角点检测

    1.角点介绍 角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测.图像匹配.视频跟踪.三维建模和目标识别等领域中,也称为特征点检测.在图像中角 ...

  9. OpenCV亚像素角点cornerSubPixel()源代码分析

    上一篇博客中讲到了goodFeatureToTrack()这个API函数能够获取图像中的强角点.但是获取的角点坐标是整数,但是通常情况下,角点的真实位置并不一定在整数像素位置,因此为了获取更为精确的角 ...

随机推荐

  1. CSS学习笔记day1

    1.css的简介  css:层叠样式表 (层叠:一层一层的:样式表:很多的属性和属性值) 使页面显示效果更好 将页面内容和显示样式进行分离,提高了显示功能. 2.css和html的结合方式(4种) 在 ...

  2. XHR

    xhr注入 XHR 注入技术是通过XMLHttpRequest来获取javascript的.但与eval不同的是,该机制是通过创建一个script的DOM元素,然后把XMLHttpRequest的响应 ...

  3. myeclipse编码

    window --->perferences

  4. 如何让你的.vue在sublime text 3 中变成彩色?

    1.打开sublime,按住shift+Ctrl+P,回车 2.输入Vue Syntax Highlight(因为我的已经装过了,所以这里没有显示) 3.选择Vue Syntax Highlight, ...

  5. Python+Selenium安装及环境配置

    一.Python安装 Window系统下,python的安装很简单.访问python.org/download,下载最新版本,安装过程与其他windows软件类似.记得下载后设置path环境变量,然后 ...

  6. 进程间通信之利用CreateFilemapping()

    这两天在复习进程间通信,复习一下记不住,复习一下记不住...就写个小博客献个丑,先来第一个内存映射 代码亲测通过 CreateFileMapping()的最后的一位用来做进程间通信 步骤: 1.Cre ...

  7. AF_INET 和PF_INET区别;AF_LOCAL PF_LOCAL 区别.

    从字面理解: AF_INET = Address Format, Internet = IP Addresses PF_INET = Packet Format, Internet = IP, TCP ...

  8. web-based installer and executable installer in python 3 ,what is the difference between them?

    Welcome to Python! This applies to all programs, not just python: An executable installer has every ...

  9. (2-3)Eureka详解

    基础架构 服务注册中心 服务提供者 服务消费者 服务治理 服务提供者 服务注册.在服务注册时,需要确认一下eureka.client.registerwith-eurek=ture参数是否正确,默认是 ...

  10. Django_xadmin_应用外键搜索功能错误

    问题: 当我在给某一张表加上外键搜索的时候,会出现 TypeError: Related Field got invalid lookup: icontains 问题原因: a 表关联 b表,也就是说 ...