上面一篇博客分析了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. DEDECMS点击主栏目默认显示第一个子栏目列表的方法

    本文实例讲述了DEDECMS点击主栏目默认显示第一个子栏目列表的方法.分享给大家供大家参考.具体分析如下: 今天公司有个需求是,点击导航上的父栏目进去默认显示第一个子栏目的列表,以下是具体实现方法,可 ...

  2. Uva 1599 Ideal Path - 双向BFS

    题目连接和描述以后再补 这题思路很简单但还真没少折腾,前后修改提交了七八次才AC...(也说明自己有多菜了).. 注意问题: 1.看清楚原题的输入输出要求,刚了书上的中文题目直接开撸,以为输入输出都是 ...

  3. Codeforces 897D. Ithea Plays With Chtholly (交互)

    题目链接:D. Ithea Plays With Chtholly 题意: 给你n张纸,在纸上写字(在 1 - c之间)可以写m次数 (,).(主要是交互,让你判断) 题解: 首先,看到m>=n ...

  4. ROM、SDRAM、RAM、DRAM、SRAM、FLASH区别

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  5. 浅谈Java Virtual Machine

          Java Virtual Machine 就是指Java虚拟器,以下简称VM.关于VM的概念,最早出自CPU模拟器,众所周知的PC上的游戏机模拟器采用的便是和Java VM类似的技术.ja ...

  6. java 包命名规范

    转载 原文地址:http://blog.csdn.net/kongjiea/article/details/45365753 Java的包名都有小写单词组成,类名首字母大写:包的路径符合所开发的 系统 ...

  7. 06_Ajax初步入门第一天

    视频来源:麦子学院 讲师:李毅 ajax:异步JavaScript和XML,局部刷新 原生ajax实例 创建对象 XMLHttpRequest对象 request=new XMLHttpRequest ...

  8. Linux时间转标准时间

    [root@nhserver2 ~]# date -d '1970-1-1 0:0:0 GMT + 1394592071 seconds'Wed Mar 12 10:41:11 CST 2014

  9. windows的MySQL安装

    Window环境下: 1.下载MySQL MySQL社区版:https://dev.mysql.com/downloads/mysql/ MySQL商业版:https://www.mysql.com/ ...

  10. Django(三) ORM操作

    一.DjangoORM 创建基本类型及生成数据库表结构 1.简介 ORM:关系对象映射.定义一个类自动生成数据库的表结构. 数据库常用的数据类型 : 数字 字符串 时间 ORM分为两种类型: 主流都是 ...