OpenCV中角点检测:Harris、Shi-Tomasi、亚像素级角点检测

∥☆過路亽.° 提交于 2020-01-26 01:51:54

1、角点的定义

角点通常被定义为两条边的交点,或者说,角点的局部邻域应该具有两个不同区域的不同方向的边界。比如,三角形有三个角,矩形有四个角,这些就是角点,也是他们叫做矩形、三角形的特征。

角点是个很特殊的存在。如果某一点在任意方向的一个微小的变动都会引起灰度很大的变化,那么我们就可以把该点看做是角点。

角点检测(Corner Detection)是计算机视觉系统中获取图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维重建和目标识别等,也可称为特征点检测。

2、角点检测算法的基本思想

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

目前,角点检测算法还不是十分完善,许多算法需要依赖大量的训练集和冗余数据来防止和减少错误的特征的出现。对于角点检测算法的重要评价标准是:其对多幅图像中相同或者相似特征的检测能力,并且能够应对光照变化、或者图像旋转等影响。

关于角点的具体描述可以有几种:

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

三类角点检测算法:

基于二值图像的角点检测;
基于轮廓曲线的角点检测;
基于灰度图像的角点检测:基于梯度、基于模板和基于模板和梯度组合三类方法;常见的基于模板的角点检测算法有:Kitchen-Rosenfeld角点检测算法,Harris角点检测算法,KLT角点检测算法及SUSAN角点检测算法。基于模板的方法主要是考虑像素领域点灰度的变化,即亮度的变化。
 

3、Harris 角点检测

Harris角点检测是一种直接基于灰度图的角点提取算法,稳定性高,尤其对L型角点(也就是直角)检测精度高。缺点也是明显的,就是运算速度慢。

void cornerHarris( InputArray src, OutputArray dst, int blockSize,int ksize,
                    double k, int borderType = BORDER_DEFAULT );
参数解释:
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。
第三个参数,int类型的blockSize,表示邻域的大小,更多的详细信息在cornerEigenValsAndVecs()中有讲到。
第四个参数,int类型的ksize,表示Sobel()算子的孔径大小。
第五个参数,double类型的k,Harris参数,经验值0.04~0.06。
第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。
#include<iostream>
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace cv;
using namespace std;

void CornerHarris_Demo(int, void*);

Mat img, gray, dst;
int thresh_value = 130; //当前阈值  
int max_thresh_value = 255; //最大阈值 

int main()
{
	img = imread("C:\\Users\\cf\\Desktop\\测试数据\\test0.jpg");
	if (img.empty()){
		return -1;
	}
	cout << img.size() << endl;
	imshow("原图",img);
	cvtColor(img, gray, CV_BGR2GRAY);

	createTrackbar("threash", "角点检测", &thresh_value, max_thresh_value, CornerHarris_Demo);
	CornerHarris_Demo(thresh_value, 0);

	waitKey();
	return 0;
}

