角点通常被定义为两条边的交点,或者说,角点的局部邻域应该具有两个不同区域的不同方向的边界。角点检测(Corner Detection)是计算机视觉系统中获取图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维重建和目标识别等,也可称为特征点检测。
1、角点的类型
2、角点检测算法的基本思想:
使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。
目前,角点检测算法还不是十分完善,许多算法需要依赖大量的训练集和冗余数据来防止和减少错误的特征的出现。对于角点检测算法的重要评价标准是:其对多幅图像中相同或者相似特征的检测能力,并且能够应对光照变化、或者图像旋转等影响。
关于角点的具体描述可以有几种:
三类角点检测算法:
1.1 原理分析
1.1.1 算法思想
角点原理来源于人对角点的感性判断,即图像在各个方向灰度有明显变化。算法的核心是利用局部窗口在图像上进行移动判断灰度发生较大的变化,所以此窗口用于计算图像的灰度变化为(3*3的窗口,这和计算梯度变化算子一样):[-1,0,1;-1,0,1;-1,0,1][-1,-1,-1;0,0,0;1,1,1]。根据下面三幅图可以清晰理解角点检测的过程:当一个窗口在图像上移动,如图(a),窗口在各个方向上都没有变化,则认为窗口区域为平滑区域。如图(b),窗口在某个方向上没有变化,另一个方向上有明显变化,那么,这块区域可能存在边缘。如图(c),窗口在各个方向上灰度发生了较大的变化,那么,这块区域可能存在角点。Harris角点检测正是利用了这个直观的物理现象,通过窗口在各个方向上的变化程度,决定是否为角点。
1.1.2 数学表示
根据算法思想,构建数学模型,计算移动窗口的的灰度差值。
为了减小计算量,利用泰勒级数进行简化公式:
上图中W函数表示窗口函数,M矩阵为偏导数矩阵。对于矩阵可以进行对称矩阵的变化,假设利用两个特征值进行替代,其几何含义类似下图中的表达。在几何模型中通过判断两个特征值的大小,来判定像素的属性。
M为梯度的协方差矩阵 ,在实际应用中为了能够应用更好的编程,定义了角点响应函数R,通过判定R大小来判断像素是否为角点。R取决于M的特征值,对于角点|R|很大,平坦的区域|R|很小,边缘的R为负值。Harris角点检测算法就是对角点响应函数R进行阈值处理:R > threshold,即提取R的局部极大值。
OpenCV里面的API:
C++:void cornerHarris( InputArray src, //输入8bit单通道灰度Mat矩阵
OutputArray dst, //用于保存Harris角点检测结果,32位单通道,大小与src相同
int blockSize, //滑块窗口的尺寸
int ksize, //Sobel边缘检测滤波器大小
double k, //Harris中间参数,经验值0.04~0.06
int borderType=BORDER_DEFAULT //插值类型
);
Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将矩阵 M 的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。
Shi 和Tomasi 的方法比较充分,并且在很多情况下可以得到比使用Harris 算法更好的结果。
OpenCV里面的API:
C++:void goodFeaturesToTrack( InputArray image, OutputArray corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray mask=noArray(), int blockSize=3,
bool useHarrisDetector=false, double k=0.04 );
参数详解:
第一个参数image:8位或32位单通道灰度图像;
第二个参数corners:位置点向量,保存的是检测到的角点的坐标;
第三个参数maxCorners:定义可以检测到的角点的数量的最大值;
第四个参数qualityLevel:检测到的角点的质量等级,角点特征值小于qualityLevel*最大特征值的点将被舍弃;
第五个参数minDistance:两个角点间最小间距,以像素为单位;
第六个参数mask:指定检测区域,若检测整幅图像,mask置为空Mat();
第七个参数blockSize:计算协方差矩阵时窗口大小;
第八个参数useHarrisDetector:是否使用Harris角点检测,为false,则使用Shi-Tomasi算子;
第九个参数k:留给Harris角点检测算子用的中间参数,一般取经验值0.04~0.06。第八个参数为false时,该参数不起作用;
当我们想要进行几何测量或者标定的时候势必要比目标识别需要更高的精度的特征点。而上面的goodFeaturesToTrack()只能得到整数的坐标值,这时候我们就需要亚像素级的角点检测来得到实数坐标值来满足精度需求。
亚像素级角点检测的位置摄像机标定,跟踪并重建摄像机的轨迹或者重建被跟踪目标的三维结构时,是一个基本的测量值。下面是将角点位置精确到亚像素精度的过程:
一个向量与其正交的向量的点积为0,角点满足上图所示情况。其中(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的API介绍:
C++:void cornerSubPix(
InputArray image, //输入图像
InputOutputArray corners,//输入角点的初始坐标和为输出的精确坐标
Size winSize, //搜索窗口边长的一半
Size zeroZone,//搜索区域中间的死区大小的一半,(-1,-1)表示没有这样的大小。
TermCriteria criteria //终止角点优化迭代的条件
);
示例程序:
//代码里面有三种程序
#include "stdafx.h"
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace std;
using namespace cv;
int main(int argv, char** argc)
{
Mat srcImage = imread("C:/Users/zhj/Desktop/image/template.bmp");
if (srcImage.empty())
{
printf("could not load image..\n");
return false;
}
Mat srcgray, dstImage, normImage,scaledImage;
cvtColor(srcImage, srcgray, CV_BGR2GRAY);
Mat srcbinary;
threshold(srcgray, srcbinary,0,255, THRESH_OTSU | THRESH_BINARY);
Mat kernel = getStructuringElement(MORPH_RECT, Size(15, 15), Point(-1, -1));
morphologyEx(srcbinary, srcbinary, MORPH_OPEN, kernel, Point(-1, -1));
/*
//1、Harris角点检测
cornerHarris(srcgray, dstImage, 3, 3, 0.01, BORDER_DEFAULT);
//归一化与转换
normalize(dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
convertScaleAbs(normImage, scaledImage);
Mat binaryImage;
threshold(scaledImage, binaryImage, 0, 255, THRESH_OTSU | THRESH_BINARY);
*/
//2、Shi-Tomasi算法:确定图像强角点
vector corners;//提供初始角点的坐标位置和精确的坐标的位置
int maxcorners = 200;
double qualityLevel = 0.01; //角点检测可接受的最小特征值
double minDistance = 10; //角点之间最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的领域范围
double k = 0.04; //权重系数
goodFeaturesToTrack(srcgray, corners, maxcorners, qualityLevel, minDistance, Mat(), blockSize, false, k);
//Mat():表示感兴趣区域;false:表示不用Harris角点检测
//输出角点信息
cout << "角点信息为:" << corners.size() << endl;
//绘制角点
RNG rng(12345);
for (unsigned i = 0; i < corners.size(); i++)
{
circle(srcImage, corners[i], 2, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), -1, 8, 0);
cout << "角点坐标:" << corners[i] << endl;
}
//3、寻找亚像素角点
Size winSize = Size(5, 5); //搜素窗口的一半尺寸
Size zeroZone = Size(-1, -1);//表示死区的一半尺寸
//求角点的迭代过程的终止条件,即角点位置的确定
TermCriteria criteria = TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40,0.001);
//TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
cornerSubPix(srcgray, corners, winSize, zeroZone, criteria);
//输出角点信息
cout << "角点信息为:" << corners.size() << endl;
//绘制角点
for (unsigned i = 0; i < corners.size(); i++)
{
circle(srcImage, corners[i], 2, Scalar(255,0,0), -1, 8, 0);
cout << "角点坐标:" << corners[i] << endl;
}
waitKey(0);
return(0);
}
效果图: (Shi-Tomasi角点检测效果图,以及角点数目、坐标值)
(亚像素级坐标位置)