OpenCV学习之路(七)——角点检测

版权声明:谢谢你那么厉害还看了我的文章,欢迎转载交流学习~ https://blog.csdn.net/kilotwo/article/details/88953414

角点检测(Corner Detection)是计算机视觉中用来获得图像特征的一种方法,广泛应用于运动检测、视频追踪、目标识别等领域中,也称特征点检测。

一、兴趣点与角点

对于角点,到目前为止还没有明确的数学定义。但是你可以认为角点就是极值点,即在某方面属性特别突出的点。一般的角点检测都是对有具体定义的、或者是能够具体检测出来的兴趣点的检测。这意味着兴趣点可以是角点,是在某些属性上强度最大或者最小的孤立点、线段的终点,或者是曲线上局部曲率最大的点。
通俗的来说,在一副图像中,我们可以认为角点是物体轮廓线的连接点(见图1),当拍摄视角变化的时候,这些特征点仍能很好地保持稳定的属性。
图1
角点通常被定义为两条边的交点,角点的局部领域应该具有两个不同区域的不同方向的边界。实际应用中,大多数角点检测方法检测的是拥有特定特征的图像点,不仅仅是“角点”。这些特征点在图像中具有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、某些梯度特征。角点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。

图像特征类型可以被分为如下三种:

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

其中,角点是个很特殊的存在。如果某一点在任意方向的一个微小变动都会引起灰度很大变化,那么就称为角点。角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,所以它们是可以精确定位的二维特征,甚至可以达到亚精度的精度。
关于角点的具体描述可以有如下几种:

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

二、角点检测

在当前的图像处理领域,角点检测算法可以归纳为以下三类

  • 基于灰度图像的角点检测
  • 基于二值图像的角点检测
  • 基于轮廓曲线的角点检测
    基于灰度图像的角点检测又可分为基于梯度、基于模版和基于模版梯度组合三类方法。基于模版的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将于邻点亮度对比足够大的点定义为角点。常见的基于模版的角点检测算法有Harris角点检测算法,KLT角点检测算法等。

三、Harris角点检测

Harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度很高。但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象。
图2

角点检测最原始的想法就是取某个像素的一个邻域窗口,当这个窗口在各个方向上进行小范围移动时,观察窗口内平均的像素灰度值的变化(即,E(u,v),Window-averaged change of intensity)。从上图可知,我们可以将一幅图像大致分为三个区域(‘flat’,‘edge’,‘corner’),这三个区域变化是不一样的。
A. 窗口图像平坦 ---------------E的变化不大

B.窗口图像是一条边 ------------1.沿边滑动E的变化不大 2.垂直于边滑动,E的变化会很大

C.窗口图像为一个角点 ------------窗口图像沿任何方向移动,E的值变化都会很大
具体原理可参考下面两篇
https://blog.csdn.net/linqianbi/article/details/78930239
https://blog.csdn.net/pbymw8iwm/article/details/82624898

四、OpenCV实现Harris角点检测

OpenCV中cornerHarris函数可用于检测图像的Harris角点。 cornerHarris函数名参数的说明:

void cornerHarris( InputArray src,  //输入8bit 单通道灰度Mat矩阵
                    OutputArray dst, //用于保存Harris角点检测结果,32位单通道,大小与src相同
                    int blockSize,   //滑块窗口的尺寸
                    int ksize,       //Sobel边缘检测滤波器大小
                    double k,        //Harries中间参数,经验值0.04~0.06
                    int borderType=BORDER_DEFAULT   //插值类型
                    );

完整代码:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;


Mat image;
Mat imageGray;
int thresh = 200;
int MaxThresh = 255;

void Trackbar(int, void*);  //阈值控制

int main()
{
	image = imread("1.jpg");
	cvtColor(image, imageGray, CV_RGB2GRAY);
	GaussianBlur(imageGray, imageGray, Size(5, 5), 1); // 滤波
	namedWindow("Corner");
	createTrackbar("threshold:", "Corner", &thresh, MaxThresh, Trackbar);
	imshow("Corner", image);
	Trackbar(0, 0);
	waitKey();
	return 0;
}

void Trackbar(int, void*)
{
	Mat dst, dst8u, dstshow, imageSource;
	dst = Mat::zeros(image.size(), CV_32FC1);
	imageSource = image.clone();
	cornerHarris(imageGray, dst, 3, 3, 0.04, BORDER_DEFAULT);
	normalize(dst, dst8u, 0, 255, CV_MINMAX);  //归一化
	convertScaleAbs(dst8u, dstshow);
	imshow("dst", dstshow);  //dst显示
	for (int i = 0;i<image.rows;i++)
	{
		for (int j = 0;j<image.cols;j++)
		{
			if (dstshow.at<uchar>(i, j)>thresh)  //阈值判断
			{
				circle(imageSource, Point(j, i), 2, Scalar(0, 0, 255), 2); //标注角点
			}
		}
	}
	imshow("Corner", imageSource);
}

