图像处理之亚像素级角点检测

621 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天

1 背景概述

若我们进行图像处理的目的不是提取用于识别的特征点而是进行几何测量,这通常需要更高的精度,而函数 goodFeaturesToTrack()只能提供简单的像素的坐标值,也就是说,有时候会需要实数坐标值而不是整数坐标值。

亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重建被跟踪目标的三维结构时,是一个基本的测量值。

下面我们将讨论如何将所求得的角点位置精确到亚像素级精度。一个向量和与其正交的向量的点积为0,角点则满足如下图所示情况。

3.png

其中,(a)点p附近的图像是均匀的,其梯度为0;(b)边缘的梯度与沿边缘方向的q—p向量正交。在图中的两种情况下,p点梯度与q—p向量的点积均为0。

我们假设起始角点q在实际亚像素级角点的附近。检测所有的q—p向量。若点p位于一个均匀的区域,则点p处的梯度为0。若q—p向量的方向与边缘的方向一致,则此边缘上p点处的梯度与q—p向量正交,在这两种情况下,p点处的梯度与q—p向量的点积为0。我们可以在p点周围找到很多组梯度以及相关的向量q—p,令其点集为0,然后可以通过求解方程组,方程组的解即为角点q的亚像素级精度的位置,也就是精确的角点位置。

OpenCV为我们提供了cornerSubPix()函数,用于发现亚像素精度的角点位置。

2 寻找亚像素角点:cornerSubPix()函数

cornerSubPix 函数用于寻找亚像素角点位置(不是整数类型的位置,而是更精确的浮点类型位置)。

C++: void cornerSubPix( InputArray image, InputOutputArray corners, Size winsize, Size zeroZone, TermCriteria criteria)

  • 第一个参数,InputArray类型的image,输入图像,即源图像。
  • 第二个参数,InputOutputArray类型的corners,提供输入角点的初始坐标和 精确的输出坐标。
  • 第三个参数,Size类型的winSize,搜索窗口的一半尺寸。若winSize=Size(5, 5),那么就表示使用(5 * 2 + 1) x (5 * 2 + 1)=11x11大小的搜索窗口。
  • 第四个参数,Size类型的zeroZone,表示死区的一半尺寸。而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值为(—1,—1)表示没有死区。
  • 第五个参数,TermCriteria类型的criteria,求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者是精确懂达到某个设定值。criteria 可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。

3 综合示例:亚像素级角点检测

源码

//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//---------------------------------------------------------------------------------------
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;


//-----------------------------------【宏定义部分】-------------------------------------------- 
//  描述:定义一些辅助宏 
//--------------------------------------------------------------------------------------------
#define WINDOW_NAME "【亚像素级角点检测】"        //为窗口标题定义的宏 


//-----------------------------------【全局变量声明部分】--------------------------------------
//          描述:全局变量声明
//-------------------------------------------------------------------------------------------
Mat g_srcImage, g_grayImage;
int g_maxCornerNumber = 33;
int g_maxTrackbarNumber = 500;
RNG g_rng(12345);//初始化随机数生成器


//-----------------------------【on_GoodFeaturesToTrack( )函数】----------------------------
//          描述:响应滑动条移动消息的回调函数
//-----------------------------------------------------------------------------------------
void on_GoodFeaturesToTrack( int, void* )
{
	//【1】对变量小于等于1时的处理
	if( g_maxCornerNumber <= 1 ) { g_maxCornerNumber = 1; }

	//【2】Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
	vector<Point2f> corners;
	double qualityLevel = 0.01;//角点检测可接受的最小特征值
	double minDistance = 10;//角点之间的最小距离
	int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
	double k = 0.04;//权重系数
	Mat copy = g_srcImage.clone();	//复制源图像到一个临时变量中,作为感兴趣区域

	//【3】进行Shi-Tomasi角点检测
	goodFeaturesToTrack( g_grayImage,//输入图像
		corners,//检测到的角点的输出向量
		g_maxCornerNumber,//角点的最大数量
		qualityLevel,//角点检测可接受的最小特征值
		minDistance,//角点之间的最小距离
		Mat(),//感兴趣区域
		blockSize,//计算导数自相关矩阵时指定的邻域范围
		false,//不使用Harris角点检测
		k );//权重系数

	//【4】输出文字信息
	cout<<"\n\t>-------------此次检测到的角点数量为:"<<corners.size()<<endl;

	//【5】绘制检测到的角点
	int r = 4;
	for( unsigned int i = 0; i < corners.size(); i++ )
	{ 
		//以随机的颜色绘制出角点
		circle( copy, corners[i], r, Scalar(g_rng.uniform(0,255), g_rng.uniform(0,255),
			g_rng.uniform(0,255)), -1, 8, 0 ); 
	}

	//【6】显示(更新)窗口
	imshow( WINDOW_NAME, copy );

	//【7】亚像素角点检测的参数设置
	Size winSize = Size( 5, 5 );
	Size zeroZone = Size( -1, -1 );
	TermCriteria criteria = TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40, 0.001 );

	//【8】计算出亚像素角点位置
	cornerSubPix( g_grayImage, corners, winSize, zeroZone, criteria );

	//【9】输出角点信息
	for( int i = 0; i < corners.size(); i++ )
	{ cout<<" \t>>精确角点坐标["<<i<<"]  ("<<corners[i].x<<","<<corners[i].y<<")"<<endl; }


}


//--------------------------------------【main( )函数】-----------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//---------------------------------------------------------------------------------------------
int main(  )
{
	//【0】改变console字体颜色
	system("color 2F"); 

	//【1】载入源图像并将其转换为灰度图
	g_srcImage = imread("1.jpg", 1 );
	cvtColor( g_srcImage, g_grayImage, CV_BGR2GRAY );

	//【2】创建窗口和滑动条,并进行显示和回调函数初始化
	namedWindow( WINDOW_NAME, CV_WINDOW_AUTOSIZE );
	createTrackbar( "最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack );
	imshow( WINDOW_NAME, g_srcImage );
	on_GoodFeaturesToTrack( 0, 0 );

	waitKey(0);
	return(0);
}

这个程序在上一节Shi-Tomasi角点检测示例程序的基础上,在on_GoodFeaturesToTack()回调函数中加亚像素角点检测的代码。

运行此程序,得到的窗口和之前的【Shi—Tomasi 角点检测】一致,区别在于在控制台窗口中输出了检测到的浮点型亚像素角点精确坐标值。

原图

1.jpg

效果图-角点检测数量

4.png

效果图-角点检测1

5.png

效果图-角点检测2

6.png


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天