OpenCV 4.10.0
开源计算机视觉
|
G-API 是一个异构框架,提供统一的 API 来以多个支持的后端对图像处理管道进行编程。
关键设计思想是将管道代码本身保持为与平台无关,同时使用额外的参数在图形编译(配置)时指定要使用哪些内核以及要利用哪些设备。此要求已导致以下体系结构
此体系结构中有三层
API 层是用户在定义和使用管道时与之交互的部分(在 G-API 中称为计算)。API 层定义了一组 G-API 动态对象,这些对象可以用作图形中的输入、输出和中间数据对象
API 层指定了针对这些数据对象定义的操作列表,即内核。有关 G-API 提供哪些默认操作,请参阅 G-API core 和 imgproc 命名空间的详细信息。
G-API 不仅限于这些操作 - 用户可以使用一个特殊宏 G_TYPED_KERNEL()轻松定义自己的内核。
API 层还负责在创建管道时编组并存储操作参数。除上述 G-API 动态对象外,操作还可能接受任意参数(更多信息请参见此处),因此,API 层会对其值进行捕获并在执行时进行内部存储。
最后,cv::GComputation 和 cv::GCompiled 是 API 层中剩余的重要组件。前者将一系列 G-API 表达式包装到一个对象(图)中,后者是图编译的产物(有关详细信息,请参见本章节)。
在执行每个 G-API 计算之前,均会对其进行编译。编译过程以两种方式触发
当输入数据格式事先未知时,推荐使用第一种方式,例如当它来自任意输入文件。第二种方式推荐用于通常在其中预定义输入数据特征的部署(生产)场景。
在 ADE 框架的顶部构建图编译过程。最初,从 API 层捕获的表达中生成一个二部图。此图包含两种类型的节点:数据和操作。图始终以一个或多个数据节点开始和结束,操作节点位于两者之间。每个操作节点都具有输入和输出,两者都是数据节点。
生成初始图后,实际上会通过许多图转换(称为pass)对其进行处理。ADE 框架充当编译器 pass 管理引擎,而 pass 是专门为 G-API 编写的。
有不同的 pass 可以检查图的有效性、优化操作和数据的详细信息、根据关联性或用户指定的区域化将节点组织到集群(“Island”)中,等等。后端还可以将特定于后端的 pass 注入到编译过程中,有关更多信息,请参阅 专门的章节。
图编译的结果是一个已编译对象,由类 cv::GCompiled 所表示。始终会创建一个新的 cv::GCompiled 对象,无论是否有明确或隐式的编译请求(请参见上文)。实际的图执行发生在 cv::GCompiled 中,并由参与图编译的后端决定。
上图列出了两个后端:OpenCV 和 Fluid。OpenCV 是所谓的“参考后端”,它使用普通的旧 OpenCV 函数来实现 G-API 操作。此后端对于在熟悉的开发系统上制作原型非常有用。Fluid 是一个插件,用于在 CPU 上执行高效缓存操作 - 它实现了一个不同的执行策略并使用其自己的特殊内核来运行。Fluid 后端允许在 CPU 上运行时实现更少的内存占用和更好的内存局部性。
可能有多个后端可用,例如 Halide、OpenCL 等 - G-API 提供了一个统一的内部 API 来开发后端,因此任何爱好者或公司都可以自由地在新的平台或加速器上扩展 G-API。就 OpenCV 基础架构而言,每个新的后端都是一个新的独立 OpenCV 模块,当作为 OpenCV 的一部分构建时,它会扩展 G-API。
图执行的方式由针对编译选择的后端定义。实际上,每个后端都会在可执行(已编译)对象生成时构建自己的执行脚本,作为图编译过程的最后阶段。例如,在 OpenCV 后端中,此脚本只是一系列按拓扑排序的 OpenCV 函数调用;对于 Fluid 后端,类似 - 一个按拓扑排序的 代理 列表,处理每次迭代的输入行。
图执行通过两种方式触发:
这两种方法都是多态的,采用可变参数,其有效性检查在运行时执行。如果传递的数据对象的数字、形状和格式与预期不一致,将引发运行时异常。G-API 还提供了类型化封装器,可在编译时执行这些检查 – 请参见cv::GComputationT<>
。
G-API 图形执行已声明为无状态 – 这意味着编译的函数对象 (cv::GCompiled) 就像一个纯 C++ 函数,针对同一组输入参数提供相同的结果。
执行方法采用 \(N+M\) 参数,其中 \(N\) 个输入,\(M\) 是针对已定义 cv::GComputation 的输出。请注意,虽然在定义中使用了 G-API 类型 (cv::GMat 等),但执行方法采用包含实际数据的传统 OpenCV 数据类型(如 cv::Mat) – 请参见 参数封送 中的表格。