【角点检测】
兴趣点也被称为关键点、特征点。他被大量用于解决物体识别、图像识别、图像匹配、视觉跟踪、三维重建等一系列问题中。
图像的特征类型分为:①边缘;②角点(感兴趣关键点);③斑点(Blobs)(感兴趣区域)
【角点】某一点在任意方向的一个微小变动都会引起灰度很大的变化。(与位于相同强度区域上的点不同,与物体轮廓上的点也不同)
它们在图像中可以轻易定位。
角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,所以它们是可以精确定位的二维特征,甚至可以达到亚像素的精度。又
由于其图像梯度又很高的变化,这种变化是可以用来帮助检测角点的。
【角点的具体描述】①一阶导数(灰度的梯度)的局部最大所对应的像素点;②两条即两条以上边缘的交点;
③图像中梯度值和梯度方向的变化速率都很高的点;④角点处的一阶导数最大,二阶导数为0,它指示了物体边缘变化不连续的方向。
【角点检测分类】目前的角点检测算法可归纳为3类:角点是图像很重要的特征,对图像图形的理解和分析有很重要的作用。
①基于灰度图像的角点检测----基于梯度、模板(Kitchen-Rosenfeld)、模板梯度组合三类方法。其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
②基于二值图像的角点检测
③基于轮廓曲线的角点检测
1、【harris角点检测】
【harris角点检测】:是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。
缺点:运算速度慢(高斯滤波),角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象
实现Harris角点检测:cornerHarris()函数
void cornerHarris(
InputArray src, //输入图像
OutputArray dst, //存放Harris角点检测的输出结果
int blockSize, //表示邻域的大小
int ksize, //表示sobel算子的孔径大小
double k, // Harris参数
int boderType=BORDER_DEFAULT//图像像素边界模式
);
转自:http://blog.csdn.net/dandan_397/article/details/42110719
1. 首先,我们不禁要问什么是harris角点?
对于角点,到目前为止还没有明确的数学定义。但是你可以认为角点就是极值点,即在某方面属性特别突出的点。一般的角点检测都是对有具体定义的、或者是能够具体检测出来的兴趣点的检测。这意味着兴趣点可以是角点,是在某些属性上强度最大或者最小的孤立点、线段的终点,或者是曲线上局部曲率最大的点。
通俗的来说,在一副图像中,我们可以认为角点是物体轮廓线的连接点(见图1),当拍摄视角变化的时候,这些特征点仍能很好地保持稳定的属性。
图1 corner
角点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。它的各种应用,这里我就不再赘述了。
2. 如何检测出harris角点?
图2 角点检测的基本思想
角点检测最原始的想法就是取某个像素的一个邻域窗口,当这个窗口在各个方向上进行小范围移动时,观察窗口内平均的像素灰度值的变化(即,E(u,v),Window-averaged change of intensity)。从上图可知,我们可以将一幅图像大致分为三个区域(‘flat’,‘edge’,‘corner’),这三个区域变化是不一样的。
其中,u,v是窗口在水平,竖直方向的偏移,
这里可以先简单复习一下泰勒级数展开的知识,因为马上就用到啦,
这是一维的情况,对于多元函数,也有类似的泰勒公式。
对I(x+u,y+v)进行二维泰勒级数展开,我们取一阶近似,有
图中蓝线圈出的部分我们称之为结构张量(structure tensor),用M表示。
讲到这里,先明确一点,我们的目的是什么?我们的目的是寻找这样的像素点,它使得我们的u,v无论怎样取值,E(u,v)都是变化比较大的,这个像素点就是我们要找的角点。不难观察,上式近似处理后的E(u,v)是一个二次型,而由下述定理可知,
令E(u,v)=常数,我们可用一个椭圆来描绘这一函数。
椭圆的长短轴是与结构张量M的两个特征值相对应的量。通过判断的情况我们就可以区分出‘flat’,‘edge’,‘corner’这三种区域,因为最直观的印象:
corner:在水平、竖直两个方向上变化均较大的点,即Ix、Iy都较大;
edge :仅在水平、或者仅在竖直方向有较大的点,即Ix和Iy只有其一较大 ;
flat : 在水平、竖直方向的变化量均较小的点,即Ix、Iy都较小;
而结构张量M是由Ix,Iy构成,它的特征值正好可以反映Ix,Iy的情况,下面我以一种更容易理解的方式来讲述椭圆的物理意义。
这样是不是更清楚了呢^_^......,因此我们可以得出结论:
当然,大牛们并没有止步于此,这样通过判断两个变量的值来判断角点毕竟不是很方便。于是他们想出了一种更好的方法,对,就是定义角点响应函数R(corner response function),
针对三种区域,R的取值如何呢?
至此,我们就可以通过判断R的值来判断某个点是不是角点了。
3. harris角点检测算法步骤
值得注意的是,在实际情况中,用来判断R的阈值依赖于实际图像的尺寸、纹理等因素,由于其不具有直观的物理意义,它的取值很难确定。通常我们采用间接的方法来判断R:通过选择图像中R值最大的前n个像素点作为特征点,再对提取到的特征点进行K*K邻域的非极大抑制处理就可以了。
综合示例 :harris角点检测与绘制
//-----------------------【harris角点检测与绘制】-----------------------------------------------------
// 描述:是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。
// 缺点:运算速度慢(高斯滤波),角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
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;//最大阈值
//--------------------【全局函数声明部分】----------------
// 描述:全局函数声明
//--------------------------------------------------------
void on_CornerHarris(int , void*);
//--------------------【main()函数】-----------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {
//【1】载入原图
g_srcImage = imread("lenna.jpg");
if (!g_srcImage.data) { printf("读取图片错误 \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);
//【4】调用一次回调函数,进行初始化
on_CornerHarris(0, 0);
waitKey(0);
return 0;
}
//--------------------------【on_CornerHarris()函数】----------------------
// 描述:鼠标消息回调函数
//-----------------------------------------------------------------------
void on_CornerHarris(int, void*) {
//定义一些局部变量
Mat dstImage;//目标图
Mat normImage;//归一化后的图
Mat scaledImage;//显现变换后的八位无符号整形的图
//置零当前需要的两幅图,即清除上一次调用此函数时的值
dstImage = Mat::zeros(g_srcImage.size(),CV_32FC1);
g_srcImage1 = g_srcImage.clone();
//【1】进行角点检测
cornerHarris(g_grayImage,dstImage,2,3,0.04,BORDER_DEFAULT);
//归一化与转换
normalize(dstImage,normImage,0,255,NORM_MINMAX,CV_32FC1);
convertScaleAbs(normImage,scaledImage);//将归一化的图线性变换成8位无符号整形
//【2】进行绘制
//将检测到的且符合阈值条件的角点绘制出来
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);
}
}
}
//显示结果
imshow(WINDOW_NAME1,g_srcImage1);
imshow(WINDOW_NAME2,scaledImage);
}
2、【Shi-Tomasi角点检测与绘制】
确定图像强角点:goodFeaturesToTrack()函数-----用于确定图像的强角点
void goodFeaturesToTrack(
InputArray image, //输入图像
OutputArray corners, //检测到的焦点的输出向量
int maxCorners, //角点的最大数量
double qualityLevel, //角点检测可接受的最小特征值
double minDistance, //角点之间的最小距离
InputArray mask=noArray(),//表示感兴趣区域
int blockSize=3, //(默认3)是计算导数自相关矩阵时指定的领域范围
bool uesHarrisDetector=false,//默认false,指示是否使用Harris角点检测
double k=0.04 //(默认0.04)为用于设置Hessian自相关矩阵行列式的相对权重的权重系数。
);
综合示例:Shi-Tomasi角点检测与绘制
//-----------------------【Shi-Tomasi角点检测与绘制】-----------------------------------------------------
// 描述:Harris的改进算法
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#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);
//--------------------【全局函数声明部分】----------------
// 描述:全局函数声明
//--------------------------------------------------------
void on_goodCornersToTrack(int, void*);
//--------------------【main()函数】-----------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {
//【1】载入原图
g_srcImage = imread("lenna.jpg");
if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
//【3】创建滚动条
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_goodCornersToTrack);
imshow(WINDOW_NAME,g_srcImage);
//【4】调用一次回调函数,进行初始化
on_goodCornersToTrack(0, 0);
waitKey(0);
return 0;
}
//--------------------------【on_goodCornersToTrack()函数】----------------------
// 描述:回调函数
//-----------------------------------------------------------------------
void on_goodCornersToTrack(int, void*) {
//【1】对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【2】Shi-Tomsai算法的参数准备
vector<Point2f>corners;
double qualityLevel = 0.01;//角点检测可接受的最小特征值
double minDistance = 10;//角点之间的最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat copy = g_srcImage.clone();
//【3】进行角点检测
goodFeaturesToTrack(g_grayImage,corners,g_maxCornerNumber,qualityLevel,minDistance,Mat(),blockSize,false,k);
//【4】输出文字信息
cout << ">此次检测到的角点数量为:" << 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);
}
3、【亚像素级角点检测】
亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重建被跟踪目标的三维结构时,是一个基本的测量值。
寻找亚像素角点:cornerSubPix()函数-----用于寻找亚像素焦点位置
void cornerSubPix(
InputArray image, //输入图像
InputOutputArray corners, //提供输入角点的初始坐标和精准的输出坐标
Size winSize, //搜索窗口的一般尺寸
Size zeroZone, //死区的一般尺寸
TermCriteria criteria //求角点的迭代过程的终止条件
);
综合示例:亚像素级角点检测与绘制
//-----------------------【亚像素级角点检测与绘制】-----------------------------------------------------
// 描述:Harris的改进算法
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#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);
//--------------------【全局函数声明部分】----------------
// 描述:全局函数声明
//--------------------------------------------------------
void on_goodCornersToTrack(int, void*);
//--------------------【main()函数】-----------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------------
int main(int argc, char** argv) {
//【1】载入原图
g_srcImage = imread("lenna.jpg");
if (!g_srcImage.data) { printf("读取图片错误 \n"); return false; }
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
//【3】创建滚动条
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_goodCornersToTrack);
imshow(WINDOW_NAME, g_srcImage);
//【4】调用一次回调函数,进行初始化
on_goodCornersToTrack(0, 0);
waitKey(0);
return 0;
}
//--------------------------【on_goodCornersToTrack()函数】----------------------
// 描述:回调函数
//-----------------------------------------------------------------------
void on_goodCornersToTrack(int, void*) {
//【1】对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【2】Shi-Tomsai算法的参数准备
vector<Point2f>corners;
double qualityLevel = 0.01;//角点检测可接受的最小特征值
double minDistance = 10;//角点之间的最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat copy = g_srcImage.clone();
//【3】进行角点检测
goodFeaturesToTrack(g_grayImage, corners, g_maxCornerNumber, qualityLevel, minDistance, Mat(), blockSize, false, k);
//【4】输出文字信息
cout << ">此次检测到的角点数量为:" << 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(TermCriteria::EPS+TermCriteria::MAX_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;
}
}