数字图像处理【14】特征检测——Harris角点检测

server/2024/10/19 3:28:25/

在上一篇文章已经介绍了opencv特征检测中的一些必要的概念,介绍了什么是特征,什么是角点,这些角点特征可以做什么。今天来看看对于我们人来说很容易就识别到角点特征,对于计算机来说是如何识别的,具体的步嘴原理是怎样的。

一、计算机中的角点

在众多的检测算法里最经典的角点特征检测就是Harris角点检测,它是一个特别好的方法,由Chris Harris和Mike Stephens于1988年提出。角点是图像中两条边缘的交点,通常具有两个主要特征:高响应和多方向性。也就是将整个图形角点的检测分成三种情况,如下图所示。

  1. 对于第一种情况 ,检测窗口朝任意方向进行移动,在任何方向进行移动之后,窗口内的像素没有任何差异的变化,这说明这是一个平坦的区域,也就是说没有检测到角点特征。
  2. 对于第二种情况,检测窗口在边缘上,沿着边缘的方向进行移动。虽然边缘的像素和周围的像素是不同的,但是检测窗口是沿着边缘移动,中心像素是没有明显的差异变化;但如果检测窗口不是沿着边缘移动,譬如是垂直边缘进行移动,中心像素会剧烈的变化。这可以简单的确定为边缘特征。
  3. 对于第三种情况,检测窗口在角点上,中心像素已经在角点上,那么在这个窗口内无论朝哪个方向移动的时候都会产生剧烈的变化,这个时候就可以简单的确定为角点特征了。

以上介绍的特征情况,对于计算机来说去做判断其实是不容易的,我们需要以数学的方式去总结以上三种特征情况,对应如下三点:

  • 局部小窗口沿各方向移动,窗口内的像素均产生明显变化的点。
  • 图像局部曲率(梯度)突变的点。
  • 对于同一场景,即使视角发生变化,其角点通常具备某些稳定不变性质的特征

有了基本的对角点的数学描述,我们可以进一步把它转化为数学公式描述:

​其中公式的参数解释分别如下:

  • 目标输出代表:统计窗口内整体像素值变化,是一个标量。
  • 窗口向水平方向移动的像素距离(u);窗口向水平方向移动的像素距离(v)
  • 检测窗口的长和宽是x和y的取值范围
  • (𝑥,𝑦) 表示窗口内 (𝑥,𝑦) 处的像素值, (𝑥+𝑢,𝑦+𝑣) 表示原窗口中 (𝑥,𝑦) 处像素在窗口移动后对应位置的像素值
  • 𝑤(𝑥,𝑦) 表示窗口内 (𝑥,𝑦) 处的位置权重

其函数的物理意义简单描述为:计算了窗口移动 (u,v)的像素距离前后,其内每个像素值变化的平方(之所以要平方,是为了使变化度量值非负)并将这些变化值加权求和,权重与窗内位置有关。得到的函数就表示了我们所想观察的“区域信息的变化特征”。

但如果我们此时用以上公式进行角点检测,会发现其中的参数u和v没有明确的规定,也就是窗口移动的方向。如果人为的规定u和v,这样一样,指定方向窗口滑动又可能导致检测出来的角点其实是边缘点;那么我们可以指定若干组的u和v,即不同的窗口滑动方向,对所有的u和v求得E再进行统计计算,完美。

然而事实上Harris角点检测并没有这么做。Harris可能在想,应该如何优化这个原始的检测函数呢,提高精度,降低计算的复杂度呢?

二、Harris角点检测算法的演变

原始的函数在计算上显得有些复杂了,Harris发现在不改变函数的物理意义的情况下可以将函数变得更直观。

原E(u,v)是一个二元函数,对E(u,v)进行一阶的泰勒近似展开得出如下的表达式。

​关于泰勒级数展开,我搞了一篇文章简单记录其知识点。然后我发现网上有一部分教程,是用泰勒二阶展开来分析,但我个人感觉一阶展开在分析的时候更容易入手。近接着上图第一个约等号后,剩余的数学推导如下图:

​所以E(u,v)能量表达式可以简化更新为如下公式,而其中的 Ix 和 Iy 对于图像来说就是水平方向和竖直方向的梯度了。

评价函数E(u,v)可视化

公式虽然是简化了,难道就可以直接用来检测角点了吗?首先我们回到最初E(u,v)能量表达式的物理意义:E的大小表示窗口移动导致的窗口整体内容的变化程度大小。之前在分析角点特征就提到,对角点来说,它是两个或多个边缘的交点,边缘的特征是梯度在某一方向变化近似为0,那么这个交点应该表现为某一区域内有两个或多个方向上的“边缘性变化”的梯度信息。

我们注意到角点区域的像素会有“两个或多个方向”上的“边缘性变化”的梯度信息,刚好和这里的“x和y两个方向上的梯度信息”产生了联系,我们可以先假设角点区域只在x和y两个相互垂直的方向上有较为明显的变化,那么梯度信息就会有如下表现:

​由上图可知,在“假设角点区域只在x和y两个相互垂直的方向上有较为明显的变化”之后,如果一个区域是角点区域,那么其梯度方向就会集中在x和y两个垂直的方向上,对于窗口内绝大多数像素点来说,要么y方向的梯度趋近0,要么x方向的梯度趋近0,两个方向的梯度的乘积也就趋近0了。对所有像素点进行累加,因为绝大多数像素点的“两个方向的梯度的乘积趋近0”,M矩阵经累加操作后,反对角线上的两个值( Ix Ix )也就趋近于0。

但假设归假设,真实图像上的角点不可能两个边缘就是相互垂直的,但其实我们可以做一个仿射变换,因为做角点检测我们也不在乎角点的方向。把两个边缘的变换到【假设的规范坐标系上】,检测完之后再逆变换还原就可以了。所以当角点区域的梯度集中在任意两个垂直的方向上时,我们对M进行相似对角化,总能得到以下结果:

𝜆1和𝜆2分别表示这两个垂直方向上的区域整体梯度信息

这个时候,我们回到那个评价函数E的形状上去,看看M怎样决定了这个形状,我们又一个怎样将函数形状的特点与角点区域特征对应起来。

如上图,若让函数E取一个函数结果值,譬如1,那么会得到一个方程,这个方程表示了E函数图像的水平切片上,根据上图中那个不太严谨的变换,可以看出这是一个椭圆方程。 

上面的式子总结为椭圆的数学公式,𝜆1和𝜆2的大小决定长短轴, 那么这个椭圆有啥用呢?水平切片总结的椭圆公式其实就是E函数的表现,具体如下:

如上图,这里对𝜆1和𝜆2的大小关系进行分类讨论,强调:𝜆1𝜆2的大小反应两个垂直方向的梯度信息集中强度。

  • 如果两个值都大,且相差不大,则“E函数水平切片椭圆”趋近于圆,区域梯度在两个垂直的方向都有较强集中度,所以是角点区域。而对于其R值,则是要大于0且绝对值较大的时候,我们判断为角点区域。
  • 如果其中一个值远大于另一个值,则,则“E函数水平切片椭圆”趋近于线,区域梯度只在一个方向上有较强集中度,所以是边缘区域。而对于其R值,则是要小于0且绝对值较大的时候,我们判断为边缘区域。
  • 如果两个值都很小,则“E函数水平切片椭圆”趋近于点,区域梯度信息在两个方向都较弱,所以是像素值平坦区域。而对于R值,则是要接近于0的小数且绝对值较小的时候,我们判断为平坦区域。

以上就是Harris角点检测,角点区域的量化判定方法的演算过程,之后我们就可以方便的利用算法,对二维图像进行角点检测。

三、OpenCV的Harris角点检测

根据上述的讨论,我们总结出Harris角点算法的基本步骤,我们试着手动实现Harris角点检测:

1、计算窗口中各像素点在x和y方向的梯度;
2、计算两个方向梯度的乘积,即Ix ^ 2 , Iy ^ 2 , IxIy(可以用一些一阶梯度算子求得图像梯度)
3、使用滤波核对窗口中的每一像素进行加权,生成矩阵M和元素A,B,C
4、计算每个像素的Harris响应值R,并对小于某阈值的R置0;
5、由于角点所在区域的一定邻域内都有可能被检测为角点,所以为了防止角点聚集,最后在3×3或5×5的邻域内进行非极大值抑制,局部最大值点即为图像中的角点。