注:
1.convertScaleAbs函数是OpenCV中的函数,使用线性变换转换输入数组元素成8位无符号整型。

2.cvCircle(CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int lineType=8, int shift=0)

img为源图像指针

center为画圆的圆心坐标

radius为圆的半径

color为设定圆的颜色,规则根据B(蓝)G(绿)R(红)

thickness 如果是正数,表示组成圆的线条的粗细程度。否则,表示圆是否被填充

line_type 线条的类型。默认是8

shift 圆心坐标点和半径值的小数点位数
效果图:

图3

五、OpenCV实现Shi-Tomasi角点检测

OpenCV 提供了函数: cv2.goodFeaturesToTrack()。这个函数可以帮我们使用 Shi-Tomasi 方法获取图像中 N 个最好的角点(也可以通过改变参数来使用 Harris 角点检测算法)。通常情况下,输入的应该是灰度图像。然后确定你想要检测到的角点数目。再设置角点的质量水平, 0到 1 之间。它代表了角点的最低质量,低于这个数的所有角点都会被忽略。最后在设置两个角点之间的最短欧式距离。根据这些信息,函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略,然后再把合格角点按角点质量进行降序排列。函数会采用角点质量最高的那个角点(排序后的第一个),然后将它附近(最小距离之内)的角点删掉。按着这样的方式最后返回 N 个最佳角点。


//goodFeaturesToTrack有比cornerHarries更多的控制参数,函数原型:
 
void goodFeaturesToTrack( InputArray image, OutputArray corners,
                                    int maxCorners, double qualityLevel, double minDistance,
                                    InputArray mask=noArray(), int blockSize=3,
                                    bool useHarrisDetector=false, double k=0.04);
 
/*第一个参数image:8位或32位单通道灰度图像;
  第二个参数corners: 位置点向量,保存的是检测到角点的坐标;
  第三个参数maxCorners: 定义可以检测到的角点的数量的最大值;
  第四个参数qualityLevel: 检测到的角点的质量等级,角点特征值小于qualityLevel*最大特征
  值的点将被舍弃;
  第五个参数minDistance: 两个角点间最小间距,以像素为单位;
  第六个参数mask: 指定检测区域,若检测整幅图像,mask置为空Mat();
  第七个参数blockSize: 计算协方差矩阵时窗口大小;
  第八个参数useHarrisDector: 是否使用Harris角点检测,为false,则使用Shi-Tomasi算子;
  第九个参数K: 留给Harris角点检测算子用的中间参数,一般取经验值0.04~0.06.第8个参数为false时,改参数不起作用;
#include "core/core.hpp"  
#include "highgui/highgui.hpp" 
#include "imgproc/imgproc.hpp"
 
using namespace cv;   
 
Mat image;
Mat imageGray;
int thresh=5;   //角点个数控制
int MaxThresh=255;
 
void Trackbar(int,void*);  
 
int main(int argc,char*argv[])  
{  
	image=imread(argv[1]);
	cvtColor(image,imageGray,CV_RGB2GRAY);
	GaussianBlur(imageGray,imageGray,Size(5,5),1); // 滤波
	namedWindow("Corner Detected");
	createTrackbar("threshold:","Corner Detected",&thresh,MaxThresh,Trackbar);
	imshow("Corner Detected",image);
	Trackbar(0,0);
	waitKey();
	return 0;
}  
 
void Trackbar(int,void*)
{
	Mat dst,imageSource;
	dst=Mat::zeros(image.size(),CV_32FC1);  
	imageSource=image.clone();
	vector<Point2f> corners;  
	goodFeaturesToTrack(imageGray,corners,thresh,0.01,10,Mat());
	for(int i=0;i<corners.size();i++)
	{
		circle(imageSource,corners[i],2,Scalar(0,0,255),2);
	}
	imshow("Corner Detected",imageSource); 
}

goodFeaturesToTrack相比cornerHarris,增加了检测的复杂度,同时也可以更好的控制检测到的角点的特性,比如角点个数,角点间最小间距等。设置检测点数为56时,只有特征值最大的前56个角点被检测出来,继续增大检测点数的值,所有角点都被检测出来
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/kilotwo/article/details/88953414