很多朋友工作中会遇到需要使用gpu的例子,gpu就是显卡,大部分对gpu有个模糊的概念,即图像渲染会使用到他,但是它是如何生效的,又说不清楚。本篇文章主要介绍opencv下如何使用gpu进行编程。

基本介绍

opencv下存在gpu可以使用的函数接口,一般以cv::cuda:xxxx cv::cudacodec::等开头。cuda是nvidia公司提供的并行计算框架,也就是说opencv提供的cuda接口仅支持nvidia公司的显卡(个人理解)。至于其它gpu如何使用本篇不涉及。那我们有了nivida显卡,安装好了opencv库,是否就可以直接利用opencv提供的gpu函数进行操作了呢?答案是并不能,对于nivida显卡来说,有一块nivida显卡,安装好驱动仅仅是基础操作,还需要到nvidia官网下载对应的cuda包进行安装,还需要下载Video_Codec_SDK_11.1.5.zip包替换一些头文件才可以。下面会进行详细的介绍。

安装cuda

  1. 首先通过nvidia-smi命令查看显卡驱动支持的最高cuda版本。

 如上面引用所示,蓝色字体部分表示的是显卡驱动版本号,红色字体部分表示的是该显卡支持的最高cuda版本,我自己的显卡支持的cuda工具包最高版本号为11.2。

2.通过上面的命令查询到cuda版本号的最高版本号后,到NVIDIA官网下载符合要求的cuda工具包,官网截图如下:

opencv之利用gpu进行编程-LMLPHP

 然后根据自己系统的特征下载对应的版本即可,选择方式如下,大家应该都懂,这里不再展开:

opencv之利用gpu进行编程-LMLPHP

 这里更正一下,上图中选择runfile(local)即可。

下载后安装方式如下:

 执行上面的命令后,一步一步的顺序执行,需要注意的就是如果gpu已经安装了驱动就不需要再重新安装驱动了。安装完成后,在命令行中输入nvcc -V命令查询cuda工具包是否安装成功。

 如果出现上面的提示说明cuda工具包安装成功了。cuda工具包的安装路径在/usr/local/cuda-11.1

3.cuda工具包安装完成后需要通过配置才可以生效。配置方法如下:

vi /etc/profile

打开该配置文件后,在末尾添加:

export PATH=//usr/local/cuda-11.1/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH

通过source命令生效下该文件,但是soruce命令好像只能在该终端生效,该终端关闭后,这连个环境变量不会生效,为了永久生效,最好重启一下。

至此cuda工具包安装完成。

安装cuDNN

cuDNN适用于深度学习的,这里不再展开,感兴趣的朋友可以自行百度搜索相关文章。

安装Video_Codec_SDK工具包

下载地址:Video Codec SDK - Get Started | NVIDIA Developer

下载后解压缩文件,进入目录找到Read_Me.pdf文件,可以找到该sdk包对系统的要求,如下图所示:

opencv之利用gpu进行编程-LMLPHP

 如果驱动版本版本不满足或者cuda工具包不满足要去,则现在低版本的nvidia-video-codec-sdk即可。

为什么要安装该SDK工具包呢?

 因此只需要头文件即可,通过如下命令将头文件拷贝至cuda工具包目录:

