Opencv计算机视觉编程攻略-第八节 检测兴趣点

发布于:2025-04-04 ⋅ 阅读:(27) ⋅ 点赞:(0)

目录

1.检测图像中的角点

2.快速检测特征

3.尺度不变特征的检测

4.多尺度FAST 特征的检测


  在计算机视觉领域,兴趣点(也称关键点或特征点)应用包括目标识别、图像配准、视觉跟踪、三维重建等。这个概念的原理是,从图像中选取某些特征点并对图像进行局部分析(即提取局部特征),而非观察整幅图像(即提取全局特征)。只要图像中有足够多可检测的兴趣点,并且这些兴趣点各不相同且特征稳定、能被精确地定位,上述方法就十分有效。
  因为要用于图像内容的分析,所以不管图像拍摄时采用了什么视角、尺度和方位,理想情况下同一个场景或目标位置都要检测到特征点。视觉不变性是图像分析中一个非常重要的属性,目前有大量关于它的研究。

1.检测图像中的角点


  角点是很容易在图像中定位的局部特征,并且大量存在于人造物体中(例如墙壁、门、窗户、桌子等)。角点的价值在于它是两条边缘线的接合点,是一种二维特征,可以被精确地检测(即使是亚像素级精度)。与此相反的是位于均匀区域或物体轮廓上的点,这些点在同一物体的不同图像上很难重复精确定位。

        Harris 特征检测方法在假定的兴趣点周围放置了一个小窗口,并观察窗口内某个方向上强度值的平均变化。如果位移向量为(u, v),那么可以用均方差之和表示强度的变化:

       累加的范围是该像素周围一个预先定义的邻域(邻域的尺寸取决于cv::cornerHarris 函数的第三个参数)。在所有方向上计算平均强度变化值,如果不止一个方向的变化值很高,就认为这个点是角点。根据这个定义,Harris 测试的步骤应为:首先获得平均强度值变化最大的方向,然后检查垂直方向上的平均强度变化值,看它是否也很大;如果是,就说明这是一个角点。
  OpenCV 中检测Harris 角点的基本函数是cornerHarris, 调用该函数时输入一幅图像,返回的结果是一个浮点数型图像,其中每个像素表示角点强度。然后对输出图像阈值化,以获得检测角点的集合。

// 检测Harris 角点
cv::Mat cornerStrength;
cv::cornerHarris(image, // 输入图像
        cornerStrength, // 角点强度的图像
        3, // 邻域尺寸
        3, // 口径尺寸
        0.01); // Harris 参数
// 对角点强度阈值化
cv::Mat harrisCorners;
double threshold= 0.0001;
cv::threshold(cornerStrength,harrisCorners,threshold,255,cv::THRESH_BINARY);

// 检测Harris 角点需要两个步骤。
//首先是计算每个像素的Harris 值:
cv::cornerHarris(image,cornerStrength,
        neighbourhood,// 邻域尺寸
        aperture, // 口径尺寸
        k); // Harris 参数
// 计算内部阈值
cv::minMaxLoc(cornerStrength,0,&maxStrength);
// 检测局部最大值
cv::Mat dilated; // 临时图像
cv::dilate(cornerStrength,dilated,cv::Mat());
cv::compare(cornerStrength,dilated, localMax, cv::CMP_EQ);

// 然后,用指定的阈值获得特征点。因为Harris 值的可选范围取决于选择的参数,所以阈值被
// 作为质量等级,用最大Harris 值的一个比例值表示:
cv::Mat getCornerMap(double qualityLevel) {
    cv::Mat cornerMap;
    // 对角点强度阈值化
    threshold= qualityLevel*maxStrength;
    cv::threshold(cornerStrength,cornerTh, threshold, 255,
    cv::THRESH_BINARY);
    // 转换成8 位图像
    cornerTh.convertTo(cornerMap,CV_8U);
    // 非最大值抑制
    cv::bitwise_and(cornerMap,localMax,cornerMap);
    return cornerMap;
}

        得到一个被检测特征的二值角点分布图。因为Harris 特征的检测过程分为两个方法,所以我们可以用不同的阈值来测试检测结果(直到获得适当数量的特征点),而不必重复进行耗时的计算过程。

       为了提升检测效果,前面介绍的类增加了一个额外的非最大值抑制步骤,作用是排除掉紧邻
的Harris 角点。因此,Harris 角点不仅要有高于指定阈值的评分,还必须是局部范围内的最大值。
为了检查这个条件,detect 方法中加入了一个小技巧,即对Harris 评分的图像做膨胀运算:
cv::dilate(cornerStrength, dilated,cv::Mat());

        膨胀运算会在邻域中把每个像素值替换成最大值,因此只有局部最大值的像素是不变的。用
下面的相等测试可以验证这一点:cv::compare(cornerStrength, dilated, localMax,cv::CMP_EQ);
因此矩阵localMax 只有在局部最大值的位置才为真(即非零)。然后将它用于getCornerMap
方法中,排除掉所有非最大值的特征(用cv::bitwise 函数)。
       如何均匀获得角点,从Harris 值最强的点开始(即具有最大的最低特征值),只允许一定距离之外的点成为兴趣点。在OpenCV 中用good-features-to-track(GFTT)实现这个算法。这个算法得名于它检测的特征非常适合作为视觉跟踪程序的起始集合,

