时间为友,记录点滴。
额,角点检测?
啥是角点?
先上定义:
- 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
- 两条及两条以上边缘的交点;
- 图像中梯度值和梯度方向的变化速率都很高的点;
- 角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
在懵逼之前,可以先看看这个图。
在上面的倒五边形中,从上到下分别取1/2/3三部分,他们分别代表了:
- 边缘
- 团块
- 角点
这么分的依据是什么呢?从肉眼上看,非常容易辨别,当然我们也有过Canny检测这种专门的算法来识别边缘特征。那么怎么从微观或者说让计算机识别角点呢?
如何识别角点?
基本思想是使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。
角点的特性下面的图更清晰:
而且当然不用我等工程狗去根据这种特征来设计算法,跟边缘检测一样,大神们早就做过了,单识别角点的算法,比较有名的就有:
- Harri算法
- Shi-Tomasi算法
- 亚像素级角点检测
等好多种,我们这里先拿Harri开刀尝试剖析一下。
什么是Harri角点检测?
公式推导
我们参考上图,用窗口在一张图片上移动的梯度变化再把三个区域定义梳理一下:
- 平坦区域:窗口移动在所有方向没有明显的梯度变化。
- 边缘区域:窗口移动在某个方向有明显梯度变化。
- 角度边缘:窗口移动在各个方向梯度值都有明显变化。
有了这个差异就好办了,我们设窗口平移
其中:
-
和:窗口偏移量
-
和:窗口内像素坐标
-
:窗口函数,内含权重信息,常用的有权重为1和呈二元高斯正太分布的权重,意在突出像素值变化明显的程度
- 函数
:像素密度函数,类比与像素值
当窗口处在平坦区域上滑动,可以想象的到,灰度不会发生变化,那么E(u,v) = 0;如果窗口处在比纹理比较丰富的区域上滑动,那么灰度变化会很大。算法最终思想就是计算灰度发生较大变化时所对应的位置,当然这个较大是指针任意方向上的滑动,并非单指某个方向。
公式变换
下面的核心就是找到
我们先回忆一下泰勒展开式:
所以,
那么
这里的M是矩阵
参考下图的说明:
矩阵M的关键性
这个时候问题的关键转到了矩阵M上,它是怎么影响
Harris角点检测是通过对窗口内的每个像素的x方向上的梯度与y方向上的梯度进行统计分析。这里以
第一步:对X和Y方向分别求导
第二步:梯度绘制
第三步:圈中数据集
可以认为:
第四步:对
结论:
- 特征值都比较大时,即窗口中含有角点
- 特征值一个较大,一个较小,窗口中含有边缘
- 特征值都比较小,窗口处在平坦区域
OpenCV 引入新公式
OpenCV中会直接引入一个新的公式,它以上面的知识为基础,以经验函数为模型,做了R的函数:
不要问我为什么用这个公式,看图,别说话。。。
其中:
-
:矩阵行列式,
-
:矩阵的迹,
-
:Harri系数,一般取值为0.04~0.06
所以,Harris的基本步骤就是:
- 求两个方向梯度,并计算出矩阵M
- 对矩阵M计算特征值、行列式和迹
- 根据特征值的关系并使用阈值确定图像特征
好了,长舒一口气。看懂都难,真不知道这些人当年是怎么想起来的。不过,老规矩,OpenCv早就给我们做好了API
CV_EXPORTS_W
- dst:Harri算法的输出矩阵(输出图像),CV_32FC1类型,与src有同样的尺寸
- src:输入图像,单通道,8位或浮点型
- blockSize:邻域大小
- ksize:Sobel算子的孔径大小
- k:Harri算法系数,即第二种表达式中的
下面就让我们试试这个算法的效果吧。。。
C++
注意点:
1. cornerHarris转化出来的Dst是CV_32FC1类型,最好经过归一化和ABS处理。
#include
运行结果:
Python:
注意:
1. np.zeros(imgHarris.shape, dtype=np.float32)定义
#!/usr/bin/env python