多媒体科技FFmpegFFmpeg与音视频流媒体工程开发相关

FFmpeg深度学习模块2019年小结

2020-01-21  本文已影响0人  郭叶军

FFmpeg是什么,通俗的说,在看片子的时候,需要用到视频播放器,而很多视频播放器的底层用的就是FFmpeg。这是我在2019年在FFmpeg深度学习模块中所做事情的总结,由于我在19年是这个模块的主要贡献者,所以,这个总结也差不多就是FFmpeg深度学习模块的现状总结了。

在2018年的时候,Pedro Arthur (bygrandao@gmail.com)带着学生Sergey Lavrushkin (dualfal@gmail.com)完成了一个GSoC项目,将TensorFlow引入FFmpeg,并且增加了一个超分辨率(super resolution)的video filer(vf_sr.c),来演示如何使用深度学习模型。

这里简单介绍一下GSoC,即Google Summer of Code,是Google公司赞助的项目,每年一次,到2020年将是第16次,用来鼓励学生参与各种开源项目的实际代码开发(不限于Google的开源项目,当然此项目也需要Google的同意),还有一定的经济资助。如果你是学生身份,强烈建议尽量参加。

回到FFmpeg,在18年的时候,我参与了这个模块的讨论,当时我的想法是,支持各种深度学习框架的模型,根据传入模型的类型,再调用相应的深度学习框架在后端加载执行。在今天事后来看,这个建议存在的一个问题是,有些深度学习框架没有提供C接口(可能提供了C++接口),而FFmpeg社区要求使用C接口,这个问题我现在也还没有解决方法。另外,我当时还建议把这个作为单独的模块,或者起码放在libavutil中,但是,最终还是放在了libavfilter目录下面,因为考虑到目前主要是被filter调用。

最后完成的代码支持深度学习框架TensorFlow,FFmpeg可以接受TensorFlow的网络模型文件(.pb),准备好输入输出后,再在后端调用TensorFlow的C库文件加载并执行这个模型。另外,考虑到某些系统可能没有TensorFlow,所以,在FFmpeg中增加了native cpu path的支持,即可以fallback到cpu path,这个native后端也定义了一套模型文件格式,即native model。为什么在native模式下不是直接接受.pb文件呢,因为在不使用第三方库(比如protobuf)的情况下解析加载pb文件也是挺麻烦的事情,需要很多的代码支持,没必要也不应该在FFmpeg中增加这样的代码。

由于其中不少代码都是hard code形式写死的,所以,我在2019年的时候,开始发patch来改进这里的代码,并且思考和实践如何让FFmpeg的用户将深度学习的相关功能用起来。大致总结如下。

虽然我把这个写在第一点,刚好可以顺便先做一个框架性的介绍。但是,实际是在发了不少patch被社区初步信任后,才做的目录调整,这也是参加开源社区工作的一个经验,仅供参考。

原先所有的代码文件都直接放在libavfilter目录下面,考虑到支持native mode将会需要很多代码,所以,在libavfilter目录下创建了子目录dnn,除了将dnn_interface.h这个接口文件继续直接放在libavfilter目录下,其他的dnn相关文件都被移到了dnn子目录中,当然,基于dnn的filter文件还是直接在libavfilter目录中。如下图所示。


FFmpeg中深度学习架构图

其中,vf_sr.c就是前面介绍的2018年GSoC完成的超分辨率filter;而去除雨点等功能的vf_derain.c则是刘歧(网名悟空,Steven Liu lq@chinaffmpeg.org)带着学生Xuewei Meng (xwmeng96@gmail.com)完成的GSoC2019项目;vf_dnn_processing.c是我做的基于dnn模型的一个通用的图像处理filter,希望所有只改变AVFrame内容的功能都可以用这一个filter来实现,目前已支持RGB和Gray等格式,而YUV相关格式的支持还在完成中;vf_dnn_analytic.c则是我设想中的filter,可以基于dnn模型从frame中提取一些信息,比如目标检测、人脸识别等等,可能会扩展成多个不同用途的filter,初步计划在2020年完成。

在libavfilter/dnn目录下,目前有两部分的内容,一是调用TensorFlow C library的接口层,二是实现native layer的代码,如果以后有需要,还可以增加更多的深度学习框架的C库支持。因为DNN interface接口层的存在,dnn的实现和基于dnn的filter之间是解耦的。

我在2019年在FFmpeg中的主要工作,除了从头开始支持ROI encoding外,就是从dnn filter到DNN interface再到dnn实现的全覆盖。filter和dnn实现互为表里,相互促进,目前两者都还处于比较初始的阶段,我希望可以做好初始推动力的作用,使得两者可以尽快形成良性循环正反馈,可以吸引更多的开发者和用户参加进来,也欢迎您的参与!

在初始代码中,native模型文件的生成方法,是根据相应的TensorFlow模型,手工一句句地写代码将模型导出到文件中。如果TF模型略有调整,也必须手工修改相应的代码,再重新生成native模型文件。这样的方法,无法扩展也不实用。所以,我写了个python脚本,输入是TF模型文件,输出是native模型文件,并且在native模型文件中增加了文件头和版本号等信息。这个脚本文件目前只支持当前FFmpeg支持的layer,需要继续完善。

在原来代码中,native模型的加载由一个函数完成,我将其拆分为了一个入口函数和若干子函数,每个子函数对应一个layer的加载。在原来代码中,所有nativer layer的执行函数放在一个文件中。我为每个layer在libavfilter/dnn目录下创建了一个新文件,对应的加载函数和执行函数都放在这个文件中,实现了不同layer之间在文件级别的隔离。在入口函数中,则用函数指针数组的方式来直接调用相应layer的函数,不使用if/else或者switch的方式,避免了大量的重复代码。

所以,现在如果要修改某个layer的实现,只需要修改相应的一个文件即可,不太会造成side effect。如果要增加一个layer,则只需要增加一个新的代码文件(.c和.h),然后注册到函数指针数组中即可,也不会存在误操作影响到已有代码的功能。

FATE (FFmpeg Automated Testing Environment) 是FFmpeg社区开发的自动化测试框架,要持续维护一个良好质量的软件项目,快速的自动化测试(包括单元测试、回归测试等等)都是必不可少的看门人角色,非常重要。基于FATE框架,我为每一个navier layer都增加了单元测试,以后有新的layer加入,只要依葫芦画瓢再增加就可以了。我也为vf_dnn_processing增加了测试。由于当前native layer中的卷积层conv2d的性能不佳,所以,还无法为vf_sr和vf_derain增加FATE测试,否则,耗时太久,反而影响了自动化测试的初始目的。

为了上述DNN接口层的调整,需要有实际的例子来表明这些调整是需要的,也为了不产生regression问题,需要增加更多nativer layer。另外,从项目本身来说,增加更多的nativer layer以支持更多的深度学习模型,比如目标检测等模型,也是题中应有之义。在19年下半年,带了上海交大密歇根学院5位同学的本科毕业设计课程,增加了一些native layer的实现,但没有upstream。

在之前的代码中,nativer layer是依次顺序排列,第i层的输出就是第i+1层的输入,无法支持一个层的输出作为多个层的输入,也不支持一个层的输入来自多个层。增加了Operand操作数概念后,层和层之间用Operand连接,就可以实现这些分支合并功能。如何使得Operand更好的节约内存、减少内存拷贝,目前还只有想法,尚未代码实现。

这部分工作,赵军Jun Zhao (barryjzhao@tencent.com)、刘歧Steven Liu( lq@chinaffmpeg.org)还有leozhang (leozhang@qiyi.com)、Zhao Zhili (quinkblack@foxmail.com)等也做了不少贡献。话说这样的统计功能怎么做啊,我是手工地肉眼看了一遍,大概率会遗漏什么,,,如有漏人,请告诉我。

最后,感谢老板的支持和社区的认可,我在2019年底成为了FFmpeg深度学习模块的maintainer。另外,我的大部分代码都是Pedro Arthur (bygrandao@gmail.com) review的,在此也表示感谢。

以上内容是本人业余时间兴趣之作,限于水平,差错难免,仅代表个人观点,和本人任职公司无关。

本文由博客一文多发平台 OpenWrite 发布!

上一篇下一篇

猜你喜欢

热点阅读