void CornerHarris_Demo(int, void*){
	Mat normImg, scaledImg;
	Mat resultImg = img.clone();
	dst = Mat::zeros(gray.size(), CV_32FC1);

	cornerHarris(gray, dst, 5, 5, 0.04, BORDER_DEFAULT);

	//归一化与转换
	normalize(dst, normImg, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
	convertScaleAbs(normImg, scaledImg);

	for (int i = 0; i < resultImg.rows; i++){
		//读取像素值
		uchar *currentRow = scaledImg.ptr(i);
		for (int j = 0; j < resultImg.cols; j++){
			if ((int)*currentRow > thresh_value){
				circle(resultImg, Point(i, j), 5, Scalar(0, 0, 255), 2, 8, 0);
			}
			currentRow++;
		}
	}
	imshow("角点检测", resultImg);
}

4、Shi-Tomasi角点检测

Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将矩阵 M 的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。
Shi 和Tomasi 的方法比较充分,并且在很多情况下可以得到比使用Harris 算法更好的结果。

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:计算协方差矩阵时窗口大小;
第八个参数useHarrisDetector:是否使用Harris角点检测,为false,则使用Shi-Tomasi算子;
第九个参数k:留给Harris角点检测算子用的中间参数,一般取经验值0.04~0.06。第八个参数为false时,该参数不起作用;
#include<iostream>
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace cv;
using namespace std;

void CornerHarris_Demo(int, void*);
void Tomoshi_Demo(int, void*);

Mat img, gray, dst;
int num_corners = 23;		//角点个数
const int max_corners = 150;//定义最大的角点个数

int main()
{
	img = imread("C:\\Users\\cf\\Desktop\\测试数据\\test0.jpg");
	if (img.empty()){
		return -1;
	}
	cout << img.size() << endl;
	imshow("原图",img);
	cvtColor(img, gray, CV_BGR2GRAY);

	createTrackbar("角点检测", "角点检测", &num_corners, max_corners, Tomoshi_demo);
	Tomoshi_demo(0, 0);

	waitKey();
	return 0;
}

void Tomoshi_demo(int, void*) {
	if (num_corners < 5){
		num_corners = 5;
	}
	vector<Point2f> corners;
	double qualityLevel = 0.01;
	double min_distance = 10.0;
	int block_size = 3;
	bool harris_use = false;
	double k = 0.04;

	Mat result_image = img.clone();
	goodFeaturesToTrack(gray, corners, max_corners, qualityLevel, min_distance, Mat(), block_size, harris_use, k);
	for (int i = 0; i < corners.size(); i++){
		circle(result_image, corners[i], 2, Scalar(0, 0, 255), 1, 8, 0);
	}

	imshow("角点检测", result_image);
}

5、亚像素级角点检测


    当我们想要进行几何测量或者标定的时候势必要比目标识别需要更高的精度的特征点。而上面的goodFeaturesToTrack()只能得到整数的坐标值,这时候我们就需要亚像素级的角点检测来得到实数坐标值来满足精度需求。

亚像素定位大致具有三种方法:

  • 插值方法
  • 基于图像矩计算
  • 曲线拟合方法(高斯曲面,椭圆曲面,多项式)
void cornerSubPix( 
    InputArray image,          //输入图像
    InputOutputArray corners,  //输入角点的初始坐标和为输出的精确坐标
    Size winSize,              //搜索窗口边长的一半
    Size zeroZone,             //搜索区域中间的死区大小的一半,(-1,-1)表示没有这样的大小。
    TermCriteria criteria      //终止角点优化迭代的条件
);
#include<iostream>
#include<opencv2\opencv.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace cv;
using namespace std;

void SubPix_Demo(int, void*);

Mat img, gray, dst;
int num_corners = 23;		//角点个数
const int max_corners = 150;//定义最大的角点个数

int main()
{
	img = imread("C:\\Users\\cf\\Desktop\\测试数据\\test0.jpg");
	if (img.empty()){
		return -1;
	}
	cout << img.size() << endl;
	imshow("原图",img);
	cvtColor(img, gray, CV_BGR2GRAY);

	createTrackbar("角点检测", "角点检测", &num_corners, max_corners, SubPix_demo);
	SubPix_demo(num_corners, 0);

	waitKey();
	return 0;
}

void SubPix_demo(int, void*) {
	if (num_corners < 5){
		num_corners = 5;
	}
	vector<Point2f> corners;
	double qualityLevel = 0.01;
	double min_distance = 10.0;
	int block_size = 3;
	bool harris_use = false;
	double k = 0.04;

	Mat result_image = img.clone();
	goodFeaturesToTrack(gray, corners, max_corners, qualityLevel, min_distance, Mat(), block_size, harris_use, k);
	Size winSize = Size(5, 5);
	Size zeroZone = Size(-1, -1);
	//终止角点优化迭代的条件
	TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
	cornerSubPix(gray, corners, winSize, zeroZone, tc);
	
	for (int i = 0; i < corners.size(); i++){
		circle(result_image, corners[i], 2, Scalar(0, 0, 255), 1, 8, 0);
	}

	imshow("角点检测", result_image);
}

6、参考

https://blog.csdn.net/zhu_hongji/article/details/81235643

https://www.cnblogs.com/skyfsm/p/6899627.html

https://blog.csdn.net/cv_pyer/article/details/89647453


 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!