专栏首页优图实验室的专栏学术资讯 |【优Tech分享】视频AI应用中的C++插件化实践
原创

学术资讯 |【优Tech分享】视频AI应用中的C++插件化实践

引子:本文主要介绍了插件机制,特别是C++语言的插件框架设计,并对实际业务中的服务进行了插件化改造的实践。

对于一名开发人员,唯一不变的就是不断变化的需求。尤其是 to B 业务,来自外部客户的定制需求、来自内部的算法策略优化需求等,都容易使得项目变得非常复杂,从而可能导致维护管理耗时耗力又容易出错。

那么,如何改变这一现状呢?既然变化的需求是永远存在的,我们可以想想其他一些成功的软件是怎么设计的。

举个简单的例子:程序员的开发需求各种各样,从开发语言,到编译工具、调试工具、版本管理工具、代码格式对齐工具,甚至是一些如编程时听歌这种个性化需求,真是五花八门;那设计一个IDE时如果全考虑进去变成无数个版本或者是一个超大型软件,必然是不现实的。

那现在非常著名的一些工程是怎么实现的呢?IDE中,著名的Eclipse只有一个支持插件扩展功能的微内核,其他所有功能都是通过插件实现的;编辑器里,我们常用的VS Code同样依赖众多功能丰富的插件;浏览器界,Chrome、Firefox、Safari,无不支持插件扩展其功能。

那么,微内核架构+插件,是否就是我们要的答案?

说到插件(plug-in),很多人可能感到既陌生又熟悉。陌生的地方在于很多人没有开发过插件,更没有设计过插件系统架构;熟悉的地方在于我们日常就在浏览器、编辑器、IDE中使用插件。

插件的好处是可以在不修改核心系统源代码的情况下扩展更多的特性,且插件之间通常不会相互依赖、开发也相对独立,这也简化了软件的开发。一个典型的插件系统架构如下图所示:

一般而言,构建一个插件系统需要下列要素:

  • 面向接口编程
  • 动态加载插件
  • 插件管理器

在 C++ 中虽然不能直接利用Duck Typing的概念,也没有类似于Java里的 interface,但我们也可以通过使用抽象类来实现接口类。

动态加载插件包含两层含义:

1、在运行时载入插件的动态库(.so/.dll文件);

2、在运行时创建插件对象

对于第一点,C++很容易实现,操作系统提供了运行时载入动态库的API,如Win32下我们可以使用LoadLibrary函数,Linux下我们可以使用dlopen函数。打开动态库后,我们需要找到要调用的函数入口,在Linux中我们用dlsym函数来实现,这里简单介绍一下:

dlsym()函数的原型是

其中,handle是打开动态链接库后返回的指针,symbol是要获取的函数的名称,函数返回是需要获取的函数的地址指针。

不过这里有个细节需要注意:C++由于语言支持函数重载等机制,为了保证每个函数的符号名(symbol)唯一性,C++编译器用了名字修饰(name mangling)的技术来修改编译到库中的函数名,这就导致我们使用dlsym可能找不到要使用的函数入口;而C语言中一般函数名与符号名是相同的。因此,我们一般把需要动态库暴露给外部的函数加上extern "C"的声明,告诉编译器用C编译器的方式编译此函数,不要修改函数的符号名。一般情况下,我们习惯写一个这样的宏,放在需要动态库导出的函数声明前(这里暂不考虑windows平台,windows下还需要加__declspec(dllexport),这里不再赘述):

对于第二点,对主程序(微内核)而言,在代码编写和编译的阶段并不知道有哪些插件需要加载,也不知道插件类名,如何在运行时创建插件对象呢?

Java 中我们可以使用反射机制(JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先知道运行对象是谁)来实现,而 C++ 语言特性原生并不支持反射机制,因此需要我们自己实现。在 C++ 中,我们可以通过对象工厂模式+宏定义的方式来实现这个功能。简单来说,对象工厂类提供了创建插件类的方式,而宏定义提供了注册每种类型的对象工厂的方式。前者由于由于dlsym返回的是函数指针,我们无法通过 new 来创建类的示例,但我们可以调用工厂类的 create 函数来创建类实例;后者利用 C/C++ 的宏定义中的##,从而将字符串和变量串接起来,使得能自动拼接出对象工厂的类名,避免了自己手写 switch 函数。

