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)中提到的相关文章。

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.计算图像两个方向梯度的乘积。

Mat Ix2, Iy2, Ixy;
Ix2 = Ix.mul(Ix);
Iy2 = Iy.mul(Iy);
Ixy = Ix.mul(Iy);

3.对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);

4.根据公式计算每个像素的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;
}
}

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

//在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) 最终实现

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

#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;
}

其运行结果为:

4. 参考文献

  1. Harris角点
  2. Harris角点算法
  3. 矩阵特征值和椭圆长短轴的关系?-Eathen的回答

harris角点检测的简要总结的更多相关文章

  1. Harris角点检测算法优化

    Harris角点检测算法优化 一.综述 用 Harris 算法进行检测,有三点不足:(1 )该算法不具有尺度不变性:(2 )该算法提取的角点是像素级的:(3 )该算法检测时间不是很令人满意. 基于以上 ...

  2. Harris角点检测

    代码示例一: #include<opencv2/opencv.hpp> using namespace cv; int main(){ Mat src = imread(); imshow ...

  3. Harris 角点检测

    一 .Motivation 对于做图像处理的人来说,Harris角点检测肯定听过,1988年发表的文章"A combined corner and edge detector"描述 ...

  4. Harris角点检测算原理

    主要参考了:http://blog.csdn.net/yudingjun0611/article/details/7991601  Harris角点检测算子 本文将该文拷贝了过来,并做了一些数学方面的 ...

  5. Harris角点检测原理分析

    看到一篇从数学意义上讲解Harris角点检测很透彻的文章,转载自:http://blog.csdn.net/newthinker_wei/article/details/45603583 主要参考了: ...

  6. cv2.cornerHarris()详解 python+OpenCV 中的 Harris 角点检测

    参考文献----------OpenCV-Python-Toturial-中文版.pdf 参考博客----------http://www.bubuko.com/infodetail-2498014. ...

  7. Opencv学习笔记------Harris角点检测

    image算法测试iteratoralgorithmfeatures 原创文章,转载请注明出处:http://blog.csdn.net/crzy_sparrow/article/details/73 ...

  8. OpenCV-Python:Harris角点检测与Shi-Tomasi角点检测

    一.Harris角点检测 原理: 角点特性:向任何方向移动变换都很大. Chris_Harris 和 Mike_Stephens 早在 1988 年的文章<A CombinedCorner an ...

  9. 第十一节、Harris角点检测原理(附源码)

    OpenCV可以检测图像的主要特征,然后提取这些特征.使其成为图像描述符,这类似于人的眼睛和大脑.这些图像特征可作为图像搜索的数据库.此外,人们可以利用这些关键点将图像拼接起来,组成一个更大的图像,比 ...

随机推荐

  1. 使用BeautifulSoup和正则表达式爬取时光网不同地区top100电影并使用Matplotlib对比

    还有一年多就要毕业了,不准备考研的我要着手准备找实习及工作了,所以一直没有更新. 因为Python是自学不久,发现很久不用的话以前学过的很多方法就忘了,今天打算使用简单的BeautifulSoup和一 ...

  2. linux文件权限总结(创建root不可以删除文件、只可追加的日志文件等)

    文件类型 对于文件和目录的访问权力是根据读访问,写访问,和执行访问来定义的. 我们来看一下 ls 命令的输出结果 [root@iZ28dr6w0qvZ test]# ls -l 总用量 72 -rw- ...

  3. 引用provinces.js的三级联动

    第一次写随笔   应该写的不是太好   请多多见谅 我这次是在网上发现了一个三级联动    也是给新人一个福利 这个是你需要新建个 JavaScript 文件  并复制到你新建的文件里面 var pr ...

  4. JavaSE:数据类型之间的转换(附常见面试题)

    数据类型之间的转换 分为以下几种情况: 1)低级到高级的自动类型转换: 2)高级到低级的强制类型转换(会导致溢出或丢失精度): 3)基本类型向类类型转换: 4)基本类型向字符串的转换: 5)类类型向字 ...

  5. css样式的继承性、层叠性 、优先级

    一.css样式的继承性: 作用:给父元素设置一些属性,子元素也可以使用 应用场景: 一般用于设置网页上的一些共性信息,例如网页的文字颜色,字体,文字大小等内容.优化代码,降低工作量 注意点: 1.并不 ...

  6. 解决Android编译时出现aapt.exe finished with non-zero exit value 1(第二篇)

    之前出现该错误,我用的是这个方法: https://www.cnblogs.com/tangZH/p/10691383.html 然而遗憾的是,这次不管用了,无奈,只好另寻他法,其实会出现这个错误就是 ...

  7. 解决WIN7启动DHCP服务报1075错误办法

    昨天电脑装了Microsoft .NET Framework后,今天一开电脑连不上网了,找了半天问题发现电脑的DHCP服务没有开启,然后在服务中开发DHCP提示下面的内容 原文地址:代码汇个人博客 h ...

  8. Cesium3DTileset示例

    3D Tiles是Cesium中很核心的一部分,尤其是用来实现大范围的模型场景数据的加载应用. 三维倾斜模型.人工建模.BIM模型等等,都可以转换成3D Tiles,进而为我们所用. 从Cesium1 ...

  9. npm 使用 taobao 的镜像后,无法 login & publish

    使用 npm adduser,添加用户之后,没有异常消息,然后使用 npm publish 发布,却报错: 401 原来是 npm 使用 taobao 的镜像后,需要指定 --registry htt ...

  10. PHP中$GLOBALS和global的区别

    很多人都认为$GLOBALS['var']和global $var只是写法上不同,其实并不是这样 根据官方的解释是  $GLOBALS['var']是外部全局变量$var的本身, 而global $v ...