了解图形结构
G-API 代表“Graph API”,但您在上面的例子中提到了任何图形吗?这是最初的设计目标之一——G-API 在设计时考虑了表达式,使采用和移植过程更加简单。人们在编写普通代码时通常不会考虑节点和边缘,因此 G-API 虽然是 Graph API,但不会强迫其用户这样做。
但是,在定义 cv::GComputation 对象时,仍会隐式构建图形。检查生成的图形的外观,以检查它是否正确生成以及它是否真正代表我们的算法可能很有用。学习图形的结构以查看它是否有任何冗余也很有用。
G-API 允许将生成的图形转储到文件中,然后可以使用流行的开放式图形可视化软件 Graphviz 进行可视化。.dot
为了将我们的图形转储到文件中,请在运行应用程序之前设置为文件名,例如:.dot
GRAPH_DUMP_PATH
<span style="background-color:#fbfcfd"><span style="color:#000000">$ GRAPH_DUMP_PATH=segm.dot ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi
</span></span>
现在,可以使用如下命令可视化此文件:dot
<span style="background-color:#fbfcfd"><span style="color:#000000">$ dot segm.dot -Tpng -o segm.png
</span></span>
或以交互方式查看(有关如何安装这些软件包,请参阅您的发行版/操作系统文档)。xdot
上图演示了 G-API 内部算法表示的许多有趣方面:
- G-API 底层图是二分图:它由操作节点和数据节点组成,数据节点只能连接到操作节点,操作节点只能连接到数据节点,单一类型的节点永远不会直接连接。
- 图形是有向的 - 图形中的每条边都有一个方向。
- 图形“开始”和“结束”以 Data 类型的节点。
- 一个数据节点只能有一个写入器和多个读取器。
- 一个 Operation 节点可以有多个输入,但每个输入都必须具有唯一的端口号(在输入之间)。
- 一个 Operation 节点可以有多个输出,并且每个输出必须具有唯一的端口号(在输出之间)。
测量内存占用
让我们测量和比较该算法在两个版本中的内存占用:基于 G-API 和基于 OpenCV。目前,G-API 版本也是基于 OpenCV 的,因为它回退到内部的 OpenCV 函数。
在 GNU/Linux 上,可以使用 Valgrind 分析应用程序内存占用情况。在 Debian/Ubuntu 系统上,它可以像这样安装(假设您具有管理员权限):
<span style="background-color:#fbfcfd"><span style="color:#000000">$ sudo apt-get install valgrind massif-visualizer
</span></span>
安装后,我们可以轻松收集两个算法版本的内存配置文件:
<span style="background-color:#fbfcfd"><span style="color:#000000">$ valgrind --tool=massif --massif-out-file=ocv.out ./bin/example_tutorial_anisotropic_image_segmentation
==6101== Massif, a heap profiler
==6101== Copyright (C) 2003-2015, and GNU GPL'd, by Nicholas Nethercote
==6101== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==6101== Command: ./bin/example_tutorial_anisotropic_image_segmentation
==6101==
==6101==
$ valgrind --tool=massif --massif-out-file=gapi.out ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi
==6117== Massif, a heap profiler
==6117== Copyright (C) 2003-2015, and GNU GPL'd, by Nicholas Nethercote
==6117== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==6117== Command: ./bin/example_tutorial_porting_anisotropic_image_segmentation_gapi
==6117==
==6117==
</span></span>
完成后,我们可以使用Massif Visualizer(安装在上述步骤中)检查收集的配置文件。
以下是该算法的原始 OpenCV 版本的可视化内存配置文件:
我们看到内存是在应用程序执行时分配的,在 calcGST() 函数中达到峰值;然后,当 calcGST() 完成其执行并释放所有临时缓冲区时,占用空间会下降。Massif 报告的峰值内存消耗为 7.6 MiB。
现在让我们看一下 G-API 版本的配置文件:
创建 G-API 计算并开始执行后,G-API 会立即分配所有必需的内存,然后内存配置文件保持平坦,直到程序终止。Massif 报告的峰值内存消耗为 11.4 MiB。
在这一点上,读者可能会问一个正确的问题——G-API 有那么糟糕吗?使用它的原因是什么?
希望不是。我们之所以在这里看到内存消耗增加,是因为使用默认的朴素的基于 OpenCV 的后端来执行此图。该后端主要用于在卸载/进一步优化之前进行快速原型设计和调试算法。
该后端尚未使用任何复杂的内存管理策略,因为它目前不是重点。在下一章中,我们将了解 Fluid 后端,并了解相同的 G-API 代码如何在完全不同的模型中运行(并且占用空间缩小到数千字节)。
后端和内核
本章介绍了如何以特殊方式执行 G-API 计算,例如卸载到另一台设备,或使用特殊智能进行调度。G-API 旨在使其图形具有可移植性——这意味着一旦以 G-API 术语定义了图形,如果我们想在 CPU 或 GPU 或同时在两个设备上运行它,则不需要对其进行任何更改。G-API 高级概述和 G-API 内核 API 更深入地揭示了使之成为可能的技术细节。在本章中,我们将利用 G-API Fluid 后端来使我们的图形缓存在 CPU 上高效。
G-API 将后端定义为知道如何运行内核的较低级别的实体。后端可能具有(事实上确实具有)不同的内核 API,这些 API 用于为该后端编程和集成内核。在此上下文中,内核是操作的实现,在顶级 API 级别上定义(参见 G_TYPED_KERNEL() 宏)。
后端是一个知道设备和平台细节的东西,并在执行其内核时牢记这些细节。例如,可能有 Halide 后端,它允许用 Halide 语言编写(实现)G-API 操作,然后为 G-API 图的部分生成功能性 Halide 代码,这些代码在那里映射得很好。
使用 Fluid 后端运行图形
OpenCV 4.0 捆绑了两个 G-API 后端——我们刚刚使用的默认“OpenCV”和一个特殊的“Fluid”后端。
Fluid 后端重新组织执行以节省内存并实现近乎完美的缓存局部性,实现所谓的“流式”执行模型。
为了开始使用 Fluid 内核,我们首先需要包含适当的头文件(默认情况下不包括这些文件):
一旦包含了这些头文件,我们就可以形成一个新的内核包,并将其指定给 G-API:
在 G-API 中,内核(或操作实现)是对象。内核被组织成集合或内核包,由类 cv::GKernelPackage 表示。内核包的主要目的是捕获我们想要在图中使用的内核,并将其作为图编译选项传递:
传统的OpenCV在逻辑上被划分为多个模块,每个模块都提供一组功能。在 G-API 中,还有一些“模块”,它们表示为特定后端提供的内核包。在此示例中,我们将 Fluid 内核包传递给 G-API,以便在图中使用适当的 Fluid 函数。
内核包是可组合的——在上面的例子中,我们将“Core”和“ImgProc”Fluid 内核包组合成一个。请参阅 cv::gapi::combine 上的文档参考。
如果选项中未指定内核包,则 G-API 使用默认包,该包由默认的 OpenCV 实现组成,因此 G-API 图默认通过 OpenCV 函数执行。OpenCV 后端提供比任何其他后端更广泛的功能覆盖范围。如果指定了内核包,如本例所示,则该软件包将与默认软件包结合使用。这意味着在发生冲突时,用户指定的实现将替换默认实现。
故障排除和自定义
经过上述修改后,(在 OpenCV 4.0 中)应用程序应该崩溃并显示如下消息:
Fluid 后端在 OpenCV 4.0 中有许多限制(有关最新状态,请参阅此 wiki 页面)。具体而言,此示例中使用的 Box 筛选器仅支持静态 3x3 内核大小。
在此示例中,我们可以通过避免使用 Box 过滤器内核的 Fluid 版本来避免 G-API,从而轻松克服这个问题。这可以通过从我们刚刚创建的内核包中删除适当的内核来完成:
现在,此内核包没有任何 Box 筛选器内核接口的实现(指定为模板参数)。如上所述,G-API 现在将回退到 OpenCV 来运行此内核。具有此更改的结果代码现在如下所示:
让我们检查一下切换到 Fluid 后端后此示例的内存配置文件。现在它看起来像这样:
现在该工具报告了 4.7MiB——我们只是在代码中更改了几行,而没有修改图形本身!它比之前的 G-API 结果提高了 ~2.4 倍,比原始 OpenCV 版本提高了 ~1.6 倍。
我们还要看看图形的内部表示现在是什么样子的。将图形转储到中将导致如下可视化效果:.dot
此图在结构上与其以前的版本(在操作和数据对象方面)没有区别,尽管更改的布局(在转储的左侧)很容易引起注意。
可视化反映了 G-API 如何处理混合图(也称为异构图)。此图中的大多数操作都是通过 Fluid 后端实现的,但 Box 过滤器是由 OpenCV 后端执行的。可以很容易地看到图形是分区的(带有矩形)。G-API 根据其亲和力对连接的操作进行分组,形成子图(或 G-API 术语中的孤岛),我们的顶级图成为多个较小子图的组合。每个后端都决定了其子图(孤岛)的执行方式,因此 Fluid 后端会尽可能地优化内存,并且 OpenCV Box 过滤器访问的 6 个中间缓冲区被完全分配,无法优化。
结论
本教程演示了 G-API 是什么及其关键设计概念是什么,如何将算法移植到 G-API,以及如何在此之后利用图模型的优势。
在 OpenCV 4.0 中,G-API 仍处于起步阶段——它更像是所有未来工作的基础,尽管现在已经准备好使用。
在线教程
- 麻省理工学院人工智能视频教程 – 麻省理工人工智能课程
- 人工智能入门 – 人工智能基础学习。Peter Norvig举办的课程
- EdX 人工智能 – 此课程讲授人工智能计算机系统设计的基本概念和技术。
- 人工智能中的计划 – 计划是人工智能系统的基础部分之一。在这个课程中,你将会学习到让机器人执行一系列动作所需要的基本算法。
- 机器人人工智能 – 这个课程将会教授你实现人工智能的基本方法,包括:概率推算,计划和搜索,本地化,跟踪和控制,全部都是围绕有关机器人设计。
- 机器学习 – 有指导和无指导情况下的基本机器学习算法
- 机器学习中的神经网络 – 智能神经网络上的算法和实践经验
- 斯坦福统计学习
有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
人工智能书籍
- OpenCV(中文版).(布拉德斯基等)
- OpenCV+3计算机视觉++Python语言实现+第二版
- OpenCV3编程入门 毛星云编著
- 数字图像处理_第三版
- 人工智能:一种现代的方法
- 深度学习面试宝典
- 深度学习之PyTorch物体检测实战
- 吴恩达DeepLearning.ai中文版笔记
- 计算机视觉中的多视图几何
- PyTorch-官方推荐教程-英文版
- 《神经网络与深度学习》(邱锡鹏-20191121)
- …
第一阶段:零基础入门(3-6个月)
新手应首先通过少而精的学习,看到全景图,建立大局观。 通过完成小实验,建立信心,才能避免“从入门到放弃”的尴尬。因此,第一阶段只推荐4本最必要的书(而且这些书到了第二、三阶段也能继续用),入门以后,在后续学习中再“哪里不会补哪里”即可。
第二阶段:基础进阶(3-6个月)
熟读《机器学习算法的数学解析与Python实现》并动手实践后,你已经对机器学习有了基本的了解,不再是小白了。这时可以开始触类旁通,学习热门技术,加强实践水平。在深入学习的同时,也可以探索自己感兴趣的方向,为求职面试打好基础。
第三阶段:工作应用
这一阶段你已经不再需要引导,只需要一些推荐书目。如果你从入门时就确认了未来的工作方向,可以在第二阶段就提前阅读相关入门书籍(对应“商业落地五大方向”中的前两本),然后再“哪里不会补哪里”。
有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