前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >harris角点检测的简要总结

harris角点检测的简要总结

作者头像
charlee44
发布2019-08-13 10:50:56
1.6K0
发布2019-08-13 10:50:56
举报
文章被收录于专栏:代码编写世界代码编写世界

目录

1. 概述相关

harris角点检测是一种特征提取的方法,而特征提取正是计算机视觉的一种重要手段。尽管它看起来很复杂,其实也是基于数学原理和简单的图像处理来实现的。

本文之前可以参看笔者写的几篇图像处理的文章,将会有助于更深入了解harris角点检测的实现。

  1. 图像的卷积(滤波)运算(一)——图像梯度
  2. 图像的卷积(滤波)运算(二)——高斯滤波
  3. 图像的膨胀与腐蚀——OpenCV与C++的具体实现

2. 原理详解

1) 算法思想

为了判断图像的角点,可以利用卷积窗口滑动的思想,让以该点为中心的窗口在附近滑动。如下图是所有描述角点文章的初始图例,它表征的正是这一特性:当滑动窗口在所有方向移动时,窗口内的像素灰度出现了较大的变化,就可能是角点。

2) 数学模型

根据上述的算法思想,可以构建数学模型,图像窗口平移u,v产生灰度变化E(u,v)为:

其中w(x,y)是一种加权函数,几乎所有的应用都把它设为高斯函数。由上述公式,进行推导如下:

最后得到的公式(6),在几何意义上表征的是一个椭圆。椭圆的长短轴分别沿着矩阵M的两个特征向量的方向,而两个与之对应的特征值分别是半长轴和半短轴的长度的平方的倒数。

那么根据矩阵M的两个特征值λ1和λ2,可以将图像上的像素点分类成直线、平面与角点:当λ1和λ2 都比较大,且近似相等时,可以认为是角点。如下图所示:

3) 优化推导

而上述表达不太方便使用,又定义了一个角点响应函数R,通过R的大小来判断像素是否为角点:

式中,detM为矩阵M的行列式,traceM为矩阵M的直迹。α为经常常数,取值范围为0.04~0.06。对于R公式,有推导如下:

可以知道,角点响应值R仍然表征了矩阵M两个特征值λ1和λ2,同样可以进行上述分类:当R为大数值正数的时候,表示为角点。如下图所示:

3. 具体实现

在OpenCV中,已经提供了Harris角点检测函数cornerHarris()。为了更好地理解Harris角点提取的原理,这里参考了网上代码,自己实现了其算法,不过也调用了OpenCV中一些基本函数。

根据上述原理,Harris图像角点检测算法的关键是计算M矩阵,M矩阵是图像I(x,y)的偏导数矩阵,也就是要先求出图像的梯度。

1) 详细步骤

1.计算图像I(x,y)在X,Y方向的梯度。在这里是通过卷积函数filter2D实现的,具体原理可以看(1)中提到的相关文章。

代码语言:javascript
复制
Mat gray;
imgSrc.convertTo(gray, CV_64F);

Mat xKernel = (Mat_<double>(1, 3) << -1, 0, 1);
Mat yKernel = xKernel.t();

Mat Ix, Iy;
filter2D(gray, Ix, CV_64F, xKernel);
filter2D(gray, Iy, CV_64F, yKernel);

2.计算图像两个方向梯度的乘积。

代码语言:javascript
复制
Mat Ix2, Iy2, Ixy;
Ix2 = Ix.mul(Ix);
Iy2 = Iy.mul(Iy);
Ixy = Ix.mul(Iy);

3.对Ix2、Iy2和Ixy进行高斯滤波,生成矩阵M的元素A、B和C。

代码语言:javascript
复制
Mat gaussKernel = getGaussianKernel(7, 1);
filter2D(Ix2, Ix2, CV_64F, gaussKernel);
filter2D(Iy2, Iy2, CV_64F, gaussKernel);
filter2D(Ixy, Ixy, CV_64F, gaussKernel);

4.根据公式计算每个像素的Harris响应值R,得到图像对应的响应值矩阵。

代码语言:javascript
复制
Mat cornerStrength(gray.size(), gray.type());
for (int i = 0; i < gray.rows; i++)
{
    for (int j = 0; j < gray.cols; j++)
    {
        double det_m = Ix2.at<double>(i, j) * Iy2.at<double>(i, j) - Ixy.at<double>(i, j) * Ixy.at<double>(i, j);
        double trace_m = Ix2.at<double>(i, j) + Iy2.at<double>(i, j);
        cornerStrength.at<double>(i, j) = det_m - alpha * trace_m *trace_m;
    }
}

5.在3×3的邻域内进行非最大值抑制,找到局部最大值点,即为图像中的角点。在这里非最大值抑制是通过图像膨胀的实现的。比较膨胀前后的响应值矩阵,得到局部最大值。

