OpenCV学习(二十四 ):角点检测(Corner Detection):cornerHarris(),goodFeatureToTrack()
参考博客:
Harris角点检测原理详解
Harris角点检测原理及C++实现
OpenCV亚像素角点cornerSubPixel()源代码分析
Taylor公式(泰勒公式)通俗+本质详解
如何理解最小二乘法?
一、概述
1、角点定义:
角点没有明确的数学定义,但人们普遍认为角点是二维图像亮度变化剧烈的点或图像边缘曲线上曲率极大值的点。这些点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。其在三维场景重建、运动估计、目标跟踪、目标识别、图像配准与匹配等计算机视觉领域起着非常重要的作用。
2、角点检测:
计算机视觉子系统中用来捕获图像特征的一种方法,广泛应用于运动检测,图相匹配,视频跟踪,三维重建和目标识别等领域,也称为特征点检测。我不再观测整幅图,二十选择某些特殊的点,然后对他们进行局部 有的放矢(有明确的目的性和针对性)地分析。
图像的特征类型包括有:
1)边缘
2)角点(感兴趣的关键点)
3)斑点(感兴趣的区域)
关于角点的具体描述有:
1)一阶导数(既灰度的梯度)的局部最大所对应的像素点
2)两条既两条以上边缘的交点
3)图像中梯度值和梯度方向的变化速率都很高的点
4)角点处得一阶导数最大,二阶导数为零,它指示了物体边缘变化不连续的方向
在当前的图像处理领域中,角点检测算法可以归纳为:
1)基于灰度图像的角点检测(基于灰度图像的角点检测又可分三类,见下)
2)基于二值图像的角点检测
3)基于轮廓曲线的角点检测
基于灰度图像的角点检测:
1)基于梯度:
其中基于梯度的方法是通过计算边缘的曲率来判断角点的存在性,角点计算数值的大小不仅与边缘强度有关,而且与边缘方向的变化率有关,该方法对噪声比基于模板的角点检测方法对噪声更为敏感。
2)基于模板:
其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
3)基于模板+梯度组合
二、harris角点检测,cornerHarris()函数
harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。但由于
该函数在图像上运行Harris角探测器。与cornerMinEigenVal()和cornerEigenValsAndVecs()函数类似,对于每个像素(x,y),它计算 blockSize×blockSize 邻域上的2×2梯度协方差矩阵M(x,y)。然后计算如下特征
:
图像中的角点可以作为此响应映射的局部最大值。即找出角点。
void cornerHarris(
InputArray src, // 输入图像,单通道8位或者浮点型图像
OutputArray dst, // 存放利用角点检测之后的图像
int blockSize*blockSize, // 表示领域的大小
int ksize, // 表示Sobel()算子孔径的大小
double k, // Harris参数
intborderType=BORDER_DEFAULT // 图像像素的边界模式有默认值BORDER_DEFAULT
)
在用cornerHarris函数时要用单通道的图像,要配合阈值操作函数一起threshold()对角点检测之后的图像进行显示操作,确定图像的强角点:goodFeatureToTrack()函数。
三、cornerMinEigenVal(),cornerEigenValsAndVecs()函数
作用:计算图像块的特征值和特征向量用于角点检测。
void cornerEigenValsAndVecs(
InputArray src, // 输入单通道8位或浮点图像
OutputArray dst, // 用来存储结果的图像;它有src相同的大小和类型为CV_32FC(6)
int blockSize, // 领域尺寸
int ksize, // Sobel()算子的孔径参数
int borderType=BORDER_DEFAULT // 图像像素的边界模式有默认值BORDER_DEFAULT
);
作用:计算梯度矩阵的最小特征值用于角点检测。
void cornerMinEigenVal(
InputArray src, // 输入单通道8位或浮点图像;
OutputArray dst, // 用来存储最小特征值的图像;它有src相同的大小和类型为CV_32FC1;
int blockSize, // 领域尺寸;
int ksize=3, // Sobel()算子的孔径参数;
int borderType=BORDER_DEFAULT // 像素外推方式;
);
三、确定图像强角点:Shi-Tomasi角点检测,goodFeaturesToTrack()函数
goodFeatureToTrack()函数结合了Shi-Tomasi算子,用于确定图像的强角点:
void goodFeaturesToTrack(
InputArray image, // 输入图像,需要8位的或者浮点型32位单通道图像
OutputArray corners, // 检测到角点的输出向量
int maxCorners, // 角点的最大数量
double qualityLevel, // 角点检测可接受的最小特征值:其实实际用于过滤角点的最小特征值// 是 qualityLevel 与图像中最大特征值得乘积。// 通常不超过1(常用0.10和0.01)。检测完所有角点后,// 还要剔除一些距离较近的角点
double minDistance, // 角点之间的最小距离,单位为像素
InputArray mask=noMask(), // 用于指定感兴趣区域的角点检测,默认是noArray()
int blockSize=3, // 是计算导数自相关矩阵时指定的邻域范围
bool useHarrisDetector=false, // 指示是否使用Harris角点检测
double k=0.04 // 为了用于设置Hessian 自相关矩阵行列式 的相对权重的权重系数
);
四、亚像素级角点检测:cornerSubPix()函数
原理见上:
cornerSubPix()函数用于寻找亚像素级角点位置(更精确的浮点类型位置)
void cornerSubPix(
InputArray _image, // 输入的单通道图像
InputOutputArray _corners, // 提取的初始整数角点(比如用goodFeatureToTrack提取的强角点)
Size winSize, // 为求取亚像素角点的窗口大小,比如设置Size(11,11),需要注意的是11为半径,则窗口大小为23x23
Size zeroZone, // 设置的“零区域”,在搜索窗口内,设置的“零区域”内的值不会被累加,权重值为0。如果设置为Size(-1,-1),则表示没有这样的区域
TermCriteria criteria // 条件阈值,包括迭代次数阈值和误差精度阈值,一旦其中一项条件满足设置的阈值,则停止迭代,获得亚像素角点
)
五示例一:harris角点检测
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;#define WINDOW_NAME1 "【程序窗口1】" //为窗口标题定义的宏
#define WINDOW_NAME2 "【程序窗口2】" //为窗口标题定义的宏Mat g_srcImage, g_srcImage1,g_grayImage;
int thresh = 30; //当前阈值
int max_thresh = 175; //最大阈值int main()
{// 1、载入原始图并进行克隆保存g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/corner.jpg", 1 );if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }imshow("原始图",g_srcImage);g_srcImage1=g_srcImage.clone( );// 2、存留一张灰度图cvtColor( g_srcImage1, g_grayImage, COLOR_BGR2GRAY );// 3、创建窗口和滚动条namedWindow( WINDOW_NAME1, WINDOW_AUTOSIZE );createTrackbar( "阈值: ", WINDOW_NAME1, &thresh, max_thresh, on_CornerHarris );on_CornerHarris( 0, 0 );waitKey(0);return 0;
}
void on_CornerHarris( int, void* )
{// 1、定义一些局部变量Mat dstImage;//目标图Mat normImage;//归一化后的图Mat scaledImage;//线性变换后的八位无符号整型的图// 2、初始化//置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值dstImage = Mat::zeros( g_srcImage.size(), CV_32FC1 );g_srcImage1=g_srcImage.clone( );// 3、正式检测// 进行角点检测// 领域大小为 2// sobel 算子孔径 3// harris 参数cornerHarris( g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT );// 归一化与转换normalize( dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );convertScaleAbs( normImage, scaledImage ); //将归一化后的图线性变换成8位无符号整型// 4、进行绘制// 将检测到的,且符合阈值条件的角点绘制出来int corner_count = 0;for( int j = 0; j < normImage.rows ; j++ )for( int i = 0; i < normImage.cols; i++ ){if( (int) normImage.at<float>(j,i) > thresh+80 ) // 设定阈值{circle( g_srcImage1, Point( i, j ), 5, Scalar(10,10,255), 2, 8, 0 );circle( scaledImage, Point( i, j ), 5, Scalar(0,10,255), 2, 8, 0 );}}cout<<"角点个数="<<corner_count<<endl;// 4、显示最终效果imshow( WINDOW_NAME1, g_srcImage1 );imshow( WINDOW_NAME2, scaledImage );
}
结果:
六、示例二:确定图像强角点:Shi-Tomasi角点检测,亚像素级角点检测:cornerSubPix()函数
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;#define WINDOW_NAME "【Shi-Tomasi角点检测】" //为窗口标题定义的宏Mat g_srcImage, g_grayImage;
int g_maxCornerNumber = 50; // 角点的最大数量
int g_maxTrackbarNumber = 500;
int g_minDistance =10;
RNG g_rng(12345);//初始化随机数生成器int main()
{// 1、载入原始图并进行克隆保存
// g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/corner2.jpg", 1 );g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/test1.png", 1 );if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }cvtColor( g_srcImage, g_grayImage, COLOR_BGR2GRAY );// 2、创建窗口和滑动条,并进行显示和回调函数初始化namedWindow( WINDOW_NAME, WINDOW_AUTOSIZE );createTrackbar( "最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack );createTrackbar( "最小距离", WINDOW_NAME, &g_minDistance, 100, on_GoodFeaturesToTrack );imshow( "原始图", g_srcImage );on_GoodFeaturesToTrack( 0, 0 );waitKey(0);return 0;
}
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;//角点检测可接受的最小特征值int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围double k = 0.04;//权重系数Mat copy = g_srcImage.clone(); //复制源图像到一个临时变量中,作为感兴趣区域// 3、进行Shi-Tomasi角点检测goodFeaturesToTrack( g_grayImage, // 输入图像corners, // 检测到的角点的输出向量g_maxCornerNumber, // 角点的最大数量qualityLevel, // 角点检测可接受的最小特征值g_minDistance, // 角点之间的最小距离Mat(), // 感兴趣区域blockSize, // 计算导数自相关矩阵时指定的邻域范围false, // 不使用Harris角点检测k ); // 权重系数// 4、输出文字信息cout<<"\t>此次检测到的角点数量为:"<<corners.size()<<endl;// 5、绘制检测到的角点int r = 4;for( 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术语标准TermCriteria criteria = TermCriteria( TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001 );// 8、计算出亚像素角点位置vector<Point2f> corners_sub=corners;cornerSubPix( g_grayImage, corners_sub, winSize, zeroZone, criteria );// 9、输出角点信息for( int i = 0; i < corners.size(); i++ ){cout<<" \t>>亚像素级/像素级 角点坐标["<<i<<"] ("<<corners_sub[i]<<")"<<" ("<<corners[i].x<<","<<corners[i].y<<")"<<endl;}
}
结果: