1. 简要回顾
首先简单复述一下FFmpeg中对深度学习的支持情况,如上图所示,FFmpeg在libavfilter中支持基于深度学习的filter,目前已经支持sr, derain和dnn_processing等filter,其中,dnn_processing是一个通用的filter,涵盖了sr和derain的功能,本文将要介绍的超分辨率(Super Resolution)功能也将使用dnn_processing来完成。
为了实现模型推理功能,FFmpeg提供了三种不同的方式(被称为后端backend),分别是native 后端、tensorflow后端和openvino后端。native后端的所有代码实现都在FFmpeg代码树中,目前以C语言的形式存在,可以跑在各种host端CPU上。tensorflow后端是指FFmpeg调用动态库libtensorflow.so进行模型加载和推理,可以跑在x86 cpu、NV GPU等各类计算设备上。openvino后端是指FFmpeg调用动态库libinference_engine_c_api.so进行模型加载和推理,可以跑在x86 cpu、Intel GPU等各类计算设备上。顺便提一下,由于FFmpeg的要求,不能调用C++接口,所以这些动态库都是提供C语言接口的库文件。
另外,在filter和后端之间,设计了一个用来解耦的接口层DNN interface,这样,filter可以很方便的从这个后端切换到另外一个后端,方便FFmpeg的开发者和用户无需修改代码,只需简单修改参数即可无缝切换到不同的计算设备上。
接下来将介绍如何使用tensorflow后端在NV GPU上加速Super Resolution,以及如何使用OpenVINO后端在Intel GPU上加速Super Resolution。
2. 资料准备
Super Resolution最本质上就是对视频图片的放大,在深度学习技术的加持下效果更好。我们采用ESPCN模型(Efficient Sub-Pixel Convolutional Neural Network model),这是论文地址https://arxiv.org/abs/1609.05158。这个模型的实现非常简洁,用了4层,依次是64个5x5的卷积层,32个3x3的卷积层,4个3x3的卷积层,卷积层的参数都是same,维持feature map尺寸不变;最后一层是depth2space层,将最后的4个feature map转换为长宽都乘以2的一张图片。
所有的模型文件和测试码流都已经放在了https://github.com/guoyejun/dnn_processing/tree/master/sr1080p, 可以直接下载。其中,onemin960x540.mp4是时长1分钟960x540分辨率的一段码流,用于输入测试;espcn.pb是tensorflow格式的模型文件;我们通过OpenVINO提供的格式转换工具,将espcn.pb文件转换成OpenVINO的格式,由于一些限制,OpenVINO格式文件对输入图片的大小有具体的数值要求(这个限制可以在OpenVINO后端中解决,但是尚未完成),所以,我们将使用espcn1080p.bin和espcn1080p.xml模型文件,来实现将输入的960x540分辨率的码流,转换成1080p(1920x1080分辨率)的码流。
要使用TensorFlow后端,只需要下载onemin960x540.mp4和espcn.pb;要使用OpenVINO后端,则需要下载onemin960x540.mp4、espcn1080p.bin和espcn1080p.xml。
3. 用NV GPU加速SR功能
要用NV GPU,软件库的调用关系是:FFmpeg -> TensorFlow -> cuDNN -> CUDA。其中的TensorFlow必须是提供C语言接口的库文件,而通过pip install tensorflow
命令安装的软件包里面并不包含c接口的库。为了得到c接口的库文件,一种方法是我们从TensorFlow源代码开始编译出c接口库文件,但是往往涉及到各种选项,并不是一件容易的事情。另外就是使用TensorFlow的预编译好的c库文件,下载其中的Linux(支持 GPU)版本。很不幸的是,目前预编译的最新2.3 GPU版本存在问题,而预编译中的上一版本则是1.15,所以,我们只能选择TensorFlow 1.15 GPU的c库文件 。
选定TensorFlow 1.15后,根据TensorFlow文档,还需要在系统中安装配套的cuDNN7.4和CUDA10.0,在原生系统中配置起来太麻烦,我们就用docker image来完成,使用NV给出的官方image。根据NV的release notes,我们找到nvcr.io/nvidia/tensorflow:18.10-py2 是我们需要的image。所以,后续操作如下所示。
// 下载docker image
$ docker pull nvcr.io/nvidia/tensorflow:18.10-py2
// 进入container,用了--rm选项,退出的时候container会被自动删除
$ nvidia-docker run --rm -it --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 nvcr.io/nvidia/tensorflow:18.10-py2
// 以下操作都在container中进行
// 在这个container中已经支持从python调用tensorflow,但是没有c库,所以,还需要下载。
# mkdir tfclib
# cd tfclib/
// 下载gpu版本压缩包。如果这里下载的是cpu版本的压缩包,那么后续tensorflow会使用cpu进行推理。
# wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
# tar zxvf libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/workspace/tfclib/lib/
// FFmpeg默认不支持TensorFlow后端,必须从源代码重新编译
# cd /workspace
# git clone --depth=1 https://git.ffmpeg.org/ffmpeg.git
# cd ffmpeg/
# mkdir build
# cd build/
// 准备依赖库
# apt-get update
# apt install nasm
# ../configure --enable-libtensorflow --extra-cflags="-I/workspace/tfclib/include" --extra-ldflags="-L/workspace/tfclib/lib"
# make
// 下载测试资料
# wget https://github.com/guoyejun/dnn_processing/raw/master/sr1080p/onemin960x540.mp4
# wget https://github.com/guoyejun/dnn_processing/raw/master/sr1080p/espcn.pb
// 运行测试
// 这个时候我们在host端运行nvidia-smi,会观察一些奇怪现象,不知道最新版本是否已经修复。
// 比如,这么轻量级的模型竟然花了90%以上的显存,我换成python直接运行也是如此。
// 还有,这么轻量级的模型第一次推理竟然花了十多分钟乃至数十分钟。第二次运行的一开始也等待了好久。
// 运行结束后,用ffplay等工具检查一下输出结果(即视频文件espcn.tf.gpu.mp4)是否符合预期
# ./ffmpeg -i onemin960x540.mp4 -vf format=yuv420p,dnn_processing=dnn_backend=tensorflow:model=espcn.pb:input=x:output=y -y espcn.tf.gpu.mp4 -benchmark
在有些系统中,可能会插有多块NV显卡,我们可以用下面的方法来指定使用其中的一块或者若干块显卡。首先,要用下面的python脚本生成一串字符串:
import tensorflow as tf
// 这里的参数0表示指定使用gpu0,如果是"1,2"就表示使用gpu1和gpu2。
// 需要说明的是,这里的数字,可能和nvidia-smi显示的序号不一致。
gpu_options = tf.GPUOptions(visible_device_list='0')
config = tf.ConfigProto(gpu_options=gpu_options)
s = config.SerializeToString()
b = ''.join("%02x" % int(ord(b)) for b in s[::-1])
print('0x%s' % b)
假如上述python脚本的输出是0x30012a0332,那么指定使用gpu0的FFmpeg命令行如下所示,将这个字符串作为dnn_procesing的参数传递进去。
# ./ffmpeg -i onemin960x540.mp4 -vf format=yuv420p,dnn_processing=dnn_backend=tensorflow:model=espcn.pb:input=x:output=y:options=sess_config=0x30012a0332 -y espcn.tf.gpu.mp4 -benchmark
4. 用Intel GPU加速SR功能
要用Intel GPU,目前的软件库的调用关系是:FFmpeg -> OpenVINO -> clDNN -> OpenCL。在host系统中,这些软件库都用最新版本,是可以从源代码编译出来的,而且,默认情况下,OpenVINO的c库也会被编译出来。但是,我们还是使用更加简洁的docker image方案。在https://hub.docker.com/r/openvino/ 可以看到OpenVINO官方给出的image。由于dev image中没有安装git,所以在docker run的时候要加上-u root
以获得apt-get install git的权限。在container中,从FFmpeg源代码开始的过程,和上一小节非常类似,只需要将TensorFlow相关的设置修改为OpenVINO相关的设置即可。所以,我们在这里换一种方法,将这些过程写到Docker文件中。
首先,创建一个文件,文件名为DockerFile,内容如下所示,主要是在openvino/ubuntu18_dev:2021.1的基础上增加FFmpeg软件包。
From openvino/ubuntu18_dev:2021.1
USER 0
RUN apt-get update
RUN apt-get install -y git nasm wget
WORKDIR /workspace
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/intel/openvino/inference_engine/lib/intel64:/opt/intel/openvino/inference_engine/external/tbb/lib:/opt/intel/openvino/deployment_tools/ngraph/lib
RUN git clone --depth=1 https://github.com/FFmpeg/FFmpeg ffmpeg && \
cd ffmpeg && \
./configure \
--extra-cflags=-I/opt/intel/openvino_2021/inference_engine/include/ \
--extra-ldflags=-L/opt/intel/openvino_2021/inference_engine/lib/intel64 \
--enable-libopenvino && \
make -j $(nproc) && \
make install
RUN wget https://github.com/guoyejun/dnn_processing/raw/master/sr1080p/onemin960x540.mp4 && \
wget https://github.com/guoyejun/dnn_processing/raw/master/sr1080p/espcn1080p.bin && \
wget https://github.com/guoyejun/dnn_processing/raw/master/sr1080p/espcn1080p.xml
然后,运行以下命令
// 在DockerFile所在的目录,创建image
$ docker build --network=host $(env | grep -E '_(proxy|REPO|VER)=' | sed 's/^/--build-arg /') -t openvino_ffmpeg .
// 上一步得到的image支持CPU、GPU等多个计算设备,下面的命令参数使得container只支持GPU,
// 为什么需要device参数,这是为了OpenCL驱动在container中使用的。
$ docker run -it --rm --device /dev/dri:/dev/dri openvino_ffmpeg:latest
// 下条命令在container中进行
// 运行结束后,用ffplay等工具检查一下输出结果(即视频文件espcn.ov.gpu.mp4)是否符合预期
root@b31ec026f1dc:/workspace# ffmpeg -i onemin960x540.mp4 -vf format=yuv420p,dnn_processing=dnn_backend=openvino:model=espcn1080p.xml:input=x:output=espcn/prediction:options=device=GPU -y espcn.ov.gpu.mp4 -benchmark
以上内容是本人业余时间兴趣之作,限于水平,差错难免,仅代表个人观点,和本人任职公司无关。