opencv-角点检测之Harris角点检测
转自:https://blog.csdn.net/poem_qianmo/article/details/29356187
先看看程序运行截图:
一、引言:关于兴趣点(interest points)
在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints)、特征点(feature points) 被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析。如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。
图像特征类型可以被分为如下三种:
- <1>边缘
- <2>角点 (感兴趣关键点)
- <3>斑点(Blobs)(感兴趣区域)
其中,角点是个很特殊的存在。他们在图像中可以轻易地定位,同时,他们在人造物体场景,比如门、窗、桌等出随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,,所以他们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。
二、角点检测算法的分类
在当前的图像处理领域,角点检测算法可归纳为三类:
- <1>基于灰度图像的角点检测
- <2>基于二值图像的角点检测
- <3>基于轮廓曲线的角点检测
而基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合三类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
三、角点的定义
“如果某一点在任意方向的一个微小变动都会引起灰度很大的变化,那么我们就把它称之为角点”
角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模和目标识别等领域中。也称为特征点检测。
角点通常被定义为两条边的交点,更严格的说,角点的局部邻域应该具有两个不同区域的不同方向的边界。而实际应用中,大多数所谓的角点检测方法检测的是拥有特定特征的图像点,而不仅仅是“角点”。这些特征点在图像中有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、某些梯度特征等。
现有的角点检测算法并不是都十分的健壮。很多方法都要求有大量的训练集和冗余数据来防止或减少错误特征的出现。另外,角点检测方法的一个很重要的评价标准是其对多幅图像中相同或相似特征的检测能力,并且能够应对光照变化、图像旋转等图像变化。
在我们解决问题时,往往希望找到特征点,“特征”顾名思义,指能描述物体本质的东西,还有一种解释就是这个特征微小的变化都会对物体的某一属性产生重大的影响。而角点就是这样的特征。
观察日常生活中的“角落”就会发现,“角落”可以视为所有平面的交汇处,或者说是所有表面的发起处。假设我们要改变一个墙角的位置,那么由它而出发的平面势必都要有很大的变化。所以,这就引出了图像角点的定义。
我们知道,特征检测与匹配是计算机视觉应用中非常重要的一部分,这需要寻找图像之间的特征建立对应关系。图像中的点作为图像的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。
另外,关于角点的具体描述可以有几种:
- 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
- 两条及两条以上边缘的交点;
- 图像中梯度值和梯度方向的变化速率都很高的点;
- 角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
四、cornerHarris函数详解
cornerHarris 函数用于在OpenCV中运行Harris角点检测算子处理图像。和cornerMinEigenVal( )以及cornerEigenValsAndVecs( )函数类似,cornerHarris 函数对于每一个像素(x,y)在blockSize × blockSize 邻域内,计算2x2梯度的协方差矩阵M(x,y),接着它计算如下式子:
即可以找出输出图中的局部最大值,即找出了角点。
其函数原型和参数解析:
C++: void cornerHarris(InputArray src,OutputArray dst, int blockSize, int ksize, double k, intborderType=BORDER_DEFAULT )
- 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
- 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。
- 第三个参数,int类型的blockSize,表示邻域的大小,更多的详细信息在cornerEigenValsAndVecs()中有讲到。
- 第四个参数,int类型的ksize,表示Sobel()算子的孔径大小。
- 第五个参数,double类型的k,Harris参数。
- 第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。
接着我们一起过一遍稍后需要用到的Threshold函数的解析,然后看一个以cornerHarris为核心的示例程序。
五、Threshold函数详解
函数Threshold( ) 对单通道数组应用固定阈值操作。该函数的典型应用是对灰度图像进行阈值操作得到二值图像。(另外,compare( )函数也可以达到此目的) 或者是去掉噪声,例如过滤很小或很大象素值的图像点。
C++: double threshold(InputArray src,OutputArray dst, double thresh, double maxval, int type)
- 第一个参数,InputArray类型的src,输入数组,填单通道 , 8或32位浮点类型的Mat即可。
- 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放输出结果,且和第一个参数中的Mat变量有一样的尺寸和类型。
- 第三个参数,double类型的thresh,阈值的具体值。
- 第四个参数,double类型的maxval,当第五个参数阈值类型type取 CV_THRESH_BINARY 或CV_THRESH_BINARY_INV 阈值类型时的最大值.
- 第五个参数,int类型的type,阈值类型,。threshold( )函数支持的对图像取阈值的方法由其确定,具体用法如下图:
而图形化的阈值描述如下图:
讲解完这两个函数,让我们看一个调用示例程序:
//-----------------------------------【头文件包含部分】--------------------------------------- // 描述:包含程序所依赖的头文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include <opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空间声明部分】--------------------------------------- // 描述:包含程序所使用的命名空间 //----------------------------------------------------------------------------------------------- using namespace cv; int main() { //以灰度模式载入图像并显示 Mat srcImage = imread("1.jpg", ); imshow("原始图", srcImage); //进行Harris角点检测找出角点 Mat cornerStrength; cornerHarris(srcImage, cornerStrength, , , 0.01); //对灰度图进行阈值操作,得到二值图并显示 Mat harrisCorner; threshold(cornerStrength, harrisCorner, 0.00001, , THRESH_BINARY); imshow("角点检测后的二值效果图", harrisCorner); waitKey(); return ; }运行截图:
六、本文相关核心函数在OpenCV中的实现源代码
这个部分贴出OpenCV中本文相关函数的源码实现细节,来给想了解实现细节的小伙伴们参考。浅墨暂时不在源码的细节上挖深作详细注释。
6.1 OpenCV2.X中cornerHarris函数源代码
源码路径: …opencv\sources\modules\imgproc\src\corner.cpp
void cv::cornerHarris( InputArray _src,OutputArray _dst, int blockSize, int ksize, double k, int borderType ) { Mat src = _src.getMat(); _dst.create( src.size(), CV_32F ); Mat dst = _dst.getMat(); cornerEigenValsVecs( src, dst, blockSize, ksize, HARRIS, k, borderType); }可见cornerHarris内部其实是调用了cornerEigenValsVecs函数,我们看看其实现源码:
static void cornerEigenValsVecs( const Mat& src,Mat& eigenv, int block_size, int aperture_size, intop_type, double k=., intborderType=BORDER_DEFAULT ) { #ifdef HAVE_TEGRA_OPTIMIZATION if (tegra::cornerEigenValsVecs(src, eigenv, block_size, aperture_size,op_type, k, borderType)) return; #endif int depth = src.depth(); double scale = (double)( << ((aperture_size > ?aperture_size : ) - )) * block_size; if( aperture_size < ) scale *= .; if( depth == CV_8U ) scale *= .; scale = ./scale; CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 ); Mat Dx, Dy; if( aperture_size > ) { Sobel( src, Dx, CV_32F, , , aperture_size, scale, , borderType ); Sobel( src, Dy, CV_32F, , , aperture_size, scale, , borderType ); } else { Scharr( src, Dx, CV_32F, , , scale, , borderType ); Scharr( src, Dy, CV_32F, , , scale, , borderType ); } Size size = src.size(); Mat cov( size, CV_32FC3 ); int i, j; for( i = ; i < size.height; i++ ) { float* cov_data = (float*)(cov.data + i*cov.step); const float* dxdata = (const float*)(Dx.data + i*Dx.step); const float* dydata = (const float*)(Dy.data + i*Dy.step); for( j = ; j < size.width; j++ ) { float dx = dxdata[j]; float dy = dydata[j]; cov_data[j*] = dx*dx; cov_data[j*+] = dx*dy; cov_data[j*+] = dy*dy; } } boxFilter(cov, cov, cov.depth(), Size(block_size, block_size), Point(-,-), false, borderType ); if( op_type == MINEIGENVAL ) calcMinEigenVal( cov, eigenv ); else if( op_type == HARRIS ) calcHarris( cov, eigenv, k ); else if( op_type == EIGENVALSVECS ) calcEigenValsVecs( cov, eigenv ); } }6.1 OpenCV2.X中Threshold函数源代码
路径:…opencv\sources\modules\imgproc\src\thresh.cpp
double cv::threshold( InputArray _src,OutputArray _dst, double thresh, double maxval, int type ) { Mat src = _src.getMat(); bool use_otsu = (type & THRESH_OTSU) != ; type &= THRESH_MASK; if( use_otsu ) { CV_Assert( src.type() == CV_8UC1 ); thresh = getThreshVal_Otsu_8u(src); } _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); if( src.depth() == CV_8U ) { int ithresh = cvFloor(thresh); thresh = ithresh; int imaxval = cvRound(maxval); if( type == THRESH_TRUNC ) imaxval = ithresh; imaxval = saturate_cast<uchar>(imaxval); if( ithresh < || ithresh >= ) { if( type == THRESH_BINARY || type == THRESH_BINARY_INV || ((type == THRESH_TRUNC || type== THRESH_TOZERO_INV) && ithresh < ) || (type == THRESH_TOZERO&& ithresh >= ) ) { int v = type ==THRESH_BINARY ? (ithresh >= ? : imaxval) : type ==THRESH_BINARY_INV ? (ithresh >= ? imaxval : ) : /*type == THRESH_TRUNC? imaxval :*/ ; dst.setTo(v); } else src.copyTo(dst); return thresh; } thresh = ithresh; maxval = imaxval; } else if( src.depth() == CV_16S ) { int ithresh = cvFloor(thresh); thresh = ithresh; int imaxval = cvRound(maxval); if( type == THRESH_TRUNC ) imaxval = ithresh; imaxval = saturate_cast<short>(imaxval); if( ithresh < SHRT_MIN || ithresh >= SHRT_MAX ) { if( type == THRESH_BINARY || type == THRESH_BINARY_INV || ((type == THRESH_TRUNC || type== THRESH_TOZERO_INV) && ithresh < SHRT_MIN) || (type == THRESH_TOZERO&& ithresh >= SHRT_MAX) ) { int v = type == THRESH_BINARY ?(ithresh >= SHRT_MAX ? : imaxval) : type == THRESH_BINARY_INV ?(ithresh >= SHRT_MAX ? imaxval : ) : /*type == THRESH_TRUNC ?imaxval :*/ ; dst.setTo(v); } else src.copyTo(dst); return thresh; } thresh = ithresh; maxval = imaxval; } else if( src.depth() == CV_32F ) ; else CV_Error( CV_StsUnsupportedFormat, "" ); parallel_for_(Range(, dst.rows), ThresholdRunner(src, dst,thresh, maxval, type), dst.total()/(double)(<<)); return thresh; }另外在贴上与之相关的自适应阈值操作函数的源码adaptiveThreshold:
void cv::adaptiveThreshold( InputArray_src, OutputArray _dst, double maxValue, int method, inttype, int blockSize, double delta ) { Mat src = _src.getMat(); CV_Assert( src.type() == CV_8UC1 ); CV_Assert( blockSize % == && blockSize > ); Size size = src.size(); _dst.create( size, src.type() ); Mat dst = _dst.getMat(); if( maxValue < ) { dst = Scalar(); return; } Mat mean; if( src.data != dst.data ) mean = dst; if( method == ADAPTIVE_THRESH_MEAN_C ) boxFilter( src, mean, src.type(), Size(blockSize, blockSize), Point(-,-), true,BORDER_REPLICATE ); else if( method == ADAPTIVE_THRESH_GAUSSIAN_C ) GaussianBlur( src, mean, Size(blockSize, blockSize), , ,BORDER_REPLICATE ); else CV_Error( CV_StsBadFlag, "Unknown/unsupported adaptive thresholdmethod" ); int i, j; uchar imaxval = saturate_cast<uchar>(maxValue); int idelta = type == THRESH_BINARY ? cvCeil(delta) : cvFloor(delta); uchar tab[]; if( type == CV_THRESH_BINARY ) for( i = ; i < ; i++ ) tab[i] = (uchar)(i - > -idelta ? imaxval : ); else if( type == CV_THRESH_BINARY_INV ) for( i = ; i < ; i++ ) tab[i] = (uchar)(i - <= -idelta ? imaxval : ); else CV_Error( CV_StsBadFlag, "Unknown/unsupported threshold type"); if( src.isContinuous() && mean.isContinuous() &&dst.isContinuous() ) { size.width *= size.height; size.height = ; } for( i = ; i < size.height; i++ ) { const uchar* sdata = src.data + src.step*i; const uchar* mdata = mean.data + mean.step*i; uchar* ddata = dst.data + dst.step*i; for( j = ; j < size.width; j++ ) ddata[j] = tab[sdata[j] - mdata[j] + ]; } }
opencv-角点检测之Harris角点检测的更多相关文章
- 【OpenCV】角点检测:Harris角点及Shi-Tomasi角点检测
角点 特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系.点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(k ...
- OpenCV计算机视觉学习(13)——图像特征点检测(Harris角点检测,sift算法)
如果需要处理的原图及代码,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice 前言 ...
- OpenCV笔记(6)(harris角点检测、背景建模)
一.Harris角点 如上图所示,红色框AB都是平面,蓝色框CD都是边缘,而绿色框EF就是角点. 平面:框往X或Y抽移动,变化都很小. 边缘:框沿X或Y轴移动,其中一个变化很小,而另外一个变化比较大. ...
- 角点检测:Harris角点及Shi-Tomasi角点检测
角点 特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系.点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(k ...
- OpenCV教程(45) harris角的检测(3)
在前面一篇教程中,我们通过取局部最大值的方法来处理检测结果,但是从图像中可以看到harris角的分布并不均匀,在纹理颜色比较深的地方检测的harris角结果更密集一些.本章中,我们使用一个 ...
- Harris角点检测算原理
主要参考了:http://blog.csdn.net/yudingjun0611/article/details/7991601 Harris角点检测算子 本文将该文拷贝了过来,并做了一些数学方面的 ...
- Harris角点检测原理分析
看到一篇从数学意义上讲解Harris角点检测很透彻的文章,转载自:http://blog.csdn.net/newthinker_wei/article/details/45603583 主要参考了: ...
- 第十一节、Harris角点检测原理(附源码)
OpenCV可以检测图像的主要特征,然后提取这些特征.使其成为图像描述符,这类似于人的眼睛和大脑.这些图像特征可作为图像搜索的数据库.此外,人们可以利用这些关键点将图像拼接起来,组成一个更大的图像,比 ...
- Harris角点检测原理详解
http://blog.csdn.net/lwzkiller/article/details/54633670 关于角点的应用在图像处理上比较广泛,如图像匹配(FPM特征点匹配).相机标定等.网上也有 ...
随机推荐
- Delphi GlobalAlloc、GlobalLock、GlobalUnlock、GlobalFree 函数
GlobalAlloc 函数 分配一块内存,该函数会返回分配的内存句柄. GlobalLock 函数 锁定内存块,该函数接受一个内存句柄作为参数,然后返回一个指向被锁定的内存块的指针. 您可以用该指针 ...
- HTML5中的Canvas和SVG
Canvas 和 SVG 都允许我们在浏览器中创建图形,但是它们在根本上是不同的. 1 SVG SVG 是一种使用 XML 描述 2D 图形的语言. SVG 基于 XML,这意味着 SVG DOM 中 ...
- jQuery, js 验证两次输了密码的一相同
<div class="form-group"> <label class="col-sm-2 control-label font"> ...
- DOM学习总结(六)DOM导航
什么是 HTML DOM 导航? DOM是一个以节点关系组成的结构,所以我们可以使用节点之间的关联找到整个HTML页面中的元素 1.HTML DOM 节点列表: getElementsByTagNam ...
- docker哪些平台技术(3)
容器平台技术 容器核心技术使得容器能够在单个 host 上运行.而容器平台技术能够让容器作为集群在分布式环境中运行. 容器平台技术包括容器编排引擎.容器管理平台和基于容器的 PaaS. 容器编排引擎 ...
- QT--QSocketNotifier类介绍
QSocketNotifier 用来监听系统文件操作,将操作转换为Qt事件进入系统的消息循环队列.并调用预先设置的事件接受函数,处理事件. 一共存在3类事件:read,write,exceptio ...
- 网络错误修复工具:Network Fault Repair Tool Build20160414
::请勿轻易修改此文件,以避免不可预知的错误 gwsbhqt@163.com @echo off color 0A setlocal enabledelayedexpansion title Netw ...
- 动态队列实现-----C语言
/***************************************************** Author:Simon_Kly Version:0.1 Date: 20170520 D ...
- Openstack组件部署 — Nova overview
目录 目录 前文列表 前言 Compute service overview Nova 的组件 nova-api service nova-api-metadata service nova-comp ...
- Docker、Kubernetes(k8s)与OpenShift之间的关系
openshift是基于容器计数搭建的一个云平台.这里的容器技术即包括Docker和Kunbernetes.如下图所示,OPenshift底层以Docker作为容器引擎驱动,以Kubernetes作为 ...