快速检测特征点 - sumpig/OpenCV GitHub Wiki
FAST 检测器
// 关键点的向量
std::vector<cv::KeyPoint> keypoints;
// FAST 特征检测器,阈值为40
cv::Ptr<cv::FastFeatureDetector> ptrFAST =
cv::FastFeatureDetector::create(40);
// 检测关键点
ptrFAST->detect(image,keypoints);
OpenCV 也提供了在图像上画关键点的通用函数:
cv::drawKeypoints(image, // 原始图像
keypoints, // 关键点的向量
image, // 输出图像
cv::Scalar(255,255,255), // 关键点的颜色
cv::DrawMatchesFlags::DRAW_OVER_OUTIMG); // 画图标志
用一个负数作为关键点颜色,画每个圆时会随机选用不同的颜色。
原理
FAST 对角点的定义基于候选特征点周围的图像强度值。以某个点为中心做一个圆,根据圆上的像素值判断该点是否为关键点。如果存在这样一段圆弧,它的连续长度超过周长的3/4,并且它上面所有像素的强度值都与圆心的强度值明显不同(全部更暗或更亮),那么就认定这是一个关键点。
算法还用了一个技巧来进一步提高处理速度。如果我们测试圆周上相隔90 度的四个点(例如取上、下、左、右四个位置),就很容易证明:为了满足前面的条件,其中必须有三个点都比圆心更亮或都比圆心更暗。如果不满足该条件,就可以立即排除这个点,不需要检查圆周上的其他点。
这里用来预测试的像素是1、5、9 和13,至少需要9 个比圆心更暗(或更亮)的连续像素。这种设置通常称为FAST-9 角点检测器,也是OpenCV 默认采用的方法。
可以在构建检测器实例时指定FAST 检测器的类型,也可以用setType 方法指定。可选的类型有cv::FastFeatureDetector::TYPE_5_8、cv::FastFeatureDetector::TYPE_7_12 以及cv::FastFeatureDetector::TYPE_9_16。
一个点与圆心强度值的差距必须达到一个指定的值,才能被认为是明显更暗或更亮;这个值就是创建检测器实例时指定的阈值参数。这个阈值越大,检测到的角点数量就越少。
至于Harris 特征,通常最好在发现的角点上执行非最大值抑制。因此,需要定义一个角点强度的衡量方法。有多种衡量方法可供选择,下面介绍的是实际选用的方法——计算中心点像素与认定的连续圆弧上的像素的差值,然后将这些差值的绝对值累加,就能得到角点强度。可以从 cv::KeyPoint 实例的response 属性获取角点强度。
扩展
在事先明确兴趣点数量的情况下,可以对检测过程进行动态适配。简单的做法是采用范围较大的阈值检测出很多兴趣点,然后从中提取出 n 个强度最大的。
if (numberOfPoints < keypoints.size())
std::nth_element(keypoints.begin(),
keypoints.begin() + numberOfPoints,
keypoints.end(),
[](cv::KeyPoint& a, cv::KeyPoint& b) {
return a.response > b.response; });
函数中 keypoints 的类型是 std::vector,表示检测到的兴趣点,numberOfPoints 是需要的兴趣点数量。最后一个参数是 lambda 比较器,用于提取最佳的兴趣点。
检测图像特征点时还会遇到一种情况,就是兴趣点的分布很不均匀。。对此有一种常用的处理方法,就是把图像分割成网格状,对每个小图像进行单独检测。以下代码就是网格适配特征检测:
// 最终的关键点容器
keypoints.clear();
// 检测每个网格
for (int i = 0; i < vstep; i++)
for (int j = 0; j < hstep; j++) {
// 在当前网格创建ROI
imageROI = image(cv::Rect(j*hsize, i*vsize, hsize, vsize));
// 在网格中检测关键点
gridpoints.clear();
ptrFAST->detect(imageROI, gridpoints);
// 获取强度最大的FAST 特征
auto itEnd(gridpoints.end());
if (gridpoints.size() > subtotal) {
// 选取最强的特征
std::nth_element(gridpoints.begin(),
gridpoints.begin() + subtotal,
gridpoints.end(),
[](cv::KeyPoint& a,
cv::KeyPoint& b) {
return a.response > b.response; });
itEnd = gridpoints.begin() + subtotal;
}
// 加入全局特征容器
for (auto it = gridpoints.begin(); it != itEnd; ++it) {
// 转换成图像上的坐标
it->pt += cv::Point2f(j*hsize, i*vsize);
keypoints.push_back(*it);
}
}
OpenCV2 中有专门的封装了适配特征检测方法的类,例如 cv::DynamicAdaptedFeatureDetector 和 GridAdaptedFeatureDetector。