上面一篇博客分析了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. 邓_phpcms_phpcms授课思路复习

    思路: 一.目前在企业中使用比较多的cms内容管理有如下几种: 1.dedecms 2.phpcms 二.我们选择学习v9版本的phpcms,主要有以下几点原因: 1.基于MVC模式的内容管理系统 2 ...

  2. ::在c++中什么意思

    (1)表示“域操作符” 例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时, 就要写成void A::f(),表示这个f()函数是类A的成 ...

  3. [知了堂学习笔记]_MVC设计模式与JavaWEB三层架构

    一.MVC设计模式 MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controlle ...

  4. javascript学习日志:前言

    javascript学习日志系列的所有博客,主要理论依据是<javascript权威指南>(犀牛书第6版)以及<javascript高级程序设计第三版>(红色书),目前js行业 ...

  5. ASPNET5 依赖注入(Dependency Injection)

    依赖注入一直是asp.net web框架(Web API,SignalR and MVC)中不可或缺的一部分,但是在以前,这个框架都是各自升级,都有各自的依赖注入实现方式,即使Katana项目想通过O ...

  6. vuejs axios安装配置与使用

    1.安装服务 npm install --save axios vue-axios 2.在main.js import axios from 'axios' import VueAxios from ...

  7. python arvg用法

    转自:http://blog.csdn.net/vivilorne/article/details/3863545 在学python的过程中,一直弄不明白sys.argv[]的意思,虽知道是表示命令行 ...

  8. DS18B20温度传感器知识点总结

    2018-01-1818:20:48 感觉自己最近有点凌乱,一个很简单的问题都能困扰自己很久.以前能很好使用和调试的DS18B20温度传感器,今天愣是搞了很久,妈卖批. 仅仅一个上拉电阻就困扰了我很久 ...

  9. linux_DNS

    linux其配置文件 : /etc/resolv.conf nameserver 223.5.5.5 nameserver 223.6.6.6 # 这两个解析地址为阿里云解析地址,格式也是这样 什么是 ...

  10. 网站静态化处理—web前端优化—上(11)

    网站静态化处理这个系列马上就要结束了,今天我要讲讲本系列最后一个重要的主题web前端优化.在开始谈论本主题之前,我想问大家一个问题,网站静态化处理技术到底是应该归属于web服务端的技术范畴还是应该归属 ...