快速检测特征点 - 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。