插件管理器除了包含了动态加载插件功能以外,一般还会提供插件的注册与发现机制、插件类型管理,甚至是插件版本管理的功能。利用插件管理器可以方便地搭建插件系统。C++ 的插件管理器中,开源的Pluma(http://pluma-framework.sourceforge.net/)是一个较轻量但是完整的C++插件框架,可以参考其文档学习使用。

学习了插件框架后,我们回到实际的业务中,想要把服务做成插件化形式,首先要考虑的是:哪些模块做成核心微服务,而哪些模块做成插件?一般来说,我们首先需要对自己的服务进行足够的抽象化,程序入口及公共不变的核心部分作为微内核,可变的部分作为插件,一些底层公用的部分作为公共函数库。

那我们就来梳理和抽象一下视频AI应用算法处理服务的整个pipeline:

▲ 抽象化视频AI应用算法处理服务pipeline

视频流数据在实验室环境获取可能比较简单,比如直接用 OpenCV 提供的 VideoCapture 类,但是在实际工程环境要考虑很多复杂的问题(如不同平台硬解码的加速、RTSP等协议的支持、断线重连机制、buffer缓冲及延时控制、跳帧控制等),我们可以把视频流解码处理做成独立的服务,使用共享内存队列传输视频流数据,算法处理服务只是从共享内存队列读数据。既然是统一的从共享内存队列获取数据,我们可以将这一部分放入微内核模块,做成接口类的公共函数。

AI算法处理包括如人脸检测跟踪、人脸识别、人体ReID、车牌识别等等,一般会对视频帧图像做一些深度学习算法获取 Object,然后再对各个 Object 进行进一步处理,不同算法不尽相同,这里不做展开。

显然,这一部分的逻辑可变化空间非常大,很适合做成插件的形式。

当然,该模块的内部还是有很多共性的部分可以挖掘,比如初始化流程、配置文件的加载方式、处理视频帧和处理 Object 的线程等;这块我们暂时没有考虑做成插件的插件,而是做了一个模板样例,编写插件时可以有参考填充。如果有更好的方式的话也欢迎探讨。

AI算法处理完后一般会得到一些结构化数据,我们需要推送到云端或推送到本地业务层处理展示等等。

数据推送一般有些公共的方式,如把数据放入本地一个消息队列,使用 HTTP POST 请求、TCP Socket、UNIX Domain Socket 等方式发送,这一部分我们可以做成公共函数,放入微内核模块来调用。

然而,结构化数据的封装各式各样,有 protobuf、JSON 等等,不同算法的字段内容也不一致,这块消息格式的定义我们可以考虑做成插件来加载。

通过插件化的改造,我们的服务从4个版本收敛到了1个版本+多个插件,版本维护的成本降低很多,也便于新业务开发与分工。

视频AI应用的插件化框架里还有诸如配置管理、设备管理等比较琐碎的模块,在此就不做赘述,后续完善后会计划开源此框架。有不足和建议欢迎指正和探讨。


原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 新闻动态|追加10亿!腾讯宣布设立15亿元“战疫基金”

    刚刚,腾讯宣布设立15亿元“抗击新型冠状病毒感染肺炎疫情综合保障基金”(以下简称战疫基金)。

    优图实验室
  • 产品资讯 | 轻松化身“和平精英”,与火箭少女101一起解锁腾讯优图人脸融合新玩法

    想不想突破次元壁,变身“和平精英”里的角色人物?想不想与火箭少女101来一场亲密合影?近日,火箭少女101主题活动已登陆手游“和平精英”,通过腾讯优图实验室最新...

    优图实验室
  • 六一儿童节 | AI技术带你一键返回童年,找回年少时光

    人像变换基于深度学习不同年龄段人群的共同高维特征,生成一张目标年龄的人脸图片,让你一键穿越回童年。

    优图实验室
  • IntelliJ IDEA 18 周岁,吐血推进珍藏已久的必装插件

    IntelliJ IDEA是目前最好最强最智能的Java IDE,前几天,他刚刚年满18岁。

    用户1655470
  • cordova学习四 插件集成

    4.1插件添加          插件集成命令在项目的根目录比如myapp则是在../myapp这个目录下 一般集成插件都是在github也可以是下载好的 ...

    cfs
  • scheduling-framework功能介绍

    scheduling framework 是Kubernetes Scheduler的一种新的可插入架构,可简化调度程序的自定义, 它向现有的调度程序中添加了一...

    有点技术
  • 萌妹子语音陪你写代码,一个神奇的 VSCode 插件

    最近在 GitHub 发现了一个有趣的 VSCode 插件:Rainbow Fart。在你写代码的时候,可根据关键字播放接近代码含义的语音。

    Enjoy233
  • 推荐个软件:uTools

    更多的内容可以去看一下差评的推文或者官方文档,我这篇文章主要放一下刚用起来就觉得挺不错的一些功能

    yichen
  • 从Elasticsearch的插件实现机制见:如何在Java中实现一个插件化系统

    此外,笔者还对Java的流行插件框架PF4J进行的简单的了解,发现其实现方式和ES比较相似:都是由ClassLoader实现,感兴趣的读者可以自行了解。

    franyang
  • 【Chromium中文文档】插件架构

    背景 在阅读这个文档前,你应当熟悉Chromium的多进程架构。 概述 插件是浏览器不稳定的主要来源。插件也会在渲染器没有实际运行时,让进程沙箱化。因为进程是第...

    梦里茶

扫码关注云+社区

领取腾讯云代金券