《OpenCV角点检测》

角点检测原理

角点检测原理使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。

图像特征类型分类

  • 边缘
  • 角点(感兴趣关键点)
  • 斑点(Blos)(感兴趣区域)

关于角点的具体描述

  • 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
  • 两条及两条以上边缘的交点;
  • 图像中梯度值和梯度方向的变化速率都很高的点;
  • 角点处的一阶导数最大,二阶导数为零,它指示了物体边缘变化不连续的方向。

Harris角点检测

Harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。

当一个窗口在图像上移动,在平滑区域如图(a),窗口在各个方向上没有变化。在边缘上如图(b),窗口在边缘的方向上没有变化。在角点处如图(c),窗口在各个方向上具有变化。Harris角点检测正是利用了这个直观的物理现象,通过窗口在各个方向上的变化程度,决定是否为角点。


由于角点代表了图像像素梯度变化,我们将寻找这个”变化”。考虑到一个灰度图像 I. 划动窗口 w(x,y) (with displacements u 在x方向和 v 方向) I 计算像素灰度变化。

E(u,v) = \sum _{x,y} w(x,y)[ I(x+u,y+v) - I(x,y)]^{2}

其中:

w(x,y) is the window at position (x,y)//w(x,y)是位于点(x,y)处的窗口

I(x,y) is the intensity at (x,y)//I(x,y)位于点(x,y)处的窗口的灰度值

I(x+u,y+v) is the intensity at the moved window (x+u,y+v)//I(x+u,y+v)位于点 (x+u,y+v)处的移动窗口的灰度值

为了寻找带角点的窗口,我们搜索像素灰度变化较大的窗口。于是, 我们期望最大化以下式子:

\sum _{x,y}[ I(x+u,y+v) - I(x,y)]^{2}

使用 泰勒(Taylor)展开式:

E(u,v) \approx \sum _{x,y}[ I(x,y) + u I_{x} + vI_{y} - I(x,y)]^{2}

式子可以展开为:

E(u,v) \approx \sum _{x,y} u^{2}I_{x}^{2} + 2uvI_{x}I_{y} + v^{2}I_{y}^{2}

一个举证表达式可以写为:

E(u,v) \approx \begin{bmatrix}                u & v               \end{bmatrix}               \left (               \displaystyle \sum_{x,y}               w(x,y)               \begin{bmatrix}                I_x^{2} & I_{x}I_{y} \\                I_xI_{y} & I_{y}^{2}               \end{bmatrix}               \right )               \begin{bmatrix}                u \\                v               \end{bmatrix}

表示为:

M = \displaystyle \sum_{x,y}                      w(x,y)                      \begin{bmatrix}                        I_x^{2} & I_{x}I_{y} \\                        I_xI_{y} & I_{y}^{2}                       \end{bmatrix}

因此我们有等式:

E(u,v) \approx \begin{bmatrix}                u & v               \end{bmatrix}               M               \begin{bmatrix}                u \\                v               \end{bmatrix}

每个窗口中计算得到一个值。这个值决定了这个窗口中是否包含了角点:

R = det(M) - k(trace(M))^{2}

其中:

det(M) = \lambda_{1}\lambda_{2}

trace(M) = \lambda_{1}+\lambda_{2}

一个窗口,它的分数R大于一个特定值,这个窗口就可以被认为是”角点”。

代码

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

/// Global variables
Mat src, src_gray;
int thresh = 200;
int max_thresh = 255;

char* source_window = "Source image";
char* corners_window = "Corners detected";

/// Function header
void cornerHarris_demo( int, void* );

/** @function main */
int main()
{
	/// Load source image and convert it to gray
	src = imread("F:\\磊神图片\\上箭头.png", 1 );
	cvtColor( src, src_gray, CV_BGR2GRAY );

	/// Create a window and a trackbar
	namedWindow( source_window, CV_WINDOW_AUTOSIZE );
	createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
	imshow( source_window, src );

	cornerHarris_demo( 0, 0 );

	waitKey(0);
	return(0);
}

/** @function cornerHarris_demo */
void cornerHarris_demo( int, void* )
{

	Mat dst, dst_norm, dst_norm_scaled;
	dst = Mat::zeros( src.size(), CV_32FC1 );

	/// Detector parameters
	int blockSize = 3;
	int apertureSize = 3;
	double k = 0.04;

	/// Detecting corners
	cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );

	/// Normalizing
	normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
	convertScaleAbs( dst_norm, dst_norm_scaled );

	/// Drawing a circle around corners
	for( int j = 0; j < dst_norm.rows ; j++ )
	{ 
		for( int i = 0; i < dst_norm.cols; i++ )
		{
			if( (int) dst_norm.at<float>(j,i) > thresh )
			{
				circle( dst_norm_scaled, Point( i, j ), 5,  Scalar(0), 2, 8, 0 );
			}
		}
	}
	/// Showing the result
	namedWindow( corners_window, CV_WINDOW_AUTOSIZE );
	imshow( corners_window, dst_norm_scaled );
}

分析

先将RGB图片进行灰度化处理

cvtColor( src, src_gray, CV_BGR2GRAY );

使用cornerHarris()函数检测角点

cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );

函数原型:

void cornerHarris( InputArray src, OutputArray dst, int blockSize,
                                int ksize, double k,
                                int borderType=BORDER_DEFAULT );

其中:

  • src:输入图像
  • dst:输出图像
  • blockSize:邻域的大小,比如:3x3
  • ksize:Sobel()算子的孔径大小
  • k:Harris参数,一般取值为0.04~0.06
  • borderType:图像像素的边界模式,默认为BORDER_DEFAULT

将图片转化成为8位图形进行显示

convertScaleAbs( dst_norm, dst_norm_scaled );

函数原型:

void convertScaleAbs(InputArray src, OutputArray dst,
                                  double alpha=1, double beta=0);

效果图

原图


角点图


参考:

https://blog.csdn.net/lwzkiller/article/details/54633670

www.opencv.org.cn

https://blog.csdn.net/xiaowei_cqu/article/details/7805206

猜你喜欢

转载自blog.csdn.net/mars_xiaolei/article/details/80512338