// 计算适合跟踪的特征
std::vector<cv::KeyPoint> keypoints;
// GFTT 检测器
cv::Ptr<cv::GFTTDetector> ptrGFTT =
cv::GFTTDetector::create(
500, // 关键点的最大数量
0.01, // 质量等级
10); // 角点之间允许的最短距离
// 检测GFTT
ptrGFTT->detect(image,keypoints);

2.快速检测特征

       Harris 算子对角点(或者更通用的兴趣点)做出了规范的数学定义,该定义基于强度值在两
个互相垂直的方向上的变化率。虽然这是一种看似很完美的定义,但它需要计算图像的导数,而
计算导数是非常耗时的,另一种特征点算子,叫作FAST(Features from Accelerated Segment Test,加速分割测试获得特征)。这种算子专门用来快速检测兴趣点——只需对比几个像素,就可以判断它是否为关键点。

// 关键点的向量
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); // 画图标志

        有一种比较有趣的做法,就是用一个负数作为关键点颜色。这样一来,画每个圆时会随机选
用不同的颜色。

       跟Harris 检测器的情况一样,FAST 特征算法源于“什么构成了角点”的定义。FAST 对角点的定义基于候选特征点周围的图像强度值。以某个点为中心做一个圆,根据圆上的像素值判断该点是否为关键点。如果存在这样一段圆弧,它的连续长度超过周长的3/4,并且它上面所有像素的强度值都与圆心的强度值明显不同(全部更暗或更亮),那么就认定这是一个关键点。

3.尺度不变特征的检测

       为解决尺度不变性,计算机视觉界引入了尺度不变特征的概念。它的理念是,不仅在任何尺度下拍摄的物体都能检测到一致的关键点,而且每个被检测的特征点都对应一个尺度因子。理想情况下,对于两幅图像中不同尺度的同一个物体点,计算得到的两个尺度因子之间的比率应该等于图像尺度的比率。近几年,人们提出了多种尺度不变特征,SURF 特征,它的全称为加速稳健特征(Speeded Up Robust Feature)

       SURF 特征检测属于opencv_contrib 库,在编译OpenCV 时包含了附加模块才能使用

        

// 创建SURF 特征检测器对象
cv::Ptr<cv::xfeatures2d::SurfFeatureDetector> ptrSURF =
cv::xfeatures2d::SurfFeatureDetector::create(2000.0);
// 检测关键点
ptrSURF->detect(image, keypoints);
为了画出这些特征,再次使用OpenCV的cv::drawKeypoints 函数,但是要采用cv::Draw
MatchesFlags::DRAW_RICH_KEYPOINTS 标志以显示相关的尺度因子:
// 画出关键点,包括尺度和方向信息
cv::drawKeypoints(image, // 原始图像
keypoints, // 关键点的向量
featureImage, // 结果图像
cv::Scalar(255,255,255), // 点的颜色
cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

     如果在不同的尺度内用高斯滤波器计算指定像素的拉普拉斯算子,会得到不同的数值。观察滤波器对不同尺度因子的响应规律,所得曲线最终在给定的σ 值处达到最大值。对于以不同尺度拍摄的两幅图像的同一个物体,对应的两个σ 值的比率等于拍摄两幅图像的尺度的比率。这一重要观察是尺度不变特征提取过程的核心。也就是说,为了检测尺度不变特征,需要在图像空间(图像中)和尺度空间(通过在不同尺度下应用导数滤波器得到)分别计算局部最大值。

4.多尺度FAST 特征的检测

  BRISK(Binary Robust Invariant Scalable Keypoints,二元稳健恒定可扩展关键点)检测法,它基于上一节介绍的FAST 特征检测法,另一种检测方法ORB(Oriented FAST and Rotated BRIEF,定向FAST 和旋转BRIEF)。在需要进行快速可靠的图像匹配时,这两种特征点检测法是非常优秀的解决方案。

    BRISK 不仅是一个特征点检测器,它还包含了描述每个被检测关键点的邻域的过程,为了在不同尺度下检测兴趣点,该算法首先通过两个下采样过程构建一个图像金字塔。第一个过程从原始图像尺寸开始,然后每一图层(八度)减少一半。第二个过程先将原始图像的尺寸除以1.5 得到第一幅图像,然后在这幅图像的基础上每一层减少一半,两个过程产生的图层交替在一起。然后在该金字塔的所有图像上应用FAST 特征检测器,提取关键点的条件与SIFT 算法类似。首先,将一个像素与相邻的八个像素之一进行强度值的比较,只有是局部最大值的像素才可能成为关键点。这个条件满足后,比较这个点与上下两层的相邻像素的评分;如果它的评分在尺度上也更高,那么就认为它是一个兴趣点。

// 构造BRISK 特征检测器对象
cv::Ptr<cv::BRISK> ptrBRISK = cv::BRISK::create();
// 检测关键点
ptrBRISK->detect(image, keypoints);