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

news2024/11/27 2:31:57

在上一篇文章已经介绍了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; // 加权的检测窗口size
    int ksize = 3;  // 求梯度的size

    if (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*Ix
            float C = M.at<Vec3f>(i, j)[1];  //Ix*Iy
            float 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.coloradmin.cn/o/2060826.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

78.内存对齐

目录 一.什么是内存对齐 二.为什么要内存对齐 三.视频教程 一.什么是内存对齐 有下面例子 #include <stdio.h>struct TEST {char a;int b; };int main(void) {struct TEST test;test.a A;test.b 1;printf("sizeof %ld\n",sizeof(test));return 0;} …

JVM指令重排序

文章目录 什么是指令重排序编译器优化JIT 编译优化处理器优化重排序数据依赖性 硬件层的内存屏障指令重排的代码验证好处减少管道阻塞提高缓存利用率利用并行执行单元性能提升更好地利用硬件资源 问题内存可见性问题编程复杂性增加调试困难 解决方案&#xff1a;Java内存模型&a…

AICon 全球人工智能与机器学习技术大会参会有感

目录 引言 大会背景 大会议程 参会体验 会后感想 结束语 引言 在数字化浪潮席卷全球的今天&#xff0c;人工智能和机器学习技术已成为推动社会进步和产业升级的关键力量。作为一名对AI技术非常感兴趣的开发者&#xff0c;在 8 月 18 日至 19 日这两天&#xff0c;我有幸参…

代码随想录算法训练营43期 | Day 15——110.平衡二叉树、 257. 二叉树的所有路径、404. 左叶子之和、222. 完全二叉树的节点个数

代码随想录算法训练营 代码随想录算法训练营43期 | Day 15110.平衡二叉树257. 二叉树的所有路径404. 左叶子之和222. 完全二叉树的节点个数 代码随想录算法训练营43期 | Day 15 110.平衡二叉树 /*** Definition for a binary tree node.* struct TreeNode {* int val;* …

【读点论文】Adaptive degraded document image binarization,真难复现,流程有点复杂

Adaptive degraded document image binarization Abstract 本文介绍了一种新的自适应方法&#xff0c;用于对退化文档进行二值化和增强。所提出的方法不需要用户进行任何参数调整&#xff0c;并且可以处理由于阴影、不均匀照明、低对比度、大信号相关噪声、污点和应变而发生的…

Spring 的 Aop 支持

Spring 的 Aop 支持 一、AOP基本概念1_AOP基本术语2_通知类型回顾3_AOP基于代理4_SpringBoot整合AOP 二、整体流程剖析1_AspectJAutoProxyRegistrar2_AnnotationAwareAspectJAutoProxyCreator1_继承结构2_初始化时机3_AnnotationAwareAspectJAutoProxyCreator的作用时机 3_收集…

【C++】_string类字符串详细解析(1)

假如没有给你生命&#xff0c;你连失败的机会都没有。你已经得到了最珍贵的&#xff0c;还需要抱怨什么!&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;什么是string&#xff1f; •&#x1f330;1.string类的概念 •&#x1…

嘉立创PCB4层板

视频&#xff1a; 四层板PCB设计保姆级教程&#xff08;1&#xff09;&#xff1a;3.0HUB设计概述_哔哩哔哩_bilibili&#xff08;虽然是四层板实际这个还是两层板&#xff01;&#xff09; 不太建议看这个。 四层PCB 最简单终教学 高校培训课程 深入浅出 不会电路也能学会 设…

Nginx的核心!!! 负载均衡、反向代理

目录 负载均衡 1.轮询 2.最少连接数 3.IP哈希 4.加权轮询 5.最少时间 6.一致性哈希 反向代理 测试 之前讲过Nginx 的简介和正则表达式&#xff0c;那些都是Nginx较为基础的操作&#xff0c;Nginx 最重要的最核心的功能&#xff0c;当属反向代理和负载均衡了。 负载均…

YOLOv8实例分割+双目相机实现物体尺寸测量

1&#xff0c;YOLOv8实例分割原理介绍 YOLOv8是YOLO系列的最新版本&#xff0c;它在目标检测和实例分割方面都进行了显著的改进和创新。以下是YOLOv8实例分割原理的一些关键点&#xff1a; 先进的骨干和颈部架构&#xff1a;YOLOv8采用了先进的骨干和颈部架构来提高特征提取和物…

分治,1875C - Jellyfish and Green Apple

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1875C - Jellyfish and Green Apple 二、解题报告 1、思路分析 n 个苹果…

小阿轩yx-Kubernetes Pod入门

小阿轩yx-Kubernetes Pod入门 前言 Kubernetes 中 一个重要的概念就是 Pod&#xff08;豆荚&#xff09;并不直接管理容器&#xff0c;它的最小管理单元叫做 Pod。 Docker 应用中 把一个应用程序封装在一个镜像中&#xff0c;之后启动这个镜像并映射个宿主机端口号&#x…

03、Redis实战:商户查询缓存、缓存更新策略、缓存穿透、缓存雪崩、缓存击穿

2、商户查询缓存 2.1 什么是缓存? 什么是缓存? 就像自行车,越野车的避震器 举个例子:越野车,山地自行车,都拥有"避震器",防止车体加速后因惯性,在酷似"U"字母的地形上飞跃,硬着陆导致的损害,像个弹簧一样; 同样,实际开发中,系统也需要"避震器&qu…

2、Unity【基础】Mono中的重要内容

Unity基础 MonoBehavior中的重要内容 文章目录 Mono中的重要内容1、延迟函数1、延迟函数概念2、延迟函数使用3、延迟函数受对象失活销毁影响思考1 利用延时函数实现计时器思考2 延时销毁 2、协同程序1、Unity是否支持多线程2、协同程序概念3、协同程序和线程的区别4、协程的使用…

APP架构设计_1.官方应用架构指南

1.官方应用架构指南 1.1架构的原则 应用架构定义了应用的各个部分之间的界限以及每个部分应承担的职责。谷歌建议按照以下原则设计应用架构。 分离关注点通过数据模型驱动界面单一数据源单向数据流 1.2谷歌推荐的应用架构 每个应用应至少有两个层&#xff1a; 界面层 - 在屏…

近视防控明星:蔡司小乐圆中期临床数据详解

近视防控明星&#xff1a;蔡司小乐圆中期临床数据详解 小乐圆镜片作为近视防控镜片里的明星产品&#xff0c;从22年5月上市以来防控效果就一直备受大家的关注。而最近中期临床试验的结果&#xff0c;给家长孩子吃了一颗定心丸。 本次实验中&#xff0c;240位受试者被随机分成三…

基于springboot框架的电影订票系统_wqc3k

TOC springboot611基于springboot框架的电影订票系统_wqc3k--论文 绪 论 1.1研究背景和意义 随着科学技术的不断发展&#xff0c;计算机现在已经成为了社会的必需品&#xff0c;人们通过网络可以获得海量的信息&#xff0c;这些信息可以和各行各业进行关联&#xff0c;电影…

你应该停止使用的 7 个已弃用的 Python 库

欢迎来到雲闪世界。升级您的 Python 工具包&#xff1a;发现 7 个应停止使用的过时库以及替代它们的功能。最近&#xff0c;我回顾了 Python 的新特性&#xff0c;发现每个版本都引入了创新&#xff0c;使我们的日常开发工作变得更加轻松。 这让我意识到科技是一门永无止境的艺…

8.21 QT

1.思维导图 2. 服务器端 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer>//服务器类 #include <QMessageBox> #include <QDebug> #include <QList> #include <QTcpSocket>QT_BEGIN_NAMESPACE names…

免费高画质提取PPT/Word/Excel中的图片工具

下载地址&#xff1a;https://pan.quark.cn/s/134ccc35b8a2 软件简介&#xff1a; 好不容易搞到一个几十上百MB的ppt&#xff0c;想导出里面的图片进行二次加工&#xff0c;却被ppt超低画质的图片另存为功能劝退&#xff0c;明知里面全是高清图片&#xff0c;走时却是两手空空…