opencv —— Harris 角点检测

Harris 角点检测原理

人眼对角点的识别通常是在一个局部区域(小窗口)内完成的。主要有三种情况:

  1. 小窗口在各个方向上移动,窗口内区域的灰度均发生较大的变化,那么就认为在窗口内遇到了角点。
  2. 小窗口在各个方向上移动,窗口内区域的灰度没有发生变化,那么就认为窗口内就不存在角点。
  3. 小窗口在各个方向上移动,仅在某一个方向移动时,窗口内区域的灰度才会发生较大的变化,那么就认为窗口内的图像可能是一条直线。如下图:

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

 

原文地址:https://www.cnblogs.com/bjxqmy/p/12456331.html