VPI - 视觉编程接口

3.2 版本

ORB 特征检测器

概述

Oriented FAST and rBRIEF (ORB) [1] 是一种特征检测器和描述符提取算法。它在输入金字塔的各个层级中检测特征或角点(也称为关键点),并为每个特征提取描述符,返回其坐标,包括找到特征的八度(即金字塔层级)以及与其关联的位串描述符。与其他特征检测器和描述符提取器(如 SIFT [2])相比,ORB 的优势在于其相对简单和计算效率。这种优势在实时视频处理和机器学习管道中非常重要。ORB 的主要缺点是,在描述角度和尺度发生变化的图像上的特征时,其鲁棒性不足。尽管 ORB 旨在处理这种情况,但在这一方面,它不如其他算法(如 SIFT 或 SURF [3])有效。

下面的示例显示了左侧的输入图像及其右侧对应的特征位置(八度大于零的特征被重新缩放到八度零或金字塔中的基本图像)。

输入输出

实现

ORB 算法使用 FAST 算法 在输入金字塔的每个层级(或八度)中检测特征或角点,也称为关键点。输入通常是高斯金字塔,它允许在基本图像的不同尺度上生成多个角点。

对于输入金字塔的每个层级,ORB 算法运行 FAST 以检测潜在的大量角点。之后,ORB 为检测到的每个 FAST 角点分配一个角点性评分。分配评分的一种方法是通过 HARRIS 算法,使用 Harris 响应评分,窗口大小为 3x3 块,灵敏度因子等于 1。然后,角点性评分用于对所有 FAST 角点从最高分到最低分进行排序,并由 ORB 过滤掉前 N 个检测到的角点,其中 N 可能远小于 FAST 找到的角点总数。另一种分配评分的方法是通过 FAST 本身,有效地跳过 Harris 响应评分分配和排序,从而以 ORB 检测到的角点的质量换取性能。

ORB 在输入金字塔的每个层级上检测到的角点都收集在单个输出数组中。角点存储输入金字塔上的 (x, y, octave) 位置,其中 octave 是找到角点的金字塔层级,(x, y) 是该金字塔层级图像内的位置。ORB 每个八度仅考虑一个层,因此 ORB 关键点始终在 关键点结构 中存储 layer=0。如果达到输出数组的最大容量,则可能会丢弃最终的、分辨率最低的层级中的角点。

ORB 为检测到的每个角点计算一个名为 rBRIEF 的描述符。这在输入金字塔的最高基本层级中完成。计算角点的 rBRIEF 描述符的第一步是计算其局部方向。这是通过计算角点与角点周围的patch的强度质心之间的角度来完成的。patch的强度质心定义为 \(m_{10}/m_{00}\) , \(m_{01}/m_{00}\),其中 \(m\_{pq}\) 定义为每个点 \((x,~y)\) 在patch中的 \(x^p * y^q * I(x, y)\)。考虑到这一点,我们可以将方向角定义为 \(atan2(m_{01},~m_{10})\)。

在确定每个角点的方向后,必须生成描述符。描述符是通过对角点周围的 patch 执行 256 个二进制测试,并将它们的结果组合成一个 256 位字符串生成的。每个二进制测试定义为:比较 patch 上的两个像素的强度,如果第一个像素的强度大于第二个像素,则设置值为 1;否则,设置值为 0。像素强度是在找到角点的金字塔层级收集的。这些测试的像素位置由一种模式确定,该模式可最大限度地减少相关性并增加方差。位置模式在进行测试之前按方向角旋转。这确保了描述符具有旋转不变性。

C API 函数

有关实现该算法的限制、约束和后端列表,请查阅以下函数的参考文档

函数描述
vpiInitORBParams 使用默认值初始化 VPIORBParams
vpiCreateORBFeatureDetector 创建 ORB 特征检测器 有效负载。
vpiSubmitORBFeatureDetector ORB 特征检测器 操作提交到流。
vpiCreateORBDescriptorExtractor 创建 ORB 描述符提取器 有效负载。
vpiSubmitORBDescriptorExtractor ORB 描述符提取器 操作提交到流。

用法

