Harris角点检测是一种在计算机视觉中广泛使用的角点检测算法,用于在图像中检测出角点特征。角点通常被定义为两条边的交点,或者说,角点的局部邻域应该具有两个不同区域的不同方向的边界。
Harris角点检测算法的原理是通过计算图像中每个像素点的响应函数来判断其是否为角点。该响应函数基于图像的灰度变化和局部窗口的协方差矩阵,通过计算特征值来确定角点的位置。当特征值较大时,表示该像素点处存在角点。
Harris角点检测算法的优点包括:
对图像旋转、尺度变化和亮度变化具有一定的不变性,即当图像发生这些变化时,Harris角点检测仍然能够准确地检测出角点。
计算简单快速,使得Harris角点检测算法在实际应用中具有较高的效率。
Harris角点检测算法的实现过程大致如下:
对图像进行高斯滤波,以减少图像噪声的影响。
计算图像中每个像素点的梯度值和梯度方向。
构建每个像素点的局部窗口的协方差矩阵。
计算协方差矩阵的特征值,并根据特征值的大小判断该像素点是否为角点。
去除边缘点和低对比度点,保留强角点作为最终的角点检测结果。
我们在大街上突然遇到一位多年不见的好友,甚至不需要看到她的正脸,仅通过背影或者走路就可以认出是。在这个过程中,把好友看做一个集合体,这个集合中有许多特征点,比如大眼睛、长发、身材很好等,正是通过这些特征点来将这个人记在脑子里,最后在人群中突然出现这些特征点的某几个,就会和存在大脑里特征点集合匹配重合,确认就是她。
如上例在图像处理中,也要对图像进行特征提取,一个图像的特征有很多,比如颜色、纹理、形状等。这里我们介绍一种特征类型,角点。它是两个边的交点,它代表这两个边的方向改变的点。
在图像拼接中,尤其是在面对具有显著特征(如眼睛、嘴巴和头发边缘)的图像时,我们通常会依赖这些特征来找到它们在原图中的正确位置。对于您提到的赫本照片中的六块区域(A、B、C、D、E、F),我们可以根据已知的特征来讨论它们的拼接策略。
E(眼睛)和F(嘴巴):
这两个区域具有非常显著的特征,使得它们在原图中的位置相对容易确定。眼睛通常位于脸部的上半部分,并且形状和大小在人脸中是非常独特的。嘴巴则位于脸部中央的下方,具有明确的轮廓。
D(头发和背景的交界处):
这个区域虽然不如眼睛和嘴巴那么显著,但头发与背景的交界处通常具有比较明显的边缘变化,这使得它在原图中的位置也相对容易识别。
A、B、C:
参考已知区域:首先,我们可以尝试将A、B、C与已经确定位置的E、F、D进行相对定位。例如,如果知道眼睛(E)的位置,那么我们可以根据人脸的结构推断出眉毛(可能是A或B)的大致位置。
利用边缘和纹理信息:尽管A、B、C区域的特征可能不够明显,但它们仍然可能包含一些边缘或纹理信息,这些信息可以帮助我们找到它们在原图中的位置。
尝试和验证:如果可能的话,我们可以尝试将A、B、C放置在原图中的不同位置,并观察拼接后的结果。通过反复尝试和验证,我们可以找到最佳的拼接位置。
使用图像拼接算法:对于更复杂的图像拼接任务,我们可以使用专门的图像拼接算法,这些算法通常能够基于图像的特征进行更准确的拼接。
对于这些特征不够明确的区域,我们可以采用以下几种策略:
矩形类比:
如果将赫本的照片看作一个矩形,并且这个矩形包含了所有重要的面部特征,那么我们可以将矩形内的空间进行分区。每个区域对应了赫本脸部的某个部分,如眼睛、鼻子、嘴巴、额头、脸颊等。然后,我们可以将A、B、C这三个特征不明确的区域尝试放置在这些分区中,看哪个位置最为合理。
Harris角点检测器是一种广泛应用于图像处理和计算机视觉中的角点检测方法。该方法的核心思想是通过检测图像中在任意方向上灰度值都有较大变化的点来确定角点的位置。具体来说,Harris角点检测器通过以下步骤实现角点检测:
滤波和平滑:首先,对图像进行滤波和平滑处理,以减少噪声和其他干扰因素的影响。这有助于更准确地检测角点。
计算图像梯度:接下来,计算图像中每个像素的梯度,即灰度值在x和y方向上的变化率。这可以通过对图像应用差分算子或梯度算子来实现。
构建Harris矩阵:在每个像素位置上,构建Harris矩阵M。该矩阵是由该像素及其邻域内的灰度值变化率组成的,反映了该像素点在不同方向上的灰度变化程度。
计算角点响应函数:通过计算Harris矩阵的行列式与迹的差,得到角点响应函数值。这个值表示该像素点处角点的可能性。如果响应函数值大于某个阈值,则认为该像素点是角点。
非极大值抑制:在得到所有像素点的角点响应函数值后,进行非极大值抑制。比较每个像素点与其邻域内其他像素点的响应函数值,只保留局部最大值作为角点。
在例子中,红色窗口内的区域就是一个角点。当这个窗口在图像上沿任意方向做微小滑动时,窗口内的灰度值(在梯度图上)都会有较大的变化。这正是Harris角点检测器所寻找的特征。相比之下,白色窗口在移动时灰度值不会改变,因此不是角点;而黑色窗口只在水平方向移动时灰度值会发生变化,也不满足角点的定义。
Harris角点检测器具有旋转不变性和对图像灰度变化的部分不变性等优点,因此在图像拼接、目标跟踪、运动检测等领域有着广泛的应用。同时,该算法也存在一些缺点,如对尺度变化敏感等。因此,在实际应用中需要根据具体需求进行选择和调整。
要点:
1、让一个窗口的中心位置放置在灰度图像的( x , y ) (x,y)(x,y)位置,这个位置的灰度值为I ( x , y ) I(x,y)I(x,y),窗口沿着x xx和y yy方向分别移动一个很小的位移u uu和v vv到达一个新位置( x + u , y + v ) (x+u,y+v)(x+u,y+v),新位置灰度值为I ( x + u , y + v ) I(x+u,y+v)I(x+u,y+v),窗口移动过程中产生的灰度值变化值为[ I ( x + u , y + v ) − I ( x , y ) ] [I(x+u,y+v)-I(x,y)][I(x+u,y+v)−I(x,y)]。窗口函数为w ( x , y ) w(x,y)w(x,y)得到如下公式(1):
Opencv中Harris检测器的使用
opencv中提供实现Harris角点检测的函数cornerHarris(),使用起来非常方便,相关代码如下:
#include <iostream> #include <string> #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> using namespace cv; using namespace std; // 原始图像和灰度图像 Mat src_image, src_image_gray; // 阈值变量,用于控制角点检测的敏感度 int thresh = 100; // 最大阈值 const int Max_thresh = 255; // 展示窗口的标题 const char* windows_title = "Harris检测器"; // 图像路径 const string PATH = "lena2.jpg"; // 随机数生成器,用于给角点画随机颜色的圆 RNG rng(12345); // 回调函数,用于在滑动条变化时更新角点检测的结果 void cornerHarris_demo(int , void*); int main(int argc, char const *argv[]) { // 读取图像 src_image = imread(PATH); if (src_image.empty()) { cerr << "无法读取图像" << endl; return -1; } // 转换为灰度图像 cvtColor(src_image, src_image_gray, COLOR_BGR2GRAY); // 创建窗口 namedWindow(windows_title, WINDOW_AUTOSIZE); // 创建滑动条,用于调整阈值 createTrackbar("Threshold: ", windows_title, &thresh, Max_thresh, cornerHarris_demo); // 初始调用回调函数,显示角点检测结果 cornerHarris_demo(0, 0); // 等待键盘输入 waitKey(0); return 0; } // 回调函数,实现Harris角点检测并显示结果 void cornerHarris_demo(int , void*) { Mat dst, dst_norm, dst_norm_scaled; // 创建一个与原始图像大小相同的浮点型矩阵,用于存储Harris角点响应值 dst = Mat::zeros(src_image.size(), CV_32FC1); // Harris角点检测的参数 int blockSize = 2; // 邻域大小 int apertureSize = 3; // 用于导数计算的扩展大小 double k = 0.04; // Harris角点检测方程中的自由参数 // Harris角点检测 cornerHarris(src_image_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT); // 归一化处理,将响应值映射到0-255 normalize(dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat()); // 位深转换,将浮点型矩阵转换为8位无符号整型矩阵 convertScaleAbs(dst_norm, dst_norm_scaled); // 遍历每个像素,如果响应值大于阈值,则在原始图像和响应值图像上画圆表示角点 for (int i = 0; i < dst_norm.rows; ++i) { for (int j = 0; j < dst_norm.cols; ++j) { if ((int)dst_norm.at<float>(i, j) > thresh) { Scalar color(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); // 生成随机颜色 circle(dst_norm_scaled, Point(j, i), 5, color, 2, 8, 0); // 在响应值图像上画圆 circle(src_image, Point(j, i), 5, color, -1, 8, 0); // 在原始图像上画圆,-1表示填充 } } } // 显示结果 imshow(windows_title, src_image); // 保存结果到文件 imwrite("Harris.jpg", src_image); }
将检测到的角点绘制在原图上并保存,最终运行结果如下: