opencv——角点检测

 

一:角点检测

什么是角点,难道是角落里面的点?我们知道,比如说墙角,他有往左延申的边缘,又有往右延申的边缘,那么这样的概念同样可以帮助我们理解图像的角点检测。

其实我们人眼对于角点的识别是通过一个小窗口来实现的,如下面这张图所示,如果在各个方向上移动这个小窗口,窗口内的灰度发生了较大的变化,那么说明窗口内存在角点。

  • 如果在各个方向上移动,灰度变化为0,则这一块区域是平坦区域
  • 如果只有一个方向移动,灰度值几乎不变,这一块区域是边缘区域
  • 如果各个方向上移动,灰度值都发生了变化,则该区域是角点区域

图像I(x,y),在点(x,y)处平移(u,v)后的自相似性,可以用灰度变化函数E(u,v)表示:

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

泰勒展开:

                                                              I(x+u,y+v)=I(x,y)+I_{x}u+I_{y}v+O(u^{2},v^{2})

代入得到:

                                                                                  E(u,v)=[u,v]M\begin{bmatrix}u\\v\end{bmatrix}

其中:

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

矩阵M的两个特征值分别代表灰度值的变化率,其与图像中的角点,边缘,平坦区域的关系如下所示:

Harris定义的角点检测响应函数为:

                                                                                  R=\lambda _{1}\lambda _{2}-k(\lambda _{1}+\lambda _{1})^2

k的取值为一个经验常数,大致在0.04-0.06之间。

我们定义当R大于threshold,并且为局部最大值的点为角点。

Harris角点检测算子对图像亮度和对比度具有部分不变性,且具有旋转不变性,但不具有尺度不变性。

opencv中实现Harris角点检测

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;


Mat src, dst, gray_src;
int thresh_value = 130;		//设置当前阈值
int max_thresh_value = 255;		//设置最大阈值

void Harris_Demo(int, void*);		//实现Harris角点检测函数
char *output_image = "Harris Result";		//输出图像窗口
int main(int argc, char**argv) {
	//读入原始图像并显示
	src = imread("D:/opencv/yuner.jpg");
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	namedWindow(output_image, CV_WINDOW_AUTOSIZE);
	cvtColor(src, gray_src, CV_BGR2GRAY);		//灰度图像转换
	//创建滑动bar
	createTrackbar("thresh", output_image, &thresh_value, max_thresh_value, Harris_Demo);
	Harris_Demo(thresh_value, 0);
	waitKey(0);
	return(0);
}

void Harris_Demo(int, void*) {
	
	Mat normImage, scaleImage;
	//将需要显示的图置零,即清除上一次调用此函数的值
	Mat ResultImage = src.clone();
	dst = Mat::zeros(gray_src.size(), CV_32FC1);
	//角点检测
	cornerHarris(gray_src, dst, 2, 3, 0.04, BORDER_DEFAULT);
	//归一化到0-255尺度下
	normalize(dst, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
	//将归一化后的图像线性变化为8位无符号整型
	convertScaleAbs(normImage, scaleImage);

	for (int i = 0; i < ResultImage.rows; i++) {
		//读取像素值
		uchar *currentRow = scaleImage.ptr(i);
		for (int j = 0; j < ResultImage.cols; j++) {
			if ((int) *currentRow > thresh_value) {
				//如果像素值大于阈值,则将角点画在输出图像上
				circle(ResultImage, Point(i, j), 2, (0, 0, 255),2,8,0);
			}
			currentRow++;
		}
	}
	imshow(output_image, ResultImage);
}

 

运行程序我们可以看到如下的效果图:

 

调整threshold的值,我们能看到焦点的变化情况,但总体来说,该算法的鲁棒性不够好,下面我们看一下和Harris算法有点相似的shi-Tomoshi角点检测。

Shi-Tomasi角点检测

由于Harris算法的稳定性和k值有关,Shi-Tomasi发现,角点的稳定性和矩阵M的较小特征值有关,改进的Harris算法即直接计算出矩阵M的特征值,用较小的特征值与阈值比较,大于阈值的即为强特征点。

opencv中对其实现算法在goodFeaturesToTrack()函数中:

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat src, gray_src;
char *output_image = "Tomoshi Result";		//输出窗口
int num_corners = 23;		//角点个数
const int max_corners = 150;		//定义最大的角点个数

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;		//是否适用harris
	double k = 0.04;		//经验系数(0.04-0.06)

	Mat result_image = src.clone();
	goodFeaturesToTrack(gray_src, 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, (0, 0, 255), 1, 8, 0);
	imshow(output_image, result_image);
}

int main() {
	src = imread("D:/opencv/yuner.jpg");
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	namedWindow(output_image, CV_WINDOW_AUTOSIZE);
	cvtColor(src, gray_src, CV_BGR2GRAY);
	
	createTrackbar("corner number", output_image, &num_corners, max_corners, Tomoshi_demo);
	Tomoshi_demo(0, 0);
	waitKey(0);
	return 0;
}

继续看看上述程序运行的结果。

可以看到,检测出来的角点更符合实际情况,Shi-Tomasi算法的鲁棒性更好。

亚像素级别角点检测

理论和现实不可能完全保持一致,上面所说的Harris和Shi-Tomasi角点检测中,可能我们检测得到的角点为(100,5),但实际上可能是一个浮点数(100.234,5.789),为了提高检测精度,特别是在跟踪定位,相机矫正,三维重建等领域里面对精度要求很高,我们引入亚像素角点检测。

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

  • 插值方法
  • 基于图像矩计算
  • 曲线拟合方法(高斯曲面,椭圆曲面,多项式)

opencv为我们提供了现成的亚像素角点检测函数cornerSubPix(),通过迭代角点或者径向鞍点精确的亚像素位置。

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

Mat src, gray_src;
char *output_image = "SubPix Result";		//输出窗口
int num_corners = 20;		//角点个数
const int max_corners = 50;		//定义最大的角点个数

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;		//是否适用harris
	double k = 0.04;		//经验系数(0.04-0.06)

	Mat result_image = src.clone();
	goodFeaturesToTrack(gray_src, corners, max_corners, qualityLevel, min_distance, Mat(), block_size, Harris_use, k);
	
	Size winSize = Size(5, 5);		//搜索窗口边长的一半,(5,5)所对应的的搜索窗口5∗2+1×5∗2+1=11×11大小的搜索窗口
	Size zeroZone = Size(-1, -1);		//搜索区域内死区大小的一半,(-1,-1)表示没有这样的大小
	TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);	//终止角点优化迭代的条件

	cornerSubPix(gray_src, corners, winSize, zeroZone, tc);
	for (int i = 0; i < corners.size(); i++) {
		cout << (i + 1) << " Point(x,y)= " << corners[i].x << ',' << corners[i].y << endl;
        circle(result_image, corners[i], 2, (0, 0, 255), 1, 8, 0);
	}
	
	return;
}

int main() {
	src = imread("D:/opencv/yuner.jpg");
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	namedWindow(output_image, CV_WINDOW_AUTOSIZE);
	cvtColor(src, gray_src, CV_BGR2GRAY);

	createTrackbar("corners", output_image, &num_corners, max_corners, SubPix_demo);
	SubPix_demo(0, 0);
	waitKey(0);
	return 0;
}

运行程序打印输出结果如下所示:

可以看到所有的角点坐标已经精确到浮点数。

总结

角点检测差不多就学到这里了,其实我们可以自定义实现Harris以及shi-Tomasi角点检测算法,自定义Harris角点检测器用到的函数为cornerEigenValsAndVecs(),用来计算特征值和特征向量,自定义Shi-Tomasi角点检测用到的函数为cornerMinEigenVal(),由于我太懒就没有实现了。有感兴趣的可以自己去尝试下。

参考链接:https://blog.csdn.net/akadiao/article/details/78893061

https://blog.csdn.net/weixin_41695564/article/details/79979784

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值