Harris 角点检测原理
人眼对角点的识别通常是在一个局部区域(小窗口)内完成的。主要有三种情况:
- 小窗口在各个方向上移动,窗口内区域的灰度均发生较大的变化,那么就认为在窗口内遇到了角点。
- 小窗口在各个方向上移动,窗口内区域的灰度没有发生变化,那么就认为窗口内就不存在角点。
- 小窗口在各个方向上移动,仅在某一个方向移动时,窗口内区域的灰度才会发生较大的变化,那么就认为窗口内的图像可能是一条直线。如下图:
Harris 角点检测是一种直接基于灰度图像的角点提取算法。对于每个像素(x,y)在 blockSize×blockSize 邻域内,计算 2×2 梯度的协方差矩阵M(x,y),接着计算如下式子
就可以找出输出图中的局部最大值,即找出了角点。
实现 Harris 角点检测:
1.计算图像 I(x,y) 在 x 和 y 两个方向的梯度 Ix , Iy。
2.计算图像两个方向梯度的乘积。
3.计算中心点为 (x,y) 的窗口 w 对应的协方差矩阵 M。
其中 w(x,y) 是窗口函数,最简单情形就是窗口 w 内的所有像素所对应的权重系数均为1,但有时候,我们会将 w(x,y) 函数设置为以窗口 w 中心为原点的二元高斯函数。
如果窗口 w 中心点是角点时,移动前与移动后,该点在灰度变化贡献最大;而离窗口 w 中心(角点)较远的点,这些点的灰度变化几近平缓,这些点的权重系数,可以设定小值,以示该点对灰度变化贡献较小,那么我们自然而然想到使用二元高斯函数来表示窗口函数。
4.计算每个像素点的 Harris 响应值 R。
- M 的对角线元素之和称为 M 的迹,记为 trace(M) , 即 tr(A) = m11 + m22 + ... + mnn;
- 所有取自不同行不同列的 n 个元素的乘积之和称为 M 的行列式,记为 det(M) 。
5.过滤大于某一阈值 t 的 R 值。
void cornerHarris(InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType = BORDER_DEFAULT);
- src,输入图像,即源图像。填 Mat 类的对象即可,且需为单通道 8 位或浮点型图像。
- dst,这个参数存放 Harris 角点检测的输出结果,和源图像有一样的尺寸和类型。
- blockSize,窗口大小。
- ksize,Sobel 算子的孔径大小,用于计算 x,y 方向的导数。
- k,R 计算公式中的 k 值,经验常数,一般取 k = 0.04~0.06。
- borderType,有默认值,详解见 https://www.cnblogs.com/bjxqmy/p/12306276.html。
代码示例:
#include<opencv.hpp>
#include<iostream>
#include<string>
using namespace std;
using namespace cv;
Mat src, cornerImg;
int thre = 180;
void ChangeThresh(int, void*) {
Mat dst = src.clone();
for (int i = 0; i < cornerImg.rows; i++) {
for (int j = 0; j < cornerImg.cols; j++) {
//注意是 (j,i)
if (cornerImg.at<uchar>(i, j) > thre) {
circle(dst, Point(j, i), 3, Scalar(0, 0, 255), -1);
}
}
}
imshow("dst", dst);
}
int main() {
src = imread("C:/Users/齐明洋/Desktop/示例图片/8.jpg");
imshow("src", src);
//转换为灰度图像
Mat grayImg;
cvtColor(src, grayImg, COLOR_BGR2GRAY);
//计算角点
cornerHarris(grayImg, cornerImg, 2, 3, 0.04);
//归一化到[0,255],详解 https://www.cnblogs.com/bjxqmy/p/12292421.html
normalize(cornerImg, cornerImg, 0, 255, NORM_MINMAX, CV_32FC1);
//因为可能为负值,统一变换成 8 位无符号整型
convertScaleAbs(cornerImg, cornerImg);
imshow("cornerImg", cornerImg);
namedWindow("dst");
createTrackbar("thresh", "dst", &thre, 255, ChangeThresh);
ChangeThresh(0, 0);
waitKey(0);
}
效果演示:
借鉴博客:https://www.cnblogs.com/zyly/p/9508131.html
https://www.cnblogs.com/Jack-Elvis/p/11640931.html
https://baike.baidu.com/item/TR/3776614?fr=aladdin
https://baike.baidu.com/item/n%E9%98%B6%E8%A1%8C%E5%88%97%E5%BC%8F/3705756