编程环境:
VS + OpenCV + C++
完整代码已经更新至GitHub,欢迎fork~GitHub链接
声明:创作不易,未经授权不得复制转载
statement:No reprinting without authorization
内容概述:
- Harris角点检测:实现Harris角点检测算法,并与OpenCV的cornerHarris函数的结果进行比较。
一、角点检测相关介绍
1、经典的Harris角点检测
基本算法步骤:
1.利用Soble计算出XY方向的梯度值得到Ix和Iy
2.计算出Ix2,Iy2,IxIy
3.(可选)利用高斯函数对Ix2,Iy2,IxIy进行滤波,或用全一窗口
4.计算局部特征结果矩阵M的特征值和响应函数的值
R(i,j)=Det(M)-k(trace(M))^2 (0.04<=k<=0.06)
5.将计算出响应函数的值C进行局部极大值抑制,滤除一些不是角点的点,防止重叠,同时要满足大于设定的阈值
2、改进的Shi-Tomasi角点检测
(1) 经典的Harris角点检测方法不难看出,该算法的稳定性和k有关,而k是个经验值,不好把握,浮动也有可能较大。鉴于此,改进的Harris方法()直接计算出两个特征值,通过比较两个特征值直接分类,这样就不用计算Harris响应函数了。
(2) 另一方面,我们不再用非极大值抑制了,而选取容忍距离,容忍距离内只有一个特征点:该算法首先选取一个具有最大最小特征值(把每个harris矩阵的最小特征值作为其衡量,认为其更有代表性)的点(即:max(min(e1,e2)),e1,e2是harris矩阵的特征值)作为角点,然后依次按照最大最小特征值顺序寻找余下的角点,当然和前一角点距离在容忍距离内的新角点被忽略,防止重叠。
在opencv中的实现为:goodFeatureTrack()
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 );
3、FAST角点检测算法
该算法检测的角点定义为在像素点的周围邻域内有足够多的像素点与该点处于不同的区域。应用到灰度图像中,即有足够多的像素点的灰度值大于该点的灰度值或者小于该点的灰度值,便是角点。算法原理比较简单,但实时性很强。
Eg:若某像素点圆形邻域圆周上有3/4的点和该像素点不同(编程时不超过某阈值th),则认为该点就是候选角点。(这一思路可以使用机器学习的方法进行加速。对同一类图像,例如同一场景的图像,可以在16个方向上进行训练,得到一棵决策树,从而在判定某一像素点是否为角点时,不再需要对所有方向进行检测,而只需要按照决策树指定的方向进行2-3次判定即可确定该点是否为角点。)
和Harris算法类似,该算法需要非极大值抑制。
补充:SUSAN 提取算子
基本原理是,与每一图像点相关的局部区域具有相同的亮度。如果某一窗口区域内的每一像元亮度值与该窗口中心的像元亮度值相同或相似,这一窗口区域将被称之为“USAN”。计算图像每一像元的“USAN”,为我们提供了是否有边缘的方法。位于边缘上的像元的“USAN”较小(像素值一般是连续变化的),位于角点上的像元的“USAN”更小。因此,我们仅需寻找最小的“USAN”,就可确定角点。该方法由于不需要计算图像灰度差,因此,具有很强的抗噪声的能力。
Fast在opencv中的实现:FastFeatureDetector fast(参数列表);
//示例代码
Mat image, image1 = cv::imread ("test.jpg");
cv::cvtColor (image1,image,CV_BGR2GRAY);
//快速角点检测
std::vector keypoints;
cv::FastFeatureDetector fast(40,true);
fast .detect (image,keypoints);
drawKeypoints(image,keypoints,image,cv::Scalar::all(255),cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
二、具体Harris角点检测实现介绍
1、图像的梯度计算:
直接调用opencv的sobel算子进行滤波:
Ix:Sobel(img, dst, CV_64FC1, 0, 1, 3);
Iy:Sobel(img, dst, CV_64FC1, 1, 0, 3);
2、Ixx、Iyy、Ixy的计算及局部特征结果矩阵M的特征值和响应函数的值:
通过第一步的大的Ix和Iy,分别遍历对应元素值进行相乘得到新的所需Mat,针对是否进行高斯的滤波(使用opencv库函数 GaussianBlur),设计两个计算的函数接口:
Gauss: Mat computeImage(Mat& ixx, Mat& iyy, Mat& ixy, int wsize);
NoGauss: Mat computeImage(Mat& ix, Mat& iy, int wsize, int para);
两种的Harris检测主要框架代码如下:
//使用全一的窗口函数
void myHarrisCorner_ave(Mat &srcImg) {
Mat image,src_grayImg,Ix,Iy,I_xx,I_yy,I_xy,R,filter_R,result;
cvtColor(srcImg, src_grayImg, COLOR_BGR2GRAY);
image = srcImg.clone();
int wsize = 3;//窗口大小
sobelGradient(src_grayImg, Ix,1);
sobelGradient(src_grayImg,Iy, 2);
I_xx = computeImage(Ix, Iy, wsize, 1);
I_yy = computeImage(Ix, Iy, wsize, 2);
I_xy = computeImage(Ix, Iy, wsize, 4);
//计算响应值
R = computeImage(Ix, Iy, wsize, 3);
//局部非极大值抑制
filter_R = filterR(R, 10);
mixP(filter_R, image, 2);
imshow("Ave", image);
imwrite("myHarris_Ave05.jpg", image);
}
//使用高斯平滑
void myHarrisCorner_Gauss(Mat &srcImg) {
Mat image, src_grayImg, Ix, Iy, I_xx, I_yy, I_xy, R, filter_R, result;
cvtColor(srcImg, src_grayImg, COLOR_BGR2GRAY);
image = srcImg.clone();
int wsize = 3;//窗口大小
sobelGradient(src_grayImg, Ix, 1);
sobelGradient(src_grayImg, Iy, 2);
I_xx = computeImage(Ix, Iy, wsize, 1);
GaussianBlur(I_xx, I_xx, Size(3, 3), 0, 0);
I_yy = computeImage(Ix, Iy, wsize, 2);
GaussianBlur(I_yy, I_yy, Size(3, 3), 0, 0);
I_xy = computeImage(Ix, Iy, wsize, 4);
GaussianBlur(I_xy, I_xy, Size(3, 3), 0, 0);
//计算响应值
R = computeImage(I_xx, I_yy,I_xy, wsize);
//局部非极大值抑制
filter_R = filterR(R, 10);
//显示结果
mixP(filter_R, image, 2);
imshow("Gauss", image);
imwrite("myHarris_gauss05.jpg", image);
三、四种方法的结果对比分析
对于opencv自带的Harris角点检测、改进的Shi-Tomasi角点检测、FAST角点检测算法、自己实现的Harri的使用高斯平滑以及不使用的五种输入同样的测试图片结果如下,每一种都调好参数后便固定不变测试对于不同大小角度的图片:(图片顺序为:①Fast、②Harris(opencv)、③myHarris(noGauss)、④myHarris(Gauss)、⑤ST)
1、第一组分辨率:692*910
2、第二组分辨率:240*319
3、第三组:340*256(翻转)
4、第四组:480*316
小结:
通过对上诉几组图片结果的观察发现,其中Shi-Tomasi的效果应该是最好的,角点标记的很全面,而且没有像Harris那样阈值很难调节,对不同图片变化很大(使用高斯的阈值大概为10^12很大!),而Fast得有点就在于简单速度快,但是精准不够,opencv库内的Harris角点检测个人感觉很难用,阈值很难调,而且可能是为了不检测出错,检测出来的角点数量比较少,效果不是很好。