《Opencv3编程入门》学习笔记
记录一下在学习《Opencv3编程入门》这本书时遇到的问题或重要的知识点。
第十章 角点检测
一、Harris角点检测
(一)兴趣点与角点
1、图像特征类型:
- 边缘
- 角点(感兴趣点)
- 斑点(感兴趣区域)
2、角点定义:
- 一阶导数(灰度的梯度)的局部最大所对应的像素点
- 两条及两条以上边缘的交点
- 图像中梯度值和梯度方向的变化速率都很高的点
- 角点处的一阶导数最大,二阶导数为0,指示了物体边缘变化不连续的方向
(二)角点检测
3、角点检测算法:
(1)基于灰度图像的角点检测
- 基于梯度
- 基于模板
考虑像素邻域点的灰度变化,将与邻域点亮度对比足够大的点定义为角点
常见算法:Kitchen-Rosenfeld角点检测、Harris角点检测、KLT角点检测、SUSAN角点检测 - 基于模板梯度组合
(2)基于二值图像的角点检测
(3)基于轮廓曲线的角点检测
(三)harris角点检测
harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。但由于采用了高斯滤波,运算速度相对较慢,焦点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象。
(四)实现Harris角点检测:cornerHarris()函数
1、作用:
基于灰度图像的角点提取算法,运行Harris角点检测算子进行角点检测
2、原理:
和cornerMinEigenVal()以及cornerEigenValsAndVecs()函数类似,cornerHarris函数对于每一个像素(x,y)在blockSize*blockSize邻域内,计算2 * 2梯度的协方差矩阵M(x,y),然后找出输出图中局部最大值,即找出角点:
3、函数原型:
void cornerHarris(InputArray src, OutputArray dst, int blockSize, int ksize, double k, intborderType=BORDER_DEFAULT)
4、参数说明:
(1)输入图像,单通道8位或浮点型图像
(2)存放Harris角点检测输出结果,与原图像一样尺寸和类型
(3)邻域大小
(4)Sobel()算子孔径大小
(5)Harris参数
(6)图像像素边界模式,默认BORDER_DEFAULT
示例代码
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
//载入灰度图像并显示
Mat srcImage = imread("D://lili/Desktop/jpg/opencv/9.jpg", 0);
imshow("【原始图】", srcImage);
//进行Harris角点检测
Mat cornerStrength;
cornerHarris(srcImage, cornerStrength, 2, 3, 0.01);
//对灰度图进行阈值操作,得到二值图并显示
Mat harrisCorner;
threshold(cornerStrength, harrisCorner, 0.00001, 255, THRESH_BINARY);
imshow("【角点检测后的二值效果图】", harrisCorner);
waitKey(0);
return 0;
}
运行效果
(五)综合示例:harris角点检测与绘制
示例代码
/*
程序说明:滑动条调节阈值,以控制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_NAME1 "【原始图窗口】"
#define WINDOW_NAME2 "【程序窗口1】"
#define WINDOW_NAME3 "【程序窗口2】"
//全局变量
Mat g_srcImage, g_srcImage1, g_grayImage;
int thresh = 30;
int max_thresh = 175;
//全局函数
void on_CornerHarris(int, void*);
int main()
{
//载入原图
g_srcImage = imread("D://lili/Desktop/jpg/opencv/6.jpg");
if (!g_srcImage.data)
{
printf("载入原图失败\n");
return false;
}
imshow(WINDOW_NAME1, g_srcImage);
g_srcImage.copyTo(g_srcImage1);
cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
//创建滑动条并初始化
namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
createTrackbar("阈值:", WINDOW_NAME2, &thresh, max_thresh, on_CornerHarris);
on_CornerHarris(0, 0);
waitKey(0);
return 0;
}
void on_CornerHarris(int, void*)
{
//局部变量
Mat dstImage;//目标图
Mat normImage;//归一化图
Mat scaledImage;//线性变换后的八位无符号整型图
//初始化
//置零当前需要显示的两幅图,即清除上一次调用此函数时的值
dstImage = Mat::zeros(g_srcImage.size(), CV_32FC1);
g_srcImage.copyTo(g_srcImage1);
//角点检测
cornerHarris(g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT);
//归一化与转换
normalize(dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
convertScaleAbs(normImage, scaledImage);//将归一化后的图线性变换成8位无符号整型
//将检测到的,符合阈值条件的角点绘制出来
for (int i = 0; i < normImage.rows; i++)
{
for (int j = 0; j < normImage.cols; j++)
{
if ((int)normImage.at<float>(i, j) > thresh + 80)
{
circle(g_srcImage1, Point(j, i), 5, Scalar(10, 10, 255), 2, 8, 0);
circle(scaledImage, Point(j, i), 5, Scalar(0, 10, 255), 2, 8, 0);
}
}
}
imshow(WINDOW_NAME2, g_srcImage1);
imshow(WINDOW_NAME3, scaledImage);
}
运行效果
二、Shi-Tomasi角点检测
(一)Shi-Tomasi角点检测概述
Shi-Tomasi算法是Harris算法的改进
(二)确定图像强角点:goodFeaturesToTrack()函数
1、定义:
Harris算法改进,若矩阵M行列式的值与M的迹中较小的一个大于最小阈值,则会得到强角点
2、作用:
确定图像的强角点,可用来初始化一个基于点的对象跟踪操作
3、函数原型:
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)
4、参数说明:
(1)输入图像,8位或浮点型32位单通道图像
(2)检测到的角点输出向量
(3)角点最大数量
(4)角点检测可接受的最小特征值为qualityLevel与图像中最大特征值的乘积,qualityLevel通常取0.1或0.01
(5)角点之间的最小距离,返回的角点之间距离不小于minDistance个像素
(6)可选参数,表示感兴趣区域,默认noArray(),参数非空则指定角点检测区域
(7)计算导数自相关矩阵时指定的范围,默认3
(8)指示是否使用Harris角点检测,默认false
(9)用于设置Hessian自相关矩阵行列式的相对权重的权重系数,默认0.04
(三)综合示例:Shi-Tomasi角点检测
示例代码
#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 "【Shi-Tomasi角点检测】"
//全局变量
Mat g_srcImage, g_grayImage,g_dstImage;
int g_maxCornerNumber = 33;//角点检测最大数量
int g_maxTrackbarNumber = 500;
RNG g_rng(12345);
//全局函数
void on_GoodFeaturesToTrack(int, void*);
int main()
{
cout << "\n\n\t欢迎来到【Shi-Tomasi角点检测】示例程序\n" << endl;
//载入原图像
g_srcImage = imread("D://lili/Desktop/jpg/opencv/6.jpg", 1);
cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME, g_srcImage);
//创建滑动条,回调函数初始化
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
on_GoodFeaturesToTrack(0, 0);
waitKey(0);
return 0;
}
void on_GoodFeaturesToTrack(int, void*)
{
//对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//Shi-Tomasi算法的参数准备
vector<Point2f>corners;
double qualityLevel = 0.01;//角点检测可接受最小特征值
double minDistance = 10;//角点之间最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat g_dstImage = g_srcImage.clone();
//进行Shi-Tomasi角点检测
goodFeaturesToTrack(g_grayImage, corners, g_maxCornerNumber, qualityLevel, minDistance, Mat(), blockSize, false, k);
//输出文字信息
cout << "\t>此次检测到的角点数量为:" << corners.size() << endl;
//绘制检测到的角点
int r = 4;
for (int i = 0; i < corners.size(); i++)
{
//以随机的颜色绘制出角点
circle(g_dstImage, corners[i], r, Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)), -1, 8, 0);
}
//显示窗口
imshow(WINDOW_NAME, g_dstImage);
}
运行效果
三、亚像素级角点检测
(一)背景概述
1、原理:
假设起始角点在实际亚像素角点附近,检测所有q-p向量,下面两种情况点p梯度与q-p向量的点积均为0:(1)点p附近图像均匀,即点p处梯度为0(2)边缘的梯度与沿边缘方向的q-p向量正交(向量与其正交向量点积为0),如下图:
则可以在p点周围找到很多组梯度以及相关向量q-p,令其点积为0,求解方程组,解即为角点q的亚像素级精度位置。
2、作用:
将角点位置精确到亚像素级精度(精确的浮点类型位置),可用于几何测量、摄像机标定、跟踪并重建摄像机的轨迹。
(二)寻找亚像素级角点:cornerSubPix()函数
寻找亚像素级角点
void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)
(1)输入图像
(2)输入角点的初始坐标和精确的输出坐标
(3)搜索窗口一半,若 winSize=Size(5,5),表示使用(52+1)(52+1)=1111大小窗口
(4)死区一半,死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性,值(-1,-1)表示没有死区
(5)求角点的迭代过程的终止条件。即角点位置的确定,迭代数大于或精确达到某设定值,此参数为最大迭代数或精确度或它们的组合
(三)综合示例:亚像素级角点检测
示例代码
/*
程序说明:在Shi-Tomasi角点检测示例程序基础上,在on_GoodFeaturesToTrack()回调函数中加上亚像素角点检测代码,完成亚像素级角点检测
*/
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
//定义辅助宏
#define WINDOW_NAME "【亚像素级角点检测】"
//全局变量
Mat g_srcImage, g_grayImage, g_dstImage;
int g_maxCornerNumber = 30;
int g_maxTrackbarNumber = 500;
RNG g_rng(12345);
//全局函数
void on_GoodFeaturesToTrack(int, void*);
void ShowHelpText();
int main()
{
cout << "\n\n\t欢迎来到【亚像素角点检测】示例程序\n" << endl;
//载入原图
g_srcImage = imread("D://lili/Desktop/jpg/opencv/6.jpg",1);
cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME, g_srcImage);
//创建滑动条,回调函数初始化
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
on_GoodFeaturesToTrack(0, 0);
waitKey(0);
return 0;
}
void on_GoodFeaturesToTrack(int, void*)
{
//对变量小于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【1】Shi-Tomasi算法参数设置
vector<Point2f>corners;
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
double k = 0.04;
g_dstImage = g_srcImage.clone();
//进行Shi-Tomasi角点检测
goodFeaturesToTrack(g_grayImage, corners, g_maxCornerNumber, qualityLevel, minDistance, Mat(), blockSize, false, k);
//输出文字信息
cout << "\t>此次检测到的角点数量为:" << corners.size() << endl;
//绘制角点
int r = 4;
for (int i = 0; i < corners.size(); i++)
{
//以随机颜色绘制出角点
circle(g_dstImage, corners[i], r, Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)), -1, 8, 0);
}
//显示更新窗口
imshow(WINDOW_NAME, g_dstImage);
//【2】亚像素角点检测参数设置
Size winSize = Size(5, 5);
Size zeroZone = Size(-1, -1);
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
//计算出亚像素角点位置
cornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);
//输出角点信息
for (int i = 0; i < corners.size(); i++)
{
cout << "\t>>精确角点坐标[" << i << "](" << corners[i].x << "," << corners[i].y << ")" << endl;
}
}
运行效果