语言
  1. 导入 VPI 模块
    import vpi
  2. 读取输入图像,将其转换为灰度图像,并使用 CPU 后端从中构建高斯金字塔。
    input = vpi.asimage(np.asarray(Image.open(args.input))) \
    .convert(vpi.Format.U8, backend=vpi.Backend.CPU) \
    .gaussian_pyramid(4, backend=vpi.Backend.CPU)
  3. 在输入金字塔上使用 CPU 后端运行 ORB 角点检测器算法。FAST 强度阈值设置为 142,以便更精细地选择此示例中找到的角点。此外,每层级的最大特征数设置为 88,要使用的最大输入金字塔层级设置为 3。
    with vpi.Backend.CPU
    corners, descriptors = input.orb(intensity_threshold=142, max_features_per_level=88, max_pyr_levels=3)
  4. (可选)以 numpy 数组形式在 CPU 内存中检索所有找到的角点位置及其描述符。
    a_corners = corners.cpu()
    a_descriptors = descriptors.cpu()
  1. 初始化阶段
    1. 包含定义 ORB 算法函数和参数结构的头文件。
      #include <vpi/algo/ORB.h>
      声明实现 ORB 支持的函数。
    2. 创建 ORB 参数对象,最初将其设置为默认参数。FAST 强度阈值设置为 142,以便更精细地选择此示例中找到的角点。此外,每层级的最大特征数设置为 88,要使用的最大输入金字塔层级设置为 3。
      VPIORBParams params;
      vpiInitORBParams(&params);
      params.maxFeaturesPerLevel = 88;
      params.maxPyramidLevels = 3;
      float intensityThreshold
      选择像素作为关键点候选点周围圆弧一部分的阈值。
      int32_t maxFeaturesPerLevel
      ORB 要使用的输入金字塔每层级的最大特征数 N。
      定义: ORB.h:106
      VPIFASTCornerDetectorParams fastParams
      FAST 角点检测器的参数,有关更多详细信息,请参见 FAST 角点检测器。
      定义: ORB.h:94
      int32_t maxPyramidLevels
      要使用的输入金字塔中的最大层级数。
      定义: ORB.h:111
      VPIStatus vpiInitORBParams(VPIORBParams *params)
      使用默认值初始化 VPIORBParams。
      定义 vpiSubmitORBFeatureDetector 参数的结构。
      定义: ORB.h:89
    3. 创建 ORB 有效负载。容量是在输入金字塔的每个层级可以检测到的最大 FAST 角点数。较高的容量通常对应于较慢的运行时间,但为 ORB 提供了更多的角点来过滤。此内部缓冲区容量设置为每层级最大特征数的 20 倍。
      VPIPayload payload;
      int bufCapacity = params.maxFeaturesPerLevel * 20;
      VPIStatus vpiCreateORBFeatureDetector(uint64_t backends, int32_t capacity, VPIPayload *payload)
      创建 ORB 特征检测器有效负载。
      struct VPIPayloadImpl * VPIPayload
      算法有效负载的句柄。
      定义: Types.h:268
      @ VPI_BACKEND_CPU
      CPU 后端。
      定义: Types.h:92
    4. 创建将在其中提交算法以供执行的流。
      VPIStream stream;
      vpiStreamCreate(0, &stream);
      struct VPIStreamImpl * VPIStream
      流的句柄。
      定义: Types.h:250
      VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
      创建流实例。
    5. 定义输入金字塔对象,请参阅 高斯金字塔 了解详细信息。
      VPIImage input;
      LoadImage(sIn, VPI_IMAGE_FORMAT_U8, &input);
      int width, height;
      vpiImageGetSize(input, &width, &height);
      VPIPyramid inputPyr;
      vpiPyramidCreate(width, height, VPI_IMAGE_FORMAT_U8, 4, 0.5, VPI_BACKEND_CPU, &inputPyr);
      #define VPI_IMAGE_FORMAT_U8
      具有一个 8 位无符号整数通道的单平面。
      VPIStatus vpiSubmitGaussianPyramidGenerator(VPIStream stream, uint64_t backend, VPIImage input, VPIPyramid output, VPIBorderExtension border)
      从输入图像计算高斯金字塔。
      struct VPIImageImpl * VPIImage
      图像的句柄。
      定义: Types.h:256
      VPIStatus vpiImageGetSize(VPIImage img, int32_t *width, int32_t *height)
      获取图像尺寸(以像素为单位)。
      VPIStatus vpiPyramidCreate(int32_t width, int32_t height, VPIImageFormat fmt, int32_t numLevels, float scale, uint64_t flags, VPIPyramid *pyr)
      使用指定的标志创建空的图像金字塔实例。
      struct VPIPyramidImpl * VPIPyramid
      图像金字塔的句柄。
      定义: Types.h:262
      @ VPI_BORDER_CLAMP
      边界像素无限重复。
      定义: Types.h:279
    6. 创建将存储 ORB 角点的输出数组。输出数组容量控制 ORB 在所有层级中要检测的最大角点数。此输出容量设置为每层级最大特征数乘以要使用的最大金字塔层级数。
      VPIArray corners;
      int outCapacity = params.maxFeaturesPerLevel * params.maxPyramidLevels;
      vpiArrayCreate(outCapacity, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &corners);
      VPIStatus vpiArrayCreate(int32_t capacity, VPIArrayType type, uint64_t flags, VPIArray *array)
      创建空数组实例。
      struct VPIArrayImpl * VPIArray
      数组的句柄。
      定义: Types.h:232
      @ VPI_ARRAY_TYPE_KEYPOINT_F32
      VPIKeypointF32 元素。
      定义: ArrayType.h:77
    7. 创建将存储 ORB 描述符的输出数组。每个 ORB 角点对应一个描述符。其容量与输出角点数组容量相同。
      VPIArray descriptors;
      vpiArrayCreate(outCapacity, VPI_ARRAY_TYPE_BRIEF_DESCRIPTOR, 0, &descriptors);
      @ VPI_ARRAY_TYPE_BRIEF_DESCRIPTOR
      VPIBriefDescriptor 元素。
      定义: ArrayType.h:84
  2. 处理阶段
    1. 将算法及其参数提交到流。它将由 CPU 后端执行。边界 limited 用于忽略图像边界附近的像素。
      VPI_CHECK_STATUS(vpiSubmitORBFeatureDetector(stream, VPI_BACKEND_CPU, payload, inputPyr, corners, descriptors,
      &params, VPI_BORDER_LIMITED));
      VPIStatus vpiSubmitORBFeatureDetector(VPIStream stream, uint64_t backend, VPIPayload payload, VPIPyramid input, VPIArray outCorners, VPIArray outDescriptors, const VPIORBParams *params, VPIBorderExtension border)
      将 ORB 特征检测器操作提交到流。
      @ VPI_BORDER_LIMITED
      将图像视为受限,以不访问外部像素。
      定义: Types.h:282
    2. (可选)等待直到处理完成。
      vpiStreamSync(stream);
      VPIStatus vpiStreamSync(VPIStream stream)
      阻止调用线程,直到此流队列中的所有提交命令都完成(队列为空)...
  3. 清理阶段
    1. 释放流、输入图像、输出数组和有效负载所持有的资源。
      vpiArrayDestroy(corners);
      vpiArrayDestroy(descriptors);
      void vpiArrayDestroy(VPIArray array)
      销毁数组实例。
      void vpiImageDestroy(VPIImage img)
      销毁图像实例。
      void vpiPayloadDestroy(VPIPayload payload)
      释放有效负载对象和所有关联的资源。
      void vpiStreamDestroy(VPIStream stream)
      销毁流实例并释放所有硬件资源。

有关更多信息,请参阅 VPI - 视觉编程接口 的“C API 参考”部分中的 ORB 特征

性能

有关如何使用下表性能表的信息,请参见 算法性能表
在比较测量结果之前,请查阅 比较算法运行时间
有关性能基准测试方式的更多信息,请参见 性能基准

 - 

参考

  1. Rublee, E., Rabaud, V., Konolige, K., & Bradski, G. (2011, November). ORB: An efficient alternative to SIFT or SURF. In 2011 International conference on computer vision (pp. 2564-2571). Ieee.
  2. Lowe, D. G. (1999, September). Object recognition from local scale-invariant features. In Proceedings of the seventh IEEE international conference on computer vision (Vol. 2, pp. 1150-1157). Ieee.
  3. Bay, H., Tuytelaars, T., & Van Gool, L. (2006). Surf: Speeded up robust features. In European conference on computer vision (pp. 404-417). Springer, Berlin, Heidelberg.