为OpenCV启用NVIDIA GPU加速视频解码

将CPU从高负载中解放出来

为OpenCV启用NVIDIA GPU加速视频解码

如今CPU的多媒体运算能力今非昔比,但是在某些特定场景下,还是需要将视频解码这样的“重活”分担给GPU去做。这篇文章记载了为OpenCV启用GPU加速解码的过程。

硬件与环境

  • GPU: Nvidia RTX 20/30系列
  • OS: Ubuntu 20.04
  • CUDA: 11.3
  • OpenCV: 4.5.4

在正式开始之前,请确保安装了合适的驱动程序与CUDA。

实现硬件加速的整个过程大致可以分为三个阶段。

第一,编译启用CUDA支持的FFMPEG

OpenCV本身没有视频解码能力,它依赖第三方库来实现这一功能。在Linux下最常用的为FFMPEG。所以,首先要为FFMPEG启用GPU加速。

NVIDIA在官网提供了FFMPEG编译指南:

Using FFmpeg with NVIDIA GPU Hardware Acceleration :: NVIDIA Video Codec SDK Documentation

按照该指南,你需要首先安装FFMPEG解码头文件。

git clone -b sdk/11.0 --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
cd nv-codec-headers && sudo make install && cd –

然后获取FFMPEG源代码。注意OpenCV对FFMPEG有版本要求。我当前测试通过的版本为4.3.3。下方是官方给出的示例,我推荐自行获取指定版本的源码。

git clone -b n4.3.3 --depth 1 https://git.ffmpeg.org/ffmpeg.git ffmpeg/

正式编译前,安装必要的依赖项。

sudo apt-get install build-essential pkg-config yasm cmake libtool libc6 libc6-dev unzip wget libnuma1 libnuma-dev libx264-dev libx265-dev

然后开始配置编译选项。这里的选项是我根据Nvidia与FFMPEG的指南合并得到的。

./configure \
    --prefix=/usr/local \ # 指定安装位置
    --extra-cflags=-I/usr/local/cuda/include \
    --extra-ldflags=-L/usr/local/cuda/lib64 \
    --extra-libs=-lpthread \
    --disable-static --enable-shared --enable-pic \ # 编译动态库
    --disable-ffplay \ # 服务器端可以省略显示功能
    --disable-doc \ # 省略文档
    --enable-cuda --enable-cuvid --enable-nvenc \ # 启用NVIDIA加速
    --enable-libx264 --enable-libx265 \ # 启用H264/265支持
    --enable-gpl --enable-nonfree

配置完成后,开始编译。

make -j 8

编译完成后,安装到系统目录。

sudo make install

手动执行下ldconfig。

sudo ldconfig

至此第一步结束。

第二步,编译启用CUDA支持的OpenCV

首先获取源码,注意除了 opencv 外,你还需要 opencv-contrib 模块。如果通过git获取源码,检出对应的tag 4.5.4。这个是我测试通过的版本。

获取源码后,使用cmake配置编译选项。如果条件允许,可以使用cmake-gui,更加方便。

使用cmake-gui

除了常规的配置,启用GPU支持需要开启以下选项。

  • 额外模块位置,对应cmake选项名 OPENCV_EXTRA_MODULES_PATH
  • 开启CUDA支持,对应cmake选项 WITH_CUDA

此时按下 configure ,执行第一次配置。

第一次配置完成后,需要手动启用硬件加速解码,对应cmake选项 WITH_NVCUVID

然后再次按下 configure ,完成第二次配置。

到这一步不应有任何报错。如果有错误需要按照提示修复后继续。

最后按下 generate 生成makefile,退出cmake,并开始编译OpenCV。

make -j 8

使用cmake

# 源码
OPENCV_SRC=/home/robin/download/opencv-4.5.4

# 额外组件
OPENCV_EXTRA_MODULES=/home/robin/download/opencv_contrib-4.5.4/modules

# 下载缓存地址
DOPENCV_DOWNLOAD_PATH=/home/robin/download/opencv_dependencies-4.5.4

# 安装目录
PREFIX=/home/robin/download/opencv-install

# 配置
mkdir opencv-build && cd opencv-build
cmake \
    -DCMAKE_INSTALL_PREFIX=${PREFIX} \
    -DOPENCV_EXTRA_MODULES_PATH=${OPENCV_EXTRA_MODULES} \
    -DOPENCV_DOWNLOAD_PATH=${DOPENCV_DOWNLOAD_PATH} \
    -DWITH_CUDA=ON \
    -DWITH_NVCUVID=ON \
    -DCUDA_ARCH_BIN=6.0,6.1,7.0,7.5,8.0,8.6 \
    -DBUILD_opencv_highgui=OFF \
    -DWITH_GTK=OFF \
    -DHIGHGUI_ENABLE_PLUGINS=OFF \
    ${OPENCV_SRC}

# 构建
make -j12

# 安装
make install

第三步,配置环境变量

在你的Python代码中添加环境变量强制启用GPU解码。

import os
os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"]="hw_decoders_any;cuda"
import cv2

cap = cv2.VideoCapture("input.mp4", 
    apiPreference=cv2.CAP_FFMPEG,
    params=[cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY]))
while True:
    r, img = cap.read()
    if r is False:
        break
    cv2.imshow('preview', img)
    cv2.waitKey(27)

在解码视频时,可以使用 nvidia-smi 工具查看硬件状况:

watch -n1 nvidia-smi -q --display=UTILIZATION

显示示例如下:

Attached GPUs                             : 1
GPU 00000000:01:00.0
    Utilization
        Gpu                               : 2 %
        Memory                            : 0 %
        Encoder                           : 0 %
        Decoder                           : 2 %

其中的 Decoder 即为解码器利用率。

总结

我实际测试中发现,在未启用GPU解码加速时,OpenCV会把视频解码任务分配给CPU的多个核心执行,一路全高清视频占用CPU资源合计约300%,FPS大约为500。在启用GPU加速后,运算被限制在一个核心,CPU的利用率100%。此时GPU的利用率约5%。FPS大约为170。所以,如何将硬件资源的利用最大化,还需要视具体任务合理分配。

以上就是全部内容。如果你有其它需求,可以参考下FFMPEG的编译指南:

CompilationGuide/Ubuntu – FFmpeg

祝解码顺利!

转发至

微信扫一扫分享

WeChat QR Code

有AI相关的想法,但不知道怎么落地?

我是国冰,独立AI开发者。十多年技术一线——半导体显示、智慧城市到自动驾驶;现在一个人+AI,接AI落地项目,从梳理需求到系统落地,陪你走通。

微信聊聊 微信二维码 看我能做什么