本文要点总结(俩算法的联系与区别)
Harris角点检测与Shi-Tomasi角点检测都是经典的角点特征提取算法,
但两者在API的使用上有出入(详见文中代码或GitHub项目);
-
Harris角点检测的API,
返回/输出
的是一个与输入图像大小一致的Mat对象
,
这个Mat对象
的每一个坐标(i,j)
都是对应输入图像对应坐标(i,j)
的像素
的响应值R
,
要先将这个Mat对象
归一化,
再循环每一个Mat数据元素
,一 一 跟自己设置的阈值
进行比较,
合格的再认为是角点
并提取
出来,
进行绘制和保存; -
与
Harris角点
输出不同,shi-tomasi
简单多了,
直接输出一个包含若干个(具体个数通过API形参设置)角点坐标的角点数组
,(其数据类型是MatOfPoint
)
省略了很多步骤;
遍历
这个角点数组,
绘制出
每个角点
即可。
引子
-
前面两章笔记(
图像操作、基本特征检测
)
主要讲述了OpenCV中图像处理模块的主要知识与API使用; -
本章的笔记记录OpenCV中另外一个重要模块——
feature2d模块
,
该模块的主要功能是检测图像的特征
,
并根据特征进行对象匹配
; -
首先,关于
图像的特征
,
简单地说,特征就是边缘、角点、纹理
等。 -
本章会笔记
特征提取、检测与匹配
相关的知识与API,
包括角点特征检测、特征点检测、特征描述子提取
,
以及根据特征描述子去匹配、寻找特征对象
。 -
本章知识会涉及较多的数学知识与公式,
我们可以阅读一些与特征提取相关的数学知识
,
比如导数与微分、多项式与高斯公式曲线拟合,三角函数,矩阵的特征值与特征向量的简单计算
等基础数学知识,
以更好地掌握本章知识以及各个API参数意义与用法。
0 角点的定义与作用
基本特征检测
一章中,学习了关于边缘检测
的知识,
在图像边缘
中,有一些特殊的像素点值
得我们特别关注,
那就是图像边缘的角点
,
这些角点更能反映出图像中对象的整体特征
,
基于角点周围的像素块生成特征描述子
可以更好地表述图像特征数据
。
本文首先笔记如何提取图像的角点特征。
1 Harris角点检测
关于角点特征提取最经典
的算法之一就是Harris角点检测
。
Harris角点检测
的基本原理
是对图像求导
,对每个像素点生成二阶梯度图像
,
只是在卷积
核使用的时候需要使用高斯核
,
得到图像X与Y方向的二阶矩
,
基于它们就可以得到如下Hessian矩阵
:
求得最大两个特征值 λ1 与 λ2
,可以得到如下 角点响应值R
:
其中,系数K常见的取值范围为0.02~0.04。
每个像素点
有自己的一个响应值R
,
也即有自己的一对特征值 λ1 与 λ2;全局像素则有多个R值;
根据M计算
可以得到特征值 λ1、λ2
,它们的值与角点的关系如下图:
Harris角点检测
的API:
-
cornerHarris(Mat src, Mat dst, int blockSize, int ksize, double k)
src
:单通道的8位或者浮点数图像,用灰度图像;
dst
:输出的每个像素点的响应值
,是CV_32F
类型,大小与输入图像一致。
blockSize
:根据特征值与特征向量计算矩阵M的大小,常见取值为2
。
ksize Sobel
:算子梯度计算,常见取值为3
。
k
:系数大小,取值范围为0.02~0.04
。
使用Harris角点检测函数计算得到图像角点的演示代码如下:
private void harrisCornerDemo(Mat src, Mat dst) {
// 定义阈值T//初始化各种Mat对象
int threshold = 100;
Mat gray = new Mat();
Mat response = new Mat();
Mat response_norm = new Mat();
// 角点检测
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);//转灰度!!!!!!
Imgproc.cornerHarris(gray, response, 2, 3, 0.04);
Core.normalize(response, response_norm, 0, 255, Core.NORM_MINMAX, CvType.CV_32F);//归一化
// 绘制角点
dst.create(src.size(), src.type());
src.copyTo(dst);
float[] data = new float[1];//!!!!!!!!!!
for(int j=0; j<response_norm.rows(); j++ )
{
for(int i=0; i<response_norm.cols(); i++ )
{
response_norm.get(j, i, data);
if((int)data[0] > 100)
{
Imgproc.circle(dst, new Point(i, j), 5, new Scalar(0, 0, 255),
2, 8, 0);
Log.i("Harris Corner", "find corner point……");
}
}
}
gray.release();
response.release();
}
response_norm
归一化后的响应值Mat对象- data[0]是某个响应值;
>100
认为其是一个较大的响应值,
响应值大于指定阈值T(这里是100),则对应的像素点被认为是角点;float[] data = new float[1] //在这里,可能有人有疑问, 数组长度只有 1
get()
方法,第三个参数要求是数组,
get多个像素时,传入一个多元素空数组,常规理解操作;
但当只要get一个像素,则需创建一个只有一个元素的数组
!而非变量
!
这种接口设计思想
,
一个方法(如get()
)接口即可实现包含一到多个数据元素的形式参数的传入;
而没必要去准备/重载
两个方法——
一个用来接收包含单个数据元素的变量型形参
,
另一个用来接收包含多个数据元素的数组型形参
;没必要这样了;
即无论是负责接收数据的形参是包含 单个数据元素 还是 多个数据元素 ,一律按 数组型形参 处理,把数据形参位定义成数组类型,接口统一设计,一套代码,一个接口即可,省时省力!
- 上述程序首先把彩色RGB图像转换为单通道灰度图像,
然后使用Harris角点检测函数完成各个像素点上角点响应值的计算,
最后使用阈值过滤绘制那些响应值R比较大的像素点(角点)。
注意,阈值T与绘制检测得到的角点数目相关,
T值越大,被过滤的响应像素点越多,留下来的就越可能是角点
,反之亦然。
本章完整代码在文末GitHub里边的Feature2dMainActivity.java文件中
,后续对此不再说明。
2 Shi-Tomasi角点检测
还有一种经常使用的角点检测方法称为
Shi-Tomasi角点检测
,
其与Harris角点检测类似,这种方法同样是基于梯度图像
发展而来的,
它是1994年由两位作者Jianbo Shi与Carlo Tomasi一起提出来的,
他们当时所发表的论文名为<<Good Feature to Track>>,
这也是为什么在OpenCV中使用同名函数来表示Shi-Tomasi角点检测的原因。
Shi-Tomasi角点检测与Harris角点检测唯一(指的是方法逻辑,不包括API,API的输出还不同) 不同的地方
在于计算角点响应R值
时使用的是如下方法:
如果R大于指定阈值T,则对应的像素点被认为是角点;
假设λ1、λ2
为坐标,
则对角点
的描述就是当λ1、λ2都大于阈值T=λmin的右上角时
,
角点响应值满足要求的区域,
如下图:
相关的API如下:
-
goodFeaturesToTrack(Mat image, MatOfPoint corners, int maxCorners, double qualityLevel, double minDistance, Mat mask, int blockSize, boolean useHarrisDetector, double k)
image
:表示输入图像、类型为单通道的8位或浮点数,用灰度图像
;
corners
:输出得到角点数组,注意数据类型
;
maxCorners
:表示获取前N个
最强响应R值的角点。
qualityLevel
:其取值范围为0~1,这里取它与最大R值
相乘,得到的值作为阈值T
,低于它的都要被丢弃,
假设Rmax=1500,qualityLevel=0.01,则阈值T=15,小于15的角点都会被丢弃。
每个像素点有自己的一个响应值R,去全局像素最大的R为Rmax;
minDistance
:最终返回的角点
之间的最小距离,小于这个距离则的角点
被丢弃。
mask
:默认全部为零。
blockSize
:计算矩阵M时需要的,常取值为3。
useHarrisDetector
:是否使用Harris角点检测,true表示使用,若为false则使用Shi-Tomasi角点检测。
k
:当使用Harris角点检测的时候才使用。
实现shi-tomasi角点检测的demo:
private void shiTomasicornerDemo(Mat src, Mat dst) {
// 变量定义
double k = 0.04;
int blockSize = 3;
double qualityLevel= 0.01;
boolean useHarrisCorner = false;
// 角点检测
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
MatOfPoint corners = new MatOfPoint();
Imgproc.goodFeaturesToTrack(gray, corners, 100, qualityLevel, 10, new Mat(), blockSize, useHarrisCorner, k);
// 绘制角点
dst.create(src.size(), src.type());
src.copyTo(dst);
Point[] points = corners.toArray();
for(int i=0; i<points.length; i++) {
Imgproc.circle(dst, points[i], 5, new Scalar(0, 0, 255), 2, 8, 0);
}
gray.release();
}
完整的代码可参考GitHub项目。
参考材料
- 《OpenCV Android 开发实战》(贾志刚 著)
- 关于《OpenCV Android 开发实战》作者的GitHub项目
- 笔者基于作者GitHub维护的APP
网友评论