opencv3学习笔记(十)------------------角点检测

【角点检测】
兴趣点也被称为关键点、特征点。他被大量用于解决物体识别、图像识别、图像匹配、视觉跟踪、三维重建等一系列问题中。
图像的特征类型分为:①边缘;②角点(感兴趣关键点);③斑点(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;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值