角点检测----Harris

引例
当我们在大街上突然遇到一位多年不见的好友,甚至不需要看到她的正脸,仅通过背影或者走路就可以认出是。在这个过程中,把好友看做一个集合体,这个集合中有许多特征点,比如大眼睛、长发、身材很好等,正是通过这些特征点来将这个人记在脑子里,最后在人群中突然出现这些特征点的某几个,就会和存在大脑里特征点集合匹配重合,确认就是她。
如上例在图像处理中,也要对图像进行特征提取,一个图像的特征有很多,比如颜色、纹理、形状等。这里我们介绍一种特征类型,角点。它是两个边的交点,它代表这两个边的方向改变的点。
在这里插入图片描述
看上图赫本的照片,我在其中截取了六块,现在如果将这个六块拼接回到原图中,E和F很容易就能认出是眼睛和嘴巴直接拼接会原位,D也可以看出是头发和背景的交界处也很容易找到原位置,但ABC特征不够明确,有很多可能的位置。现在将赫本看做为一个矩形,一遍更易理解。
在这里插入图片描述
如上图,有三个颜色的矩形窗口在蓝色大矩形上滑过,现在让这三个窗口都沿着横平竖直两个方向移动一定的距离,白色窗口移动时在两个方向上像素值都不会改变,黑色窗口在水平方向移动不会有像素变化竖直方向会有变化,红色窗口在两个方向像素值都会有变化。红色窗口中就是一个角,显然他比黑色窗口里的边缘更易定位。
Harris 角点检测器(Harris Corner Detection)
这里介绍一种角点检测算法, Harris 角点检测器(Harris Corner Detection),角点是两条边缘的交点,它表示两条边方向改变的地方,所以角点在任意一个方向上做微小移动,都会引起该区域的梯度图的方向和幅值发生很大变化。也就是一阶导数(即灰度图的梯度)中的局部最大所对应的像素点就是角点,使一个固定尺寸的窗口在图像上某个位置以任意方向做微小滑动,如果窗口内的灰度值(在梯度图上)都有较大的变化,那么这个窗口所在区域就存在角点。
1、让一个窗口的中心位置放置在灰度图像的 ( x , y ) (x,y) (x,y)位置,这个位置的灰度值为 I ( x , y ) I(x,y) I(x,y),窗口沿着 x x x y y y方向分别移动一个很小的位移 u u u v v v到达一个新位置 ( 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):
E ( u , v ) = ∑ ( x , y ) w ( x , y ) × [ I ( x + u , y + v ) − I ( x , y ) ] 2 E(u,v)=\displaystyle\sum_{(x,y)}w(x,y)\times[I(x+u,y+v)-I(x,y)]^2 E(u,v)=(x,y)w(x,y)×[I(x+u,y+v)I(x,y)]2 (1)
经过泰勒公式展开得到式(2).
E ( u , v ) ≈ ∑ ( x , y ) w ( x , y ) × [ I ( x , y ) + u I x + v I y − I ( x , y ) ] 2 E(u,v)\approx\displaystyle\sum_{(x,y)}w(x,y)\times[I(x,y)+uI_x+vI_y-I(x,y)]^2 E(u,v)(x,y)w(x,y)×[I(x,y)+uIx+vIyI(x,y)]2 (2)
化简得到式(3).
E ( u , v ) = ∑ ( x , y ) w ( x , y ) × [ u 2 I x 2 + v 2 I y 2 + 2 u v I x I y ] E(u,v)=\displaystyle\sum_{(x,y)}w(x,y)\times[u^2I_x^2+v^2I_y^2+2uvI_xI_y] E(u,v)=(x,y)w(x,y)×[u2Ix2+v2Iy2+2uvIxIy] (3)
u u u v v v提出得到式(4).
E ( x , y ) = [ u , v ] M [ u v ] E(x,y)=\left[\begin{array}{cccc}u,v\end{array}\right]M\left[\begin{array}{cccc}u\\v\end{array}\right] E(x,y)=[u,v]M[uv] (4)
其中矩阵 M M M为:
M = ∑ ( x , y ) w ( x , y ) [ I x 2 I x I y I x I y I y 2 ] M=\displaystyle\sum_{(x,y)}w(x,y)\left[\begin{array}{cccc}I_x^2&I_xI_y\\I_xI_y&I_y^2\end{array}\right] M=(x,y)w(x,y)[Ix2IxIyIxIyIy2] → \rightarrow R − 1 [ λ 1 0 0 λ 2 ] R R^{-1}\left[\begin{array}{cccc}\lambda_1&0\\0&\lambda_2\end{array}\right]R R1[λ100λ2]R
可以把 R R R看成旋转因子,其不影响两个正交方向的变化分量。经对角化处理后,将两个正交方向的变化分量提取出来,就是 λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2(特征值)。
2、灰度值变化的大小则取决于矩阵 M M M,那么如何找到这些窗口,我们可以使用矩阵的特征值来实现。计算每个窗口对应的得分(角点响应函数 R R R):
R = d e t ( M ) − k ( t r a c e ( M ) ) 2 R=det(M)-k(trace(M))^2 R=det(M)k(trace(M))2
其中 d e t ( M ) = λ 1 λ 2 det(M)=\lambda_1\lambda_2 det(M)=λ1λ2是矩阵的行列式, t r a c e ( M ) = λ 1 + λ 2 trace(M)=\lambda_1+\lambda_2 trace(M)=λ1+λ2是矩阵的轨迹。 λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2是矩阵的特征值, k k k是经验常数,范围在 [ 0.04 , 0.06 ] [0.04,0.06] [0.04,0.06]之间。
3、根据 R R R 的值,将这个窗口所在的区域划分为平面、边缘或角点。为了得到最优的角点,我们还可以使用非极大值抑制。因为特征值 λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2 决定了 R 的值,所以我们可以用特征值来决定一个窗口是平面、边缘还是角点:
*平面: 该窗口在平坦区域上滑动,窗口内的灰度值基本不会发生变化,所以 ∣ R ∣ \left|R\right| R值非常小,在水平和竖直方向的变化量均较小,即 I x I_x Ix I y I_y Iy 都较小,那么 λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2 都较小;
*边缘:[公式] 值为负数,仅在水平或竖直方向有较大的变化量,即 I x I_x Ix I y I_y Iy 只有一个较大,也就是 λ 1 ≫ λ 2 \lambda_1\gg\lambda_2 λ1λ2 λ 1 ≪ λ 2 \lambda_1\ll\lambda_2 λ1λ2
*角点: R R R值很大,在水平、竖直两个方向上变化均较大的点,即 I x I_x Ix I y I_y Iy 都较大,也就是 λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2 都很大。
Harris 角点检测的结果是带有这些分数 R R R的灰度图像,设定一个阈值,分数大于这个阈值的像素就对应角点。
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);
	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;
}

void cornerHarris_demo(int , void*)
{
	Mat dst, dst_norm, dst_norm_scaled;
	dst = Mat::zeros( src_image.size() , CV_32FC1 );
	int blockSize = 2;
	int apertureSize = 3;
	double k = 0.04;
	cornerHarris( src_image_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );    //Harris 角点检测
	normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );					//归一化处理
	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 )
			{
				circle( dst_norm_scaled, Point( i , j ), 5, Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255)), 2, 8, 0 );
				circle( src_image, Point( i , j ), 5, Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255)), -1, 8, 0 );
			}
		}
	}
	imshow( windows_title, src_image );
	imwrite( "Harris.jpg", src_image );
}


将检测到的角点绘制在原图上并保存,最终运行结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

К свободе

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值