在手写Hairrs角点检测前,看看OpenCV内置的Hairrs角点检测的函数为cornerHairrs(),但是它的输出是一幅浮点值图像,浮点值越高,表明越可能是特征角点,我们需要对图像进行阈值化。

CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize,int ksize, double k,int borderType = BORDER_DEFAULT );
  • src – 输入的单通道8-bit或浮点图像。
  • dst – 存储着Harris角点响应的图像矩阵,大小与输入图像大小相同,是一个浮点型矩阵。
  • blockSize – 邻域大小。
  • ksize – 扩展的微分算子大,也就是求梯度的窗口大小。
  • k – 响应公式中的,参数α(0.04~0.06)。
  • boderType – 边界处理的类型。
void harris_detaction(int, void*)
{Mat det_mat, normal_det_mat;det_mat = Mat::zeros(gray_src.size(), CV_32FC1);int blockSize = 3; // 加权的检测窗口sizeint ksize = 3;  // 求梯度的sizeif (SELF_HARRIS) {my_cornerHarris(gray_src.clone(), det_mat, ksize, 0.04);}else {cornerHarris(gray_src.clone(), det_mat, blockSize, ksize, 0.04);}//阈值化Harris检测结果,0.001为分割阈值threshold(det_mat, normal_det_mat, 0.001, 255, THRESH_BINARY);//将Harris响应值转为整型(uchar)convertScaleAbs(normal_det_mat, normal_det_mat, 1, 0); // show detection.Mat result = src.clone();for (int r = 0; r < result.rows; r++) {for (int l = 0; l < result.cols; l++) {int det_value = normal_det_mat.at<uchar>(r, l);if (det_value > 0) {circle(result, Point(l, r), 1, Scalar(0, 0, 255), 2);}}}imshow("Harris", result);
}

Opencv-Harris检测结果如下图红点位置。

void my_cornerHarris(Mat src, Mat& dst, int ksize, double k)
{Mat Ix, Iy;  //存储图像一阶梯度    Mat M(src.size(), CV_32FC3); //创建3通道矩阵M存储 Ix*Ix , Ix*Iy , Iy*Iy    Mat R(src.size(), CV_32FC1); //创建3通道矩阵R存储Harris响应值//使用Sobel算子提取图像x方向梯度和y方向梯度    Sobel(src, Ix, CV_32FC1, 1, 0, ksize);Sobel(src, Iy, CV_32FC1, 0, 1, ksize);//存储Ix*Ix , Ix*Iy , Iy*Iy    for (int i = 0; i < src.rows; i++) {for (int j = 0; j < src.cols; j++) {M.at<Vec3f>(i, j)[0] = Ix.at<float>(i, j) * Ix.at<float>(i, j);  //Ix*Ix            M.at<Vec3f>(i, j)[1] = Ix.at<float>(i, j) * Iy.at<float>(i, j);  //Ix*Iy            M.at<Vec3f>(i, j)[2] = Iy.at<float>(i, j) * Iy.at<float>(i, j);  //Iy*Iy        }}//高斯滤波对M矩阵进行加权求和    GaussianBlur(M, M, Size(ksize, ksize), 2, 2);//求得Harris响应值    for (int i = 0; i < src.rows; i++) {for (int j = 0; j < src.cols; j++) {float A = M.at<Vec3f>(i, j)[0];  //Ix*Ixfloat C = M.at<Vec3f>(i, j)[1];  //Ix*Iyfloat B = M.at<Vec3f>(i, j)[2];  //Iy*Iy//响应值计算公式:矩阵的行列式的绝对值等于其所有特征值之积的绝对值。R.at<float>(i, j) = (A * B - C * C) - (k * (A + B) * (A + B));}}dst = R.clone();
}void nms(Mat& src)
{int i, j, k, l = 0;//遍历图像   for (i = 2; i < src.rows-2; i++)for (j = 2; j < src.cols-2; j++)//采用5×5窗口,小于中心像素置零for (k = i - 2; k <= i + 2; k++)for (l = j - 2; l <= j + 2; l++)if (src.at<float>(k, l) <= src.at<float>(i, j) && k != i && l != j && src.at<float>(k, l) > 0)src.at<float>(k, l) = 0;
}

这里我也尝试自己实现my_cornerHarris,基本代码如上,输出的是0~255的规范值,所以输出后阈值化检测结果的阈值得换成[0,255]之间的数值,还得经过局部非极大值抑制nms处理,要不然会看到很多重复的点位置。

以上就是Harris角点检测,我知道的。

参考链接:

怎么深入了解 Harris 角点检测? - 知乎 (zhihu.com)

Harris角点详细解释_harris角点中文-CSDN博客


http://www.ppmy.cn/server/103635.html

相关文章

Linux 音媒体小项目练手

1.1 项目背景 该项目旨在开发一个基于 Mplayer 的视频播放器&#xff0c;支持加载指定路径下的音视频文件&#xff0c;并通过命令行界面进行播放控制。播放器支持顺序播放、随机播放、单曲循环等模式&#xff0c;用户可通过简单的按键操作进行视频播放的控制。 1.2 目标 支持…

408专业135|王道和二轮强化课的经验分享

408 进入第二轮复习阶段&#xff0c;主要任务是大量练习大题。 此时&#xff0c;不建议完整地观看强化课程&#xff0c;因为在第一轮复习中&#xff0c;你已经做了大量选择题&#xff0c;积累了丰富的经验&#xff0c;并且熟悉了题目的出题方式。然而&#xff0c;这并不意味着…

【python与java的区别-04(文件流)】

一、文件和目录的操作 1、IO流&#xff08;Stream&#xff09; 通过“流”的形式允许计算机程序使用相同的方式来访问不同的流入/流出源。Stream是从起源&#xff08;source&#xff09;到接收(sink)的有序数据。我们把输入/输出源对比成“水桶”&#xff0c;那么流就是“管道…

迈入退休生活,全职开发ue独立游戏上架steam

决定退休了。算了算睡后收入&#xff0c;也可以达到每月一万一&#xff0c;正好可以养家糊口。 既然退休了&#xff0c;那就做些想做的事情&#xff0c;别人养花养草&#xff0c;而我打算开发独立游戏上架steam。 一&#xff0c;盘点下目前的技术体系。 1&#xff0c;图形学底…

ctfshow之web29~web51

目录 web29 题解&#xff1a; web30 web31 web32&#xff08;32~36&#xff09; web33 web34 web35 web36 web37 web38 web39 web40 web41 web42 &#xff08;42~51&#xff09; web43 web44 web45 web50 web51 web29 前瞻知识&#xff1a; isset() …

大数据量实现滚动分页-vue3+element-plus实现方式

1.背景&#xff1a;分页是处理大数据量的一种常见方式&#xff0c;一般有页码分页、滚动分页的实现方式&#xff0c;表格页面分页非常常见&#xff0c;下面是一个列表或者表格的滚动分页。 2.话不多说&#xff0c;上代码&#xff1a; &#xff08;1&#xff09;解题思路&#x…

ES的介绍和使用

全文搜索引擎 Elastic Search 第一节 引言 当系统数据量上了10亿、100亿条的时候&#xff0c;我们用什么数据库好&#xff1f;如何解决单点故障&#xff1f;如何提升检索速度&#xff1f;如何解决统计分析问题&#xff1f; 传统数据库的应对解决方案 关系型数据库 通过主从备…

什么是生信分析?深入探讨生物信息学的技术、方法与广泛应用

介绍 生物信息学分析&#xff0c;简称生信分析&#xff0c;是一个结合了生物学、计算机科学、信息学和统计学的多学科领域&#xff0c;旨在处理、分析和解释海量的生物数据。随着现代生物技术的发展&#xff0c;尤其是高通量测序&#xff08;Next-Generation Sequencing, NGS&…