代码语言:javascript
复制
//在3×3的邻域内进行非最大值抑制,找到局部最大值点,即为图像中的角点
double maxStrength;
minMaxLoc(cornerStrength, NULL, &maxStrength, NULL, NULL);
Mat dilated;
Mat localMax;
dilate(cornerStrength, dilated, Mat());             //膨胀
compare(cornerStrength, dilated, localMax, CMP_EQ);      //比较保留最大值的点

//得到角点的位置
Mat cornerMap;
double qualityLevel = 0.01;
double thresh = qualityLevel * maxStrength;   
cornerMap = cornerStrength > thresh;                //小于阈值t的R置为零。
bitwise_and(cornerMap, localMax, cornerMap);            //位与运算,有0则为0, 全为1则为1

imgDst = cornerMap.clone();

2) 最终实现

合并以上步骤,传入参数,最终的实现代码:

代码语言:javascript
复制
#include <iostream>
#include <algorithm>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

void detectHarrisCorners(const Mat& imgSrc, Mat& imgDst, double alpha)
{
    //
    Mat gray;
    imgSrc.convertTo(gray, CV_64F);

    //计算图像I(x,y)在X,Y方向的梯度
    Mat xKernel = (Mat_<double>(1, 3) << -1, 0, 1);
    Mat yKernel = xKernel.t();

    Mat Ix, Iy;
    filter2D(gray, Ix, CV_64F, xKernel);
    filter2D(gray, Iy, CV_64F, yKernel);

    //计算图像两个方向梯度的乘积。
    Mat Ix2, Iy2, Ixy;
    Ix2 = Ix.mul(Ix);
    Iy2 = Iy.mul(Iy);
    Ixy = Ix.mul(Iy);

    //对Ix2、Iy2和Ixy进行高斯滤波,生成矩阵M的元素A、B和C。
    Mat gaussKernel = getGaussianKernel(7, 1);
    filter2D(Ix2, Ix2, CV_64F, gaussKernel);
    filter2D(Iy2, Iy2, CV_64F, gaussKernel);
    filter2D(Ixy, Ixy, CV_64F, gaussKernel);

    //根据公式计算每个像素的Harris响应值R,得到图像对应的响应值矩阵。
    Mat cornerStrength(gray.size(), gray.type());
    for (int i = 0; i < gray.rows; i++)
    {
        for (int j = 0; j < gray.cols; j++)
        {
            double det_m = Ix2.at<double>(i, j) * Iy2.at<double>(i, j) - Ixy.at<double>(i, j) * Ixy.at<double>(i, j);
            double trace_m = Ix2.at<double>(i, j) + Iy2.at<double>(i, j);
            cornerStrength.at<double>(i, j) = det_m - alpha * trace_m *trace_m;
        }
    }

    //在3×3的邻域内进行非最大值抑制,找到局部最大值点,即为图像中的角点
    double maxStrength;
    minMaxLoc(cornerStrength, NULL, &maxStrength, NULL, NULL);
    Mat dilated;
    Mat localMax;
    dilate(cornerStrength, dilated, Mat());             //膨胀
    compare(cornerStrength, dilated, localMax, CMP_EQ);      //比较保留最大值的点
    
    //得到角点的位置
    Mat cornerMap;
    double qualityLevel = 0.01;
    double thresh = qualityLevel * maxStrength;   
    cornerMap = cornerStrength > thresh;                //小于阈值t的R置为零。
    bitwise_and(cornerMap, localMax, cornerMap);            //位与运算,有0则为0, 全为1则为1

    imgDst = cornerMap.clone();
}

//在角点位置绘制标记
void drawCornerOnImage(Mat& image, const Mat&binary)
{
    Mat_<uchar>::const_iterator it = binary.begin<uchar>();
    Mat_<uchar>::const_iterator itd = binary.end<uchar>();
    for (int i = 0; it != itd; it++, i++)
    {
        if (*it)
            circle(image, Point(i%image.cols, i / image.cols), 3, Scalar(0, 255, 0), 1);
    }
}

int main()
{
    //从文件中读取成灰度图像
    const char* imagename = "D:\\Data\\imgDemo\\whdx.jpg";
    Mat img = imread(imagename, IMREAD_GRAYSCALE);
    if (img.empty())
    {
        fprintf(stderr, "Can not load image %s\n", imagename);
        return -1;
    }

    //
    Mat imgDst;
    double alpha = 0.05;    
    detectHarrisCorners(img, imgDst, alpha);
    
    //在角点位置绘制标记
    drawCornerOnImage(img, imgDst);

    //
    imshow("Harris角点检测", img);
    waitKey();
    
    return 0;
}

其运行结果为:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 概述相关
  • 2. 原理详解
    • 1) 算法思想
      • 2) 数学模型
        • 3) 优化推导
        • 3. 具体实现
          • 1) 详细步骤
            • 2) 最终实现
            相关产品与服务
            图像处理
            图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档