OpenCV角点检测goodFeaturesToTrack()源代码分析
上面一篇博客分析了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()源代码分析的更多相关文章
- opencv: 角点检测源码分析;
以下6个函数是opencv有关角点检测的函数 ConerHarris, cornoerMinEigenVal,CornorEigenValsAndVecs, preConerDetect, coner ...
- OpenCV角点检测源代码分析(Harris和ShiTomasi角点)
OpenCV中常用的角点检测为Harris角点和ShiTomasi角点. 以OpenCV源代码文件 .\opencv\sources\samples\cpp\tutorial_code\Trackin ...
- opencv 角点检测+相机标定+去畸变+重投影误差计算
https://blog.csdn.net/u010128736/article/details/52875137 https://blog.csdn.net/h532600610/article/d ...
- Opencv角点检测
#include "stdafx.h" #define max_corners 20 int main() { int cornerNum = max_corners; vecto ...
- OpenCV探索之路(十五):角点检测
角点检测是计算机视觉系统中用来获取图像特征的一种方法.我们都常说,这幅图像很有特点,但是一问他到底有哪些特点,或者这幅图有哪些特征可以让你一下子就识别出该物体,你可能就说不出来了.其实说图像的特征,你 ...
- opencv学习之路(32)、角点检测
一.角点检测的相关概念 二.Harris角点检测——cornerHarris() 参考网址: http://www.cnblogs.com/ronny/p/4009425.html #include ...
- Shi-Tomasi角点检测
代码示例: #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #inc ...
- OpenCV3入门(十二)角点检测
1.角点介绍 角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测.图像匹配.视频跟踪.三维建模和目标识别等领域中,也称为特征点检测.在图像中角 ...
- OpenCV亚像素角点cornerSubPixel()源代码分析
上一篇博客中讲到了goodFeatureToTrack()这个API函数能够获取图像中的强角点.但是获取的角点坐标是整数,但是通常情况下,角点的真实位置并不一定在整数像素位置,因此为了获取更为精确的角 ...
随机推荐
- Java进阶篇(四)——Java异常处理
程序中总是存在着各种问题,为了使在程序执行过程中能正常运行,使用Java提供的异常处理机制捕获可能发生的异常,对异常进行处理并使程序能正常运行.这就是Java的异常处理. 一.可捕获的异常 Java中 ...
- WEB前端大神之路之基础篇
CSS篇: 1.CSS权重: 不重复造轮子啦,直接传送门(CSS选择器的权重与优先规则) JavaScript篇: 1.this关键字: 它是一种引用(referent).指向的是当前上下文(cont ...
- yum错误,Cannot find a valid baseurl for repo: base 和 No more mirrors to try
可能出错原因: 1. yum 配置错误 2. 虚拟机无法连接外网 3. 域名解析没有 如何解决这个错误? 1. 网上找 /ect/yum.conf 和 /etc/yum.repos.d/CentOS- ...
- 用MapViewOfFile处理大文件-内存不足
用MapViewOfFile处理大文件时,如果文件过大,如400M,则无法一次性映射入内存,否则会出现1132错误,即内存不足.原因可能为操作系统无法找到连续的内存.因此需要通过分页的方式,逐页将文件 ...
- 使用telnet发送HTTP请求
使用telnet发送HTTP请求 写这篇博客,其实没有太大的实际意义,但是还是很有必要的,如果用好Telnet指令,就可以很好的理解HTTP的一些概念,特别是http1.1的持续链接. 要想使用Tel ...
- Git知识总览(四) git分支管理之rebase 以及 cherry-pick相关操作
上篇博客聊了<Git知识总览(三) 分支的创建.删除.切换.合并以及冲突解决>,本篇博客我们主要来看一下 rebase 变基相关的操作.rebase 操作和 merge 操作最终都可以达到 ...
- js控制滚动条滑动
window.scrollTo(0,document.body.scrollHeight);或者通过设置Location的hash属性参见:http://www.cnblogs.com/oospace ...
- 几次面试后才弄懂的HashMap
本人大四,以前也开发过几个项目,Map相关集合也总用.但是从来没有研究过底层的实现,只知道杂用.结果在最开始的几次面试中一脸懵逼.认识到不足后,浅显的学习了一下,总结成一下几点.(如果写错了还望指正) ...
- htmlcss渐变及兼容性
自我总结,欢饮拍砖. <!DOCTYPE HTML> <html lang="en"> <head> <meta content ...
- 命令行登陆mysql提示'mysql' 不是内部或外部命令
问题:命令行登陆mysql提示'mysql' 不是内部或外部命令.如图1所示. 图1 原因:没有将mysql的bin文件夹配置到环境变量里区,因为命令行登陆mysql需要调用bin下的mysql.ex ...