目录
1 概念
Harris角点检测算法的主要思想是利用局部窗口内的灰度变化来判断是否存在角点。在每个像素点处,通过计算该点与邻域像素之间的灰度差异,以及随着像素位移的变化而引起的灰度变化,来评估该点是否为角点。通过计算响应函数,可以得到每个像素点的角点强度,进而进行非极大值抑制和阈值处理,得到最终的角点检测结果。
Harris角点检测主要用于检测图像中直线的交点,上图中harris角点检测过程中,由以下3种情况:
1.在像素值光滑的区域移动滑动窗口,不管无论往哪个方向移动,移动窗口下的像素值灰度变化不大。
2.在像素值的边缘区域移动滑动窗口,沿着垂直方向移动滑动窗口,移动窗口下的像素值灰度变化不大,但是如果在水平方向上移动滑动窗口,窗口下的像素值灰度变化剧烈。
3.在两条直线的交点处,无论往哪个方向移动滑动窗口,窗口下的像素值的灰度变化都非常剧烈,因此两直线的交点是harris角点。
2 harris角点检测原理
2.1 原理
“考虑这样一个小窗口,从不同方向移动窗口,观测窗口内像素值的变化,来区分(判断)当前窗口内的点是否是角点。”
令窗口w(x,y)移动[u,v], 定义窗口内像素变化如:
其中,[u,v]是窗口的偏移量,[x,y]是窗口内所对应的像素坐标位置。w(x,y)是窗口的权重系数,一般可以使用高斯二维函数。
根据二阶泰勒公式
将函数展开:
根据上式可以得到:
其中M是计算harris的梯度协方差矩阵,其具体表达式如下:
将M矩阵写为:
计算角点响应函数C:
其中、
是梯度协方差矩阵M的特征值,k为常量,一般取0.04~0.06,此参数仅仅是这个函数的一个系数,它的存在只是调节函数的形状。
λ1和λ2为M矩阵的两个主特征值。主特征值越大,表明函数在该方向上的变化越显著。再次简化问题,只看λ1和λ2,判断窗口内是否为角点。由此可以得出,harris角点的判断方法:
1.当C越大时,表示两个特征值相似或接近,则该点为角点。
2.当C<0时,表示两个特征值相差较大,该点位于边缘处。
3.当C较小时,说明两个特征值都比较小,该点位于平面内。
2.2 Harris角点检测的步骤
1.利用水平、竖直差分算子对图像每个像素进行滤波求得I_x、I_y,进而求得M中四个元素的值。
2.对M中方的四个元素进行高斯平滑滤波,为的是消除一些不需要的孤立点和凸起,得到新的矩阵M。
3.利用M计算对应每个像素的角点响应函数C。
4.在矩阵R中,同时满足R(I,j)大于一定阈值和R(I,j)是某邻域的局部极大值,则被认为是角点。
3 函数详解
OpenCV提供了cv::cornerHarris函数来实现Harris角点检测。该函数的原型如下:
void cv::cornerHarris(
cv::InputArray src,
cv::OutputArray dst,
int blockSize,
int ksize,
double k,
int borderType = cv::BORDER_DEFAULT
);
src:输入图像(单通道、浮点型)
dst:输出角点响应图(与输入图像尺寸相同,单通道、浮点型)
blockSize:角点计算时所考虑的邻域大小
ksize:Sobel导数算子的窗口大小
k:角点响应参数,取值范围为0.04到0.06之间
borderType:边界类型,默认为cv::BORDER_DEFAULT
4 c++代码实现
下面是一个使用OpenCV进行Harris角点检测的示例代码:
#include <opencv2/opencv.hpp>
int main() {
cv::Mat srcImage = cv::imread("home.jpg");
if (srcImage.empty()) {
return -1;
}
cv::Mat grayImage;
cv::cvtColor(srcImage, grayImage, cv::COLOR_BGR2GRAY);
cv::Mat dstImage;
cv::cornerHarris(grayImage, dstImage, 3, 3, 0.04);
cv::Mat dstImageNorm, dstImageScaled;
cv::normalize(dstImage, dstImageNorm, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
cv::convertScaleAbs(dstImageNorm, dstImageScaled);
for (int i = 0; i < dstImage.rows; i++)
{
for (int j = 0; j < dstImage.cols; j++)
{
int r = dstImageScaled.at<uchar>(i, j);
if (r > 100)
{
cv::circle(srcImage, cv::Point(j, i), 5, cv::Scalar(0, 0, 255), 2);
}
}
}
cv::imshow("Harris Corners", srcImage);
cv::waitKey(0);
return 0;
}