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

  函数原型如下:

void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray _mask, int blockSize, int gradientSize,
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值的设定选取不同的方法提取初始角点:

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

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

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

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

// collect list of pointers to features - put them into temporary image
Mat mask = _mask.getMat();
for( int y = ; y < imgsize.height - ; y++ )
{
const float* eig_data = (const float*)eig.ptr(y);
const float* tmp_data = (const float*)tmp.ptr(y);
const uchar* mask_data = mask.data ? mask.ptr(y) : ; for( int x = ; x < imgsize.width - ; x++ )
{
float val = eig_data[x];
if( val != && val == tmp_data[x] && (!mask_data || mask_data[x]) )
tmpCorners.push_back(eig_data + x);
}
}

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

 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邻域(一对多的关系)。

  接下来的分析见注释:

if (minDistance >= )
{
// Partition the image into larger grids
int w = image.cols;
int h = image.rows; const int cell_size = cvRound(minDistance);
    
     //grid中元素(vector)个数为grid_width*grid_height个,+cell_size-1的目的是保证能够覆盖所有的像素点
const int grid_width = (w + cell_size - ) / cell_size;
const int grid_height = (h + cell_size - ) / cell_size; std::vector<std::vector<Point2f> > grid(grid_width*grid_height); minDistance *= minDistance; for( i = ; i < total; i++ )
{
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
//假设开始所有的角点是good
bool good = true;
//将原图中角点坐标归一化到mindistance邻域
int x_cell = x / cell_size;
int y_cell = y / cell_size;
//取归一化后点的邻域4个点坐标(不是4邻域,而是4个角),在grid中的坐标
int x1 = x_cell - ;
int y1 = y_cell - ;
int x2 = x_cell + ;
int y2 = y_cell + ; // boundary check 边界判断
x1 = std::max(, x1);
y1 = std::max(, y1);
x2 = std::min(grid_width-, x2);
y2 = std::min(grid_height-, y2);
//遍历邻域4个点, grid中的邻域坐标
for( int yy = y1; yy <= y2; yy++ )
{
for( int xx = x1; xx <= x2; xx++ )
{
//取出grid中一个元素对应的所有强角点坐标位置
std::vector <Point2f> &m = grid[yy*grid_width + xx];
//如果某元素对应的原图像角点容器中有已经保存的强角点,则需要进行距离判断。否则指定原图像中该角点就是强角点
if( m.size() )
{
//遍历其对应容器内的其他强角点,并依次判断原图像中当前角点与其邻域内其他强角点之间的欧式距离,如果欧式距离小于minDistance,则将当前角点标志置为good=false(抛弃),并跳出
for(j = ; j < m.size(); j++)
{
float dx = x - m[j].x;
float dy = y - m[j].y; if( dx*dx + dy*dy < minDistance )
{
good = false;
goto break_out;
}
}
}
}
} break_out:
       //如果角点为good,则将该角点保存在当前grid中一个元素对应的容器中,同时保存在输出容器corners中,并累加计数器ncorners。由于已经进行过降序排序,前面保存的都是强角点。
if (good)
{
grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y)); corners.push_back(Point2f((float)x, (float)y));
++ncorners; if( maxCorners > && (int)ncorners == maxCorners )
break;
}
}
}
else
{
for( i = ; i < total; i++ )
{
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float)); corners.push_back(Point2f((float)x, (float)y));
++ncorners;
if( maxCorners > && (int)ncorners == maxCorners )
break;
}
} 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. ThinkPHP配置文件的加载

    分类: thinkphp(39) 版权声明:本文为博主原创文章,未经博主允许不得转载. 惯例配置->应用配置->模式配置->调试配置->状态配置->模块配置->扩展 ...

  2. mysql 分组和聚合函数

    mysql 分组和聚合函数 Mysql 聚集函数有5个: 1.COUNT() 记录个数(count(1),count(*)统计表中行数,count(列名)统计列中非null数) 2.MAX() 最大值 ...

  3. mybatis_SQL映射(2)

    文章摘录自:http://blog.csdn.net/y172158950/article/details/17258377 1. sql的重用:定义一个sql片段,可在任何SQL语句中重用该片段. ...

  4. system进程占用80端口

    服务器规划:apache分配80,iis分配其他端口 理论上,只需要把iis 默认站点的80端口改成其他端口就可以了,可是发现改了apache80端口还是用不了, cmd查了下,发现system进程占 ...

  5. P1345 [USACO5.4]奶牛的电信Telecowmunication

    P1345 [USACO5.4]奶牛的电信Telecowmunication 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮 ...

  6. asp.net web api 向客户端返回错误信息

    1使用Http状态码 ASP.NET Web Api框架提供了Http状态码的值,如下图所示. 虽然有这些预定义的状态码,但在实际项目中使用自定状态码结合预定义状态码更有优势. 通过在适当的位置抛出异 ...

  7. android 自定义控件用的定时CountDownTimer

    定时执行在一段时候后停止的倒计时,在倒计时执行过程中会在固定间隔时间得到通知(译者:触发onTick方法), 下面的例子显示在一个文本框中显示一个30s倒计时: new CountdownTimer( ...

  8. Android Studio 下获取debug sha1和md5

    Open Android Studio Open Your Project Click on Gradle (From Right Side Panel, you will see Gradle Ba ...

  9. strlen出错

    1.特别奇怪的错误 $url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid= ...

  10. nagios 数据更新不及时的问题

    配置nagios的时候发现一个问题,就是改变了某个主机或者服务的描述之后,在主页信息总是更新很慢,而且告警信息还是老的信息,重启多次 nagios甚至重启主机都没有解决,其实这些都是由于nagios每 ...