OpenCV Graph API初体验

OpenCV Graph API (G-API)

Introduction:

OpenCV的Graph API(或称G-API)是一个让常规图像处理变得更快(fast)和轻量(portable)的新模块。这两种方式的是通过引入一个新的基于图像的执行模型实现的(graph-based model of execution)。

G-API是一个特殊的OpenCV模块。和其它大多数的主要模块(OpenCV本体模块)不同的是,此模块扮演的角色是一个框架,而不是某种特别的CV算法。G-API提供了定义CV操作的方式,用以构图(以一种表达的方式),最终通过特定的后端(backend)来生效和执行操作。

注意:G-API是一个新模块,目前正处在活跃的开发过程中,它的API现阶段是不稳定的,在未来或许会有一些微小但破坏兼容性的变化。

Contents

G-API的文档被编排为如下篇章:

开发G-API背后的动机和其的目标
G-API构架的总体概览和其主要的内部构件
学习如何引入G-API中新的操作并使其在不同的后端下生效
G-API的核心操作,算术、布尔和其他矩阵运算
图像处理的函数:色彩空间转换,滤波器等等

API Example

下面举一个非常简单的G-API应用的例子:

g-api.cpp

#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>
int main(int argc, char *argv[])
{
    cv::VideoCapture cap;
    if (argc > 1) cap.open(argv[1]);
    else cap.open(0);
    CV_Assert(cap.isOpened());
    cv::GMat in;
    cv::GMat vga      = cv::gapi::resize(in, cv::Size(), 0.5, 0.5);
    cv::GMat gray     = cv::gapi::BGR2Gray(vga);
    cv::GMat blurred  = cv::gapi::blur(gray, cv::Size(5,5));
    cv::GMat edges    = cv::gapi::Canny(blurred, 32, 128, 3);
    cv::GMat b,g,r;
    std::tie(b,g,r)   = cv::gapi::split3(vga);
    cv::GMat out      = cv::gapi::merge3(b, g | edges, r);
    cv::GComputation ac(in, out);
    cv::Mat input_frame;
    cv::Mat output_frame;
    CV_Assert(cap.read(input_frame));
    do
    {
        ac.apply(input_frame, output_frame);
        cv::imshow("output", output_frame);
    } while (cap.read(input_frame) && cv::waitKey(30) < 0);
    return 0;
}

注意 必须编译opencv-contrib,才能正常编译以上代码。且必须为OpenCV 4.x
代码中调用了摄像头,没有摄像头可以用视频代替,cap.open("filename.xxx")或者在运行程序时在后面加入视频的名称(如./g-api ./filename.xxx)。
编译命令:
g++ g-api.cpp -o g-api `pkg-config --flags --libs opencv4`

由于G-API是OpenCV的一个独立模块,所以其头文件必须单独引入。
也就是说 直接引入#include <opencv2/opencv.hpp>头文件不行,opencv.hpp里面没有包含G-API的头文件。main()函数里面首先创建和初始化了标准OpenCV视频类,可以从视频流或者摄像头读取每帧图像。

G-API的管线(pipeline:pipeline表示在外接程序与其宿主之间交换数据的管线段的线性通信模型。从宿主端开始,管线具有以下一系列管线段:宿主、外接程序的宿主视图、宿主端适配器、协定、外接程序端适配器、外接程序视图和外接程序。)随之被创建,事实上它是通过调用cv::GMat来完成一系列的G-API数据操作。关于这块代码比较重要的一个方面是:它只是声明了要去做什么操作,而并不是直接执行什么操作。到此时也还没有进行任何的图像处理。G-API只追踪管线的操作以及其实如何连接的。G-API的数据对象(在此处是cv::GMat)是用以连接各种操作的(PS,这点其实就等同于流水线作业)。cv::GMat in则是一个为空的GMat信号,用于告知计算的开始。

在写完G-API这块代码之后,图像(frame帧)便被捕捉到一个调用图(call graph)中,并同时实例化cv::GComputation这个对象。此对象把输入/输出(input/output)数据当做参数(在本例中依次是inout这两个cv::GMat对象),并基于in和out的中的数据流来重建调用图。

在某种程度上,cv::GComputation是一个缩减对象(thin object,我也不知道怎么翻译好),它负责捕捉何种图像处理操作来组成计算。但他也可以用于执行计算——在接下来的循环处理中,每次捕捉到的帧(cv::Mat frame)就被传入cv::GComputation::apply()中。

官网测试图

cv::GComputation::apply()是一个多态方法(polimorphic method),它可以接受数量可变(variadic number)的参数。由于定义好了一个输入和一个输出,cv::GComputation::apply()的一个特殊重载函数就被用于传入输入参数,得到输出参数。
总而言之,cv::GComputation::apply()就根据给定输入参数来编译捕捉图(captured graph)并立即执行编译捕捉图。

针对这个例子中一些比较重要的概念可以列出如下大纲:
  • 图像(Graph)的声明和执行时分开的两个步骤
  • 图像是根据一系列的G-API表达式来隐式构建的
  • G-API支持一些类似函数的调用——比如cv::gapi::resize()以及一些重载操作符,比如 operator|() 一个用于按位或的操作符。
  • G-API的语法力求简洁,每一项调用操作即在一个图(Graph)中产生一个新的结果,从而形成了有向无环图(Directed Acyclic Graph,DAG)。
  • 图的声明并没有绑定任何的数据——真实数据(cv::Mat)是在图(Graph)已经定义之后才生成的。

PS:这里的图(Graph)实际上是不能看到的,OpenCV调用cv::imshow()函数现阶段是不支持cv::GMat格式的,只有转化为cv::Mat格式才能正常显示。

如果对G-API的特性和理念有兴趣,还可以上OpenCV看更多的例子和教程。教程和例子

以下是个人实验的部分

(没有拿去和普通OpenCV操作作对比。)
g-api.cpp中我加入了计时部分。编译时加入-DDEBUG显示时间
头文件额外引入#include <iomanip>,不然setprecision()可能不能用。

do
    {
        double start = (double)cv::getTickCount();
        ac.apply(input_frame, output_frame);
        cv::imshow("output", output_frame);
        double time_consume = ((double)cv::getTickCount() - start) / cv::getTickFrequency();
#ifdef DEBUG
        std::cout << "time_comsume = "<< time_consume * 100  //以s做单位没意义,改为ms
        << std::setprecision(2) << "ms"<<std::endl;
#endif
    } while (cap.read(input_frame) && cv::waitKey(30) < 0);

以下是测试的结果:


测试结果

CPU占用也如图所示,大概在30%左右,消耗的时间在调试控制台中显示,平均时间大概在0.3ms左右。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349