角点通常被定义为两条边的交点,或者说,角点的局部邻域应该具有两个不同区域的不同方向的边界。角点检测(Corner Detection)是计算机视觉系统中获取图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维重建和目标识别等,也可称为特征点检测。
使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。
目前,角点检测算法还不是十分完善,许多算法需要依赖大量的训练集和冗余数据来防止和减少错误的特征的出现。对于角点检测算法的重要评价标准是:其对多幅图像中相同或者相似特征的检测能力,并且能够应对光照变化、或者图像旋转等影响。
关于角点的具体描述可以有几种:
1.一阶导数(即灰度的梯度)的局部最大所对应的像素点;
2.两条及两条以上边缘的交点;
3.图像中梯度值和梯度方向的变化速率都很高的点;
4.角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
三类角点检测算法:
1.基于二值图像的角点检测;
2.基于轮廓曲线的角点检测;
3.基于灰度图像的角点检测:基于梯度、基于模板和基于模板和梯度组合三类方法;常见的基于模板的角点检测算法有:Kitchen-Rosenfeld角点检测算法,Harris角点检测算法,KLT角点检测算法及SUSAN角点检测算法。基于模板的方法主要是考虑像素领域点灰度的变化,即亮度的变化。
不过多赘述原理.
cornerHarris( InputArray src, //输入8bit单通道灰度Mat矩阵
OutputArray dst, //用于保存Harris角点检测结果,32位单通道,大小与
src相同
int blockSize, //滑块窗口的尺寸,一般取 2
int ksize, //Sobel边缘检测滤波器大小,一般取 3
double k, //Harris中间参数,一般取 0.04~0.06
int borderType=BORDER_DEFAULT //插值类型
);
Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将矩阵 M 的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。Shi 和Tomasi 的方法比较充分,并且在很多情况下可以得到比使用Harris 算法更好的结果
goodFeaturesToTrack( InputArray image, //8位或32位单通道灰度图像
OutputArray corners, //位置点向量,保存的是检测到的角点的坐标
int maxCorners, //定义可以检测到的角点的数量的最大值
double qualityLevel, //检测到的角点的质量等级,角点特征值小于
qualityLevel*最大特征值的点将被舍弃
double minDistance, //两个角点间最小间距,以像素为单位
InputArray mask=noArray(), //指定检测区域,若检测整幅图像,mask置为
空Mat()
int blockSize=3, //计算协方差矩阵时窗口大小
bool useHarrisDetector=false, //是否使用Harris角点检测,为false,则使用
Shi-Tomasi算子
double k=0.04 //留给Harris角点检测算子用的中间参数,一般
取经验值0.04~0.06。第八个参数为false时,该参数不起作用
);
当我们想要进行几何测量或者标定的时候势必要比目标识别需要更高的精度的特征点。而上面的goodFeaturesToTrack()只能得到整数的坐标值,这时候我们就需要亚像素级的角点检测来得到实数坐标值来满足精度需求。
亚像素级角点检测的位置摄像机标定,跟踪并重建摄像机的轨迹或者重建被跟踪目标的三维结构时,是一个基本的测量值。下面是将角点位置精确到亚像素精度的过程:
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);
}