OpenVX 1.3 版本特性及硬件实现解析
1. OpenVX 现有实现与平台
OpenVX 应用具有良好的可移植性,它可以跨运行 OpenCL 或 CUDA 的 GPU 以及非 GPU 架构运行,还能利用内存层次结构和专用内存,无需编写专用的 OpenCL 或 CUDA 代码就能在其中进行内核优化。
1.1 专用硬件实现
专用硬件能够实现特定功能的高度优化版本。在为特定功能分配硅块与该块带来的性能和功耗优势之间存在权衡。如果某个功能经常使用且在标准处理器上消耗大量周期,那么它适合进行定制硬件加速。而不常用的功能或能在标准硬件上快速运行的功能,通常最好留在标准处理器上,以最有效地利用硅面积。
因此,结合专用硬件块的 OpenVX 实现通常也会有传统处理器来执行部分甚至大部分 OpenVX 功能。专用块仅用于少数能获得最大收益的功能。例如,卷积是一种常见的计算机视觉功能,在标准处理器架构上会消耗大量周期,所以它是专用硬件实现的热门功能。图像缩放和颜色转换等图像处理操作也可在专用图像信号处理器(ISP)上运行。从应用程序员的角度来看,他们的 OpenVX 代码无需更改,但如果其图中包含具有专用硬件块的功能,那么该功能将比在通用处理器上运行得更快且功耗更低。
1.2 FPGA 实现
虽然目前没有专门针对 FPGA 平台的 OpenVX 合规实现,但其他类型平台的实现可以在 FPGA 上实例化。至少有一个 DSP 实现已为此进行了演示。FPGA 可用于实现运行 OpenVX 的 DSP,当然,也可以使用自定义 FPGA 块更直接地实现 OpenVX 功能。商业 FPGA 实现可能类似于专用硬件实现,即可能有一个传统处理器执行大部分 API,同时有专用 FPGA 块处理少数受益于定制硬件的功能。鉴于 FPGA 平台和 OpenVX API 的灵活性,有很多实现可能性。
1.3 混合片上系统(SoCs)
现代 SoC 作为当今手机和自动驾驶汽车的核心,通常包含多种处理器,如多个 CPU、GPU、DSP、ISP 和一些专用硬件块。为 SoC 设计的 OpenVX 实现可以利用所有这些处理器。根据每个处理器的相对性能和功耗,特定功能或图的部分可以发送到不同的处理器,并通过共享内存或 DMA 进行块之间的通信。单个图可以在多个处理器上运行,从而通过并行性实现更高的性能。随着 OpenVX 在更复杂的 SoC 上部署,我们有望看到利用各种计算资源的实现。OpenVX 应用程序员甚至无需知道他们的应用在哪个处理器上运行,他们只会看到低功耗下的高性能。
2. OpenVX 1.3 版本的变化
2019 年 8 月,Khronos OpenVX 工作组发布了 OpenVX 规范的 1.3 版本。与之前的版本相比,1.3 版本的主要变化是引入了功能集。功能集使实现能够支持规范中定义明确的子集,而不必支持规范描述的每个功能。此外,OpenVX 1.3 还引入了对二值图像的支持,增加了用于实现安全关键应用的信息,移除了对双向参数的支持,并进行了一些其他小的更改。
2.1 功能集
在 OpenVX 1.3 版本之前,所有 OpenVX 实现者都必须支持主规范文档中描述的所有 API 和功能。为了将实现推向市场并使用相关的 Khronos 商标,必须通过一致性测试,以确保支持规范中描述的每个功能。随着该领域的发展,规范中添加的功能越来越多,但并非所有功能对每个应用都有用。如果实现者想专注于 OpenVX 的特定应用领域的客户,他们仍然必须实现所有功能,即使其中一些对他们无用。这部分是出于设计考虑,因为拥有一组应用开发者可以依赖的标准功能对于确保应用的可移植性至关重要。然而,随着标准中功能列表的增长,在多样化和专业化的硬件上实现规范中的所有功能的负担变得过重,因此 OpenVX 工作组引入了功能集来解决这个问题。
功能集在要求每个人实现所有功能和让每个实现者独立决定支持哪些功能之间取得了平衡。如果每个实现者独立决定,应用开发者将无法假设任何功能都能正常工作。功能集是为特定应用空间量身定制的、定义明确的连贯规范子集。应用开发者可以依赖几个功能集中每个功能集中的所有功能,因此他们只需检查他们使用的实现是否支持他们需要的功能集,然后就可以使用该功能集中的所有功能。
OpenVX 的功能集在与主规范分开的文档中定义。初始功能集文档可在 此处 找到。功能集文档描述了几种不同类别的功能集,但最重要的两种类型是一致性功能集和可选功能集。
一致性功能集对实现者很重要,因为为了将实现宣传为符合 OpenVX 标准,甚至只是宣传为“OpenVX”,至少必须完全支持一个一致性功能集。可选功能集正如其名,实现可以在支持至少一个一致性功能集的基础上选择是否支持它们。可选功能集在这方面与 OpenVX 扩展类似,实际上,扩展和可选功能集之间的唯一技术区别在于,功能集在功能集文档中定义,引用主规范的子集,而扩展在完全不同的文档中定义。如果一个实现支持一个可选功能集或扩展,那么它必须完全支持该功能集或扩展,不能挑选其中的个别功能。功能集规范还对组织性和信息性功能集进行了细微区分,并分别定义了一个,下面将进行描述。
规范中描述的第一个功能集是基础功能集,它包含构建和执行 OpenVX 图所需的基本框架元素。它既不是一致性功能集也不是可选功能集,而是组织性的。它不是一致性功能集,因为仅实现这个功能集无法获得 OpenVX 一致性认证。原因是如果不在框架中定义一些实际要使用的功能,仅靠基础功能集无法完成任何事情。它也不是可选的,所有 OpenVX 实现都必须支持基础功能集中的所有功能。基础功能集主要用于在一致性功能集描述中引用这组核心功能,而无需重复列出它们,因此称为“组织性”。
功能集规范还定义了三个一致性功能集:
- 视觉功能集 :包含一组基本的经典视觉功能,大致对应于 OpenVX 规范 1.1 版本中可用的功能集。
- 神经网络功能集 :包含一组常见的神经网络“层”功能,对应于为 OpenVX 规范 1.2 版本定义的神经网络扩展。
- NNEF 功能集 :参考了 Khronos 神经网络交换格式规范,使应用能够导入和执行使用 NNEF 规范中描述的领域特定语言定义的神经网络。它基本上是神经网络的另一种实现方式,比神经网络功能集中定义的层功能更灵活、功能更丰富。有关 NNEF 的详细信息,请参考 此处 。
一个 OpenVX 实现必须支持一个或多个这些一致性功能集,并通过相应功能集的一致性测试,才能作为 OpenVX 实现推向市场。一个实现也可以支持多个一致性功能集,因此从这个意义上说,一旦支持了至少一个一致性功能集,其他功能集对该实现者来说就变成了“可选”。
功能集文档还定义了两个特定的可选功能集:
- 二值图像功能集
- 增强视觉功能集
这两个功能集都依赖于视觉一致性功能集,即只有在实现了视觉功能集的情况下才能实现它们,因为它们依赖于视觉功能。增强视觉功能集大致对应于 OpenVX 规范 1.2 版本中引入的功能集。二值图像功能集值得进一步讨论。
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A(OpenVX 功能集):::process --> B(基础功能集):::process
A --> C(一致性功能集):::process
A --> D(可选功能集):::process
C --> C1(视觉功能集):::process
C --> C2(神经网络功能集):::process
C --> C3(NNEF 功能集):::process
D --> D1(二值图像功能集):::process
D --> D2(增强视觉功能集):::process
2.2 二值图像功能集
二值图像功能集引入了一种图像,其中每个像素由单个位表示,数据格式为 VX_DF_IMAGE_U1。在引入这种数据格式之前,二值图像必须用完整的 8 位字节表示,其中零表示为 0x00(所有位为零),一表示为 0xFF(所有位为一),即十进制的 255。如果一个实现支持二值图像功能集,那么这种图像可以仅用每个像素一位来高效表示,节省了数据空间,并可能显著加快关键二值操作的执行速度。
明显的例子是逻辑操作(与、或、非、异或),二值图像功能集支持所有这些操作。二值图像的另一个常见用途是非线性滤波器,这当然包括通用的 vxNonLinearFilter 函数,以及用于特定 3×3 形态学操作(膨胀、腐蚀和中值滤波)的函数。如果你的 OpenVX 实现支持二值图像功能集,那么你只需将输入和输出图像参数设置为使用 VX_DF_IMAGE_U1 数据格式创建的图像,就可以使用这些函数的二值版本。
二值图像功能集还支持使用现有的 vxConvertDepth 函数在 U1 图像和整数类型 U8 及 S16 之间进行转换。只需将 vxConvertDepth 的一个参数设置为 U1 图像,另一个参数设置为你要转换到或从其转换的 U8 或 S16 图像。当从 U8 或 S16 转换到 U1 时,输入中设置为零(0x00 或 0x0000)的像素当然会在 U1 输出中设置为“0”位,输入中的所有非零像素将在 U1 输出中设置为“1”位。当从 U1 转换到较大类型时,“0”位将在输出中设置为全零(0x00 或 0x0000),“1”位将在输出中设置为全一(即 0xFF 或 0xFFFF,在 U8 和 S16 中分别对应十进制值 255 和 -1)。
你还可以使用 vxScaleImage 和 vxWarpAffine 对 U1 图像进行缩放和扭曲操作。输入和输出图像都必须是 U1 格式,不允许混合格式。如果你想更改格式,则必须使用 vxConvertDepth 函数显式进行。
一些 OpenVX 函数的输出自然是二值的,但到目前为止,这些输出必须用每个像素 8 位来表示。然而,如果支持二值图像功能集,你只需将输出参数设置为具有 VX_DF_IMAGE_U1 格式的图像,就可以使 vxCannyEdgeDetector 和 vxThreshold 的输出真正以二值形式表示。另外有三个函数将接受二值图像作为输入:vxHoughLinesP、vxMeanStdDev 和 vxNonMaxSuppression。你可以使用 vxHoughLinesP 在二值图像中搜索直线。以二值图像作为输入的 vxMeanStdDev 函数将为你提供图像中“1”像素的比例作为均值,标准差范围在 0(全 0 或全 1)到约 0.5(约一半 0 和一半 1)之间。vxNonMaxSuppression 函数有一个“掩码”输入,现在可以用真正的二值图像表示。
支持 U1 参数的二值图像功能集中的函数总结如下:
| 类别 | 函数 |
| ---- | ---- |
| 逻辑 | 与、非、或、异或 |
| 非线性 | 3×3 膨胀、3×3 腐蚀、3×3 中值滤波、非线性滤波 |
| 重塑 | 深度转换、图像缩放、仿射扭曲 |
| 二值输出 | 坎尼边缘检测、阈值处理 |
| 二值输入 | 概率霍夫直线检测、均值标准差计算、非极大值抑制(仅掩码) |
OpenVX 1.3 版本特性及硬件实现解析
2. OpenVX 1.3 版本的变化(续)
2.3 安全关键应用支持
OpenVX 1.3 规范的一个重大改进是纳入了支持安全关键应用(如自动驾驶汽车)的功能。安全关键应用最初是在基于 OpenVX 1.1 主规范的单独的 OpenVX - SC 规范文档中处理的。它与 OpenVX 1.2 并行开发,因此没有纳入 OpenVX 1.2 的功能。在 OpenVX 1.3 中,安全关键功能被合并回主规范,因此不再需要单独的安全关键规范。
为了支持安全关键应用,OpenVX 1.3 在功能集规范中确定了一个“部署”子集,即安全关键部署功能集。该功能集包含数据对象(如图像和张量)、高级上下文、图和引用结构,以及一个“导入”对象,用于加载和执行图。它不包含图的内部细节,如内核、节点或参数。其理念是,所有用于构建和验证图的这些细节仅在离线时使用,在运行时,图直接加载并执行。
另一个主要改进是将需求标签纳入主 1.3 规范文档。需求标签的形式为 [REQ - XXXX],其中“XXXX”是该需求的唯一数字标识符。这些标签最初在单独的 OpenVX - SC 文档中引入,在 OpenVX 1.3 中包含在主规范中。这些标签唯一标识了每个必须实现和验证的需求。拥有这样的唯一标签使 OpenVX 实现者和应用开发者能够应用安全关键开发方法(如 ISO - 26262)所需的严格跟踪技术。在这些方法中,每个需求都必须明确指定、实现和验证,并对每个步骤进行清晰的文档记录。标签使每个需求在整个过程中都能被跟踪。
规范文档中还有一些支持安全关键应用的小变化。其中一个变化是将“未定义”一词的使用改为“实现定义”。在规范的几个地方,描述了使用标准正确编写程序的规则。在之前的规范版本中,如果违反了这样的规则,违反规则的程序的行为被描述为“未定义”。对于安全关键应用,任何未定义的行为都是不可接受的,因此这种表述改为“实现定义”。在 OpenVX 的安全关键实现中,即使 OpenVX 规范本身没有定义,实现者也必须定义规则被违反时会发生什么。
例如,在各种对象的映射和取消映射函数中可以看到实现定义行为的示例。当你映射一个对象时,你会得到一个带有基指针和大小的内存块。如果你试图访问这个内存块之外的内存,无论是在基指针之前还是在内存区域的末尾之后,结果是实现定义的。如果你试图写入只读内存或读取只写内存,结果也是实现定义的。OpenVX 的安全关键实现需要指定当你进行这些操作时会发生什么,例如,当你试图读取超出内存区域末尾时,会生成一个错误,并且读取的结果全为零。非安全关键实现可以做最容易的事情,这可能包括读取随机结果甚至崩溃,但安全关键实现必须禁止这种情况。
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A(OpenVX 1.3 安全关键支持):::process --> B(安全关键部署功能集):::process
A --> C(需求标签):::process
A --> D(表述更改: 未定义 -> 实现定义):::process
B --> B1(数据对象):::process
B --> B2(高级上下文):::process
B --> B3(图和引用结构):::process
B --> B4(导入对象):::process
C --> C1(唯一标识需求):::process
C --> C2(支持严格跟踪):::process
D --> D1(定义违规行为):::process
2.4 双向参数的移除
OpenVX 1.3 规范的另一个变化是移除了双向参数。运行带有双向参数的图会同时读取和写入双向参数数据,从技术上讲,这会在图中形成一个循环。这对于一些优化技术可能是一个问题,并且需要很多规则来确定数据何时被读取和写入,以确保行为是明确的。现在移除了双向参数,这些规则就不再需要了。
在 OpenVX 1.2 及更早版本中,标准中唯一使用双向参数的内核是累积节点:累积、累积平方和加权累积。累积节点的功能可以使用加法节点轻松重建。通过同时使用乘法节点,也可以实现累积平方节点的功能。加权累积功能用 OpenVX 1.2 节点构建有点困难,因此 OpenVX 1.3 添加了一个“加权平均”节点来简化这一过程:
vx_node vxWeightedAverageNode(
vx_graph graph,
vx_image img1,
vx_scalar alpha,
vx_image img2,
vx_image output);
该函数接受两个输入图像和一个介于 0 和 1 之间的“alpha”参数,该参数表示对每个图像的加权程度。输出图像中的每个像素设置为第一个输入图像乘以 alpha 加上第二个输入图像乘以 (1 - alpha):
output(x,y) = alpha * img1(x,y) + (1 - alpha) * img2(x,y)
这个简单的计算也称为 alpha 混合,因为它产生了由 alpha 参数控制的两个输入图像的“混合”。它的工作方式类似于移除的加权累积节点中的 alpha 参数。
以下是移除双向参数后相关功能的实现对比:
| 功能 | OpenVX 1.2 及更早 | OpenVX 1.3 |
| ---- | ---- | ---- |
| 累积 | 使用双向参数的累积节点 | 用加法节点重建 |
| 累积平方 | 使用双向参数的累积平方节点 | 用加法和乘法节点实现 |
| 加权累积 | 使用双向参数的加权累积节点 | 使用新的加权平均节点 |
综上所述,OpenVX 1.3 版本在功能集、二值图像支持、安全关键应用支持和双向参数移除等方面进行了重要改进,这些改进使得 OpenVX 在不同的硬件平台和应用场景中更加灵活和可靠。无论是硬件实现者还是应用开发者,都可以从这些变化中受益,更好地利用 OpenVX 进行计算机视觉和相关领域的开发。
超级会员免费看
1995

被折叠的 条评论
为什么被折叠?