cp Video_Codec_SDK_11.1.5/Interface/* /usr/local/cuda/include/

 如果没有这个步骤的话,编译opencv虽然不会发生错误,但是在再利用gpu编成的程序进行编译连接时会报错,错误信息如下:function/feature is not implement,the called functionality is disabled for current build or platform in function ‘throw_no_cuda’

参考连接:【AVD】Linux 编译支持 Cuda 的 OpenCV 4.6,解决报错 throw_no_cuda_深海Enoch的博客-CSDN博客

 安装ffmpeg

ffpmeg在opencv中的作用是什么呢?opencv依赖ffmpeg进行软解码,依赖nvidia-video-codec-sdk进行硬解码,如果未安装ffmpeg,那么opencv无法进行软解码,我的理解是无法读取视频文件或者拉流播放等工作。因此为了保证opencv既然软解码又能硬解码,ffmpeg和nvidia-video-codec-sdk都需要安装,nvidia-video-codec-sdk的安装在上一节已经介绍完毕,本届介绍如何安装ffmpeg。需要注意的是ffmpeg也提供了可以利用英伟达进行硬解码的方式,这样ffmpeg也可以利用英伟达显卡进行硬解码

安装ffmpeg的依赖项

到github下载对应版本的ffmpeg即可。

特别需要注意的是ffmpeg提供了可操作英伟达显卡的头文件,下载命令如下:

git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git

下载完成后的目录是nv-codec-headers,截图如下:

opencv之利用gpu进行编程-LMLPHP

 opencv之利用gpu进行编程-LMLPHP

从上面可以看到include目录中是一些头文件,头文件中是一些可以操作cuda工具包的函数,由此可以理解ffmpeg是利用cuda提供的api来实现对gpu的控制。

特别需要注意的是上图中的README文件中描述了该nv-codec-headers的依赖项:

opencv之利用gpu进行编程-LMLPHP

nv-codec-headers安装方法:

cd nv-codec-headers && sudo make install

 编译ffmpeg的脚本如下:

#!/bin/bash
  
./configure --enable-nonfree --enable-cuda-nvcc --enable-libnpp --extra-cflags=-I/usr/local/cuda/include --extra-ldflags=-L/usr/local/cuda/lib64 --disable-static --enable-shared
 
make -j$(nproc)
 
sudo make install
 
echo '/usr/local/ffmpeg/lib' >> /etc/ld.so.conf
 
ldconfig

 上述执行完成后,测试ffmpeg硬件访问和cuvid解码器:

ffmpeg -hwaccels
ffmpeg -codecs | grep cuvid

测试结果如下:

opencv之利用gpu进行编程-LMLPHP

ffmepg测试硬解码方法如下:

ffmpeg -y -vsync 0 -hwaccel cuda -hwaccel_output_format cuda -extra_hw_frames 5 -i out.mp4 -c:a copy -c:v h264_nvenc -b:v 5M out1.mp4

上面的一些选项我也不太清楚,感兴趣的朋友可以自行研究下。上述的命令是输入一个mp4文件,然后输出一个mp4文件。

重新编译opencv

上面的步骤完成后,需要通过cmke-gui命令重新编译opencv,因为opencv默认编译时不会打开cuda相关选项,opencv有很多的编译选项,其中一部分跟cuda有关。

cmake打开opencv的界面如下,我通过关键字cuda进行了过滤:

opencv之利用gpu进行编程-LMLPHP

通过上图可以看到WITH_CUDA,该选项一定要勾选;OPENCV_DNN_CUDA选项如果安装了cuDNN可以勾选;确认下CUDA_TOOLKIT_ROOT_DIR的路径是否正确,若与安装路径不一致,修改成cuda工具包的安装路径;CUDA_ARCH_BIN配置方法自行百度搜索。其它的勾选项与上面的一致就行了。

另外还有WITH_FFMPEG选项,这个一定要勾选上。

linux下opencv的编译方法可参考:Linux下OpenCV安装方法 - 知乎

按照上面连接的步骤点击cmake-gui上的Configure按钮,可以看到opencv下一些依赖项的配置信息,这里我们重点关注ffmpeg和NVIDIA的配置信息,其它类似,如下所示:

上面的NVCUVID选项一定要有,否则无法通过opencv无法利用gpu进行硬解码会报错,报错信息我这里是:function/feature is not implement,the called functionality is disabled for current build or platform in function ‘throw_no_cuda’

关于docker下的一些错误可以参考下面的信息,我从别的地方拷贝过来的,没有经过验证,需要朋友们自行验证:

 

#!/bin/bash
 
sopath=/usr/lib/x86_64-linux-gnu
 
if [ ! -L ${sopath}/libcuda.so ]; then
    files=(`find $sopath/libcuda.so*`)
    raw_so=${files[0]}
    echo Create soft link ${raw_so}
    ln -s ${raw_so} ${sopath}/libcuda.so
fi
 
if [ ! -L ${sopath}/libnvcuvid.so ]; then
    echo Create soft link ${sopath}/libnvcuvid.so.1
    ln -s ${sopath}/libnvcuvid.so.1 ${sopath}/libnvcuvid.so
fi
 
if [ ! -L ${sopath}/libnvidia-encode.so ]; then
    echo Create soft link ${sopath}/libnvidia-encode.so.1
    ln -s ${sopath}/libnvidia-encode.so.1 ${sopath}/libnvidia-encode.so
fi
ldconfig

 然后点击cmake-gui中的gennerate触发生成makefile文件。然后执行

make -j$(nproc)
 
make install

 opencv硬解码测试代码:

void MainWindow::playVideoByGpu()
{
    const std::string fname("rtsp://admin:consys123@192.168.0.64//Streaming/Channels/1");
    //cv::cuda::setGlDevice();
    cv::cuda::GpuMat d_frame;
    cv::cuda::GpuMat d_outFrame;
    cuda::Stream stream;
    cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader(fname);
    while(1)
    {
        if (!d_reader->nextFrame(d_frame))
            break;
        cuda::resize(d_frame,d_outFrame,Size(1920,1080));
        cv::Mat temp;

        d_outFrame.download(temp, stream);
        stream.waitForCompletion();
        cv::imshow("GPU", temp);
        if (cv::waitKey(30) > 0)
            break;
    }
}

但是有一点奇怪的是,在我的环境上使用gpu进行硬解码读取视频,然后在主线程调用imshow显示视频,并没有发现cpu占有率有明显下降,有知道原因的朋友可以给我留言,非常感谢!

参考连接:

OpenCV4.7.0、FFmpeg5.1 Nvidia GPU视频硬解码_opencv 硬解码_洪流之源的博客-CSDN博客

09-27 08:59