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函数能够获取图像中的强角点.但是获取的角点坐标是整数,但是通常情况下,角点的真实位置并不一定在整数像素位置,因此为了获取更为精确的角 ...
随机推荐
- 【WebApi系列】浅谈HTTP
[01]浅谈HTTP在WebApi开发中的运用 [02]聊聊WebApi体系结构 [03]详解WebApi如何传递参数 [04]详解WebApi测试和PostMan [05]浅谈WebApi Core ...
- css cursor属性-显示的光标的类型(形状)的用法和定义
在网页布局的时候,在特定的地方,光标形状各有区别.这个时候,就需要用到css的cursor属性.根据自身需要选择设置鼠标指针样式. 定义和用法 cursor 属性规定要显示的光标的类型(形状). 该属 ...
- php对数组进行分页
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ...
- CCF系列之数列分段(201509-1)
试题名称: 数列分段 试题编号: 201509-1 时间限制: 1.0s 内存限制: 256.0MB 问题描述 给定一个整数数列,数列中连续相同的最长整数序列算成一段,问数列中共有多少段? 输入格式 ...
- JavaScript总结学习一:js中构造函数与普通函数的区别
构造函数不仅只出现在JavaScript中,它同样存在于很多主流的程序语言里,比如c++.Java.PHP等等.与这些主流程序语言一样,构造函数在js中的作业一样,也是用来创建对象时初始化对象,并且总 ...
- 通过Azure 存储账号URL鉴别是标准磁盘还是高性能磁盘
对于不知道虚拟机磁盘是标准磁盘还是高性能磁盘时,我们可以通过nslookup解析存储账号的URL,来判断存储账号的类型,从而得知虚拟磁盘的类型 1.标准存储账号的解析结果,字母"st&quo ...
- C语言学习之选择排序
上一篇文章中讲C语言排序中的比较常见的(交换)冒泡排序,那么这篇文章也将以新手个人的经历来讲同样比较常见而实用的数组排序之选择排序. 选择排序,从字面上看是通过选择来进行排序.其实它的用法就是通过选择 ...
- php中curl模拟post提交多维数组(转载)
原文地址:http://www.cnblogs.com/mingaixin/archive/2012/11/09/2763265.html 今天需要用curl模拟post提交参数,请求同事提供的一个接 ...
- ATS缓存数据结构
ATS缓存数据结构 HttpTunnel类 数据传输驱动器(data transfer driver),包含一个生产者(producer)集合,每个生产者连接到一个或是多个消费者(comsumer). ...
- 浏览器通过file://访问文件和通过http://访问文件有什么区别
1.file协议用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件一样,注意它是针对本地(本机)的,简单来说,file协议是访问你本机的文件资源.http访问本地HTML,是在本地 ...