FFMPEG下利用Intel VPP_QSV插件实现基于GPU的图像缩放和色彩空间转换 (一) - 命令行模式

本文主要是介绍FFMPEG下利用Intel VPP_QSV插件实现基于GPU的图像缩放和色彩空间转换 (一) - 命令行模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近做图像预处理的工作有点多。这里记录一下最近做OpenVINO推理的端到端优化时对FFMPEG做图像预处理的一点心得。

 

这几天碰到的问题是在准备给mobilenet-ssd神经网络推理前需要对视频文件做解码,然后缩放到需要的分辨率再转成RGB格式的数据,这样才能丢进网络里做推理。这个解码,缩放,色彩转换的流程通常是通过FFMPEG来实现的,通常的代码实现流程是

  1. 先创建硬件设备,因为我是intel集成显卡,所以我指定了QSV来做解码的硬件加速器 ret = av_hwdevice_ctx_create(&decode.hw_device_ref, AV_HWDEVICE_TYPE_QSV, "auto", NULL, 0);
  2. 指定用hevc_qsv的硬件解码器来解HEVC码流 decoder = avcodec_find_decoder_by_name("hevc_qsv");
  3. 从视频文件里读一帧数据回来 ret = av_read_frame(input_ctx, &pkt);
  4. 如果数据是视频码流包,则丢给解码器去解码 ret = avcodec_send_packet(decoder_ctx, pkt);
  5. 等待一帧视频解码结束,此时解码出的NV12数据在GPU显存里 ret = avcodec_receive_frame(decoder_ctx, frame);
  6. 将数据从GPU内存中读回到系统内存中 ret = av_hwframe_transfer_data(sw_frame, frame, 0);
  7. 利用FFMPEG的swscale库来做基于CPU的图像缩放和色彩转换 sws_scale(sws_ctx, sw_frame->data, sw_frame->linesize, 0, sw_frame->height, pFrameRGB->data, pFrameRGB->linesize);
  8. 将转换好的数据丢给神经网络去推理
  9. goto step 3

流程示意图

对应的FFMPEG命令行大致等同于

ffmpeg -hwaccel qsv -c:v hevc_qsv -i 1.mp4 -vf hwdownload,format=nv12,scale=w=1920:h=1080 -pix_fmt rgb32 -f sdl -

这样的处理会有一些性能上的损失

  • 如果原始视频很大的话,比如4K的图像,那么从显存读数据到系统内存的函数av_hwframe_transfer_data的开销会很大
  • CPU做缩放和颜色空间转换效率不高,虽然swscale库已经优化的很好了,但是毕竟是用CPU资源做的,对于大量数据的处理,对CPU资源和内存带宽的压力都很大;如果推理是基于CPU的,那么无可避免的会带来不小的性能损失

 

优化时发现问题

更为高效的办法是将缩放和颜色空间的转换都放在GPU侧去做,由GPU把所有的数据预处理都做好,CPU只需要读回数据,就可以直接丢进推理网络里。

流程示意如下

如果要用GPU做图像缩放和色彩空间转换,网上有很多教程,例如用OpenCL代码来实现。但是对于我这个混子程序员来说,能不写代码就不写代码。经过一番搜索,终于让我得到了答案

在这里https://trac.ffmpeg.org/wiki/Hardware/QuickSync有这么命令

这个命令是利用FFMPEG的filter插件vpp_qsv, 将hevc_qsv解码出来的数据缩放成1920x1080分辨率,rgb32格式, 然后用hwdownload将数据读到系统内存,再送到sdl中显示。

照着这个命令运行一下,发现出错了。换了几个FFMPEG版本,结果都一样

 

解决问题 - 对FFMPEG源码的改动

什么叫不支持bgra的像素格式?百思不得其解。万般无奈,只能祭出VS2017的debug大法,经过一些跟踪和调试,发现问题出在创建vqq_qsv滤镜的时候, 在滤镜初始化创建directx的surface buffer的时候,会调用到libavutil\hwcontext_dxva2.c里dxva2_init_pool()函数,在line 172行左右会检查传进来的ctx->sw_format是否是dxva2 surface支持的像素格式。

而支持的像素格式在第79行

只支持NV12和P010? 实在不明白为啥,随手加个一行,看看能不能跳过去继续往下走?

编译FFMPEG, 再运行刚才的命令

图像竟然出来,还正常播放结束了!!!

 

性能数据的比较

下面来比较一下2个命令的CPU/GPU占用率和解码播放的速度有什么不同

#命令1, CPU做缩放和颜色转换
ffmpeg -hwaccel qsv -c:v hevc_qsv -i 1.mp4 -vf hwdownload,format=nv12,scale=w=1920:h=1080 -pix_fmt rgb32 -f sdl -#命令2, GPU做缩放和颜色转换
ffmpeg -hwaccel qsv -c:v hevc_qsv -i 1.mp4 -vf vpp_qsv=w=1920:h=1080:format=rgb32,hwdownload,format=rgb32 -f sdl -

先测试一下1.mp4 这是一个分辨率是640x480 h264编码的文件

CPU做缩放和颜色转换

播放速度2.03x

CPU占用率16%,可以看到有一个核基本是100%占用率。 GPU占用率39%

GPU做缩放和颜色转换

播放速度3.4x

CPU占用率16%,可以看到有一个核基本是70%占用率。 GPU占用率56%

小节

可以看出,

  • 基于GPU缩放和颜色转换的版本,播放速度是CPU版本的大约3.4x/2.03x=1.67x左右,速度有明显的提升
  • 有一个核的CPU占用率始终很高,但是GPU版本要好于CPU版本。因为有相当一部分CPU开销用在了sdl的显示上了,如果把-f sdl改为-f null, GPU版本的CPU占用率就变得非常低了

 

再测试一下4k-hevc.mp4 这是一个分辨率是3840x2160 h265编码的文件

CPU做图像缩放和色彩空间转换

有sdl显示 - 播放速度0.661x, CPU占用率18%, GPU占用率30%,

无显示     - 播放速度 0.852x

GPU做图像缩放和色彩空间转换

有sdl显示 - 播放速度1.51x, CPU占用率18%, GPU占用率58%

无显示     - 播放速度4.25x

小节

可以看出

  • 在高分辨率视频的情况下,GPU做缩放和颜色控制转换的版本的性能提升巨大
  • 在CPU和GPU间拷贝4K的视频数据会消耗大量的资源

 

最终总结和思考

问题解决了,FFMPEG的基于Intel QSV的一些滤镜用起来还是很方便的,比如vpp_qsv可以做图像分辨率缩放,色彩空间转换,帧率转换以及图像的截取crop以及各种图像效果增强等功能

具体的功能和参数可以用命令来查询

ffmpeg -h filter=vpp_qsv

 

但是有2个疑点还没有找到答案

  1. 首先想不明白为什么现在的FFMPEG代码里hwcontext_dxva2部分不支持创建RGB32格式的directx surface buffer, 那个https://trac.ffmpeg.org/wiki/Hardware/QuickSync的命令应该是2018年左右加上的,当时应该是能用的,但是现在已经不能用了。不知道是基于什么原因做了这种改动。(也许是因为大家都喜欢用GPU里面的opencl或者shader language自己来实现颜色空间转换吧,这样更灵活?)
  2. Intel的Media SDK里管做缩放和颜色空间转换的模块叫VPP (Video Post Process), 在实现上有2条路径来做,一个是走可编程的EU来做,另一条路是走专用的硬件(fixed function block)来做,这样功耗更低。具体可以参考MediaSDK github的说明, 但是这个readme也说的比较模糊,大致意思是如果数据放在系统内存里,会走EU来处理,如果数据放在显存里,会走Fixed Function来处理。我实在搞不清楚怎么看当前数据走的是哪条通道,windows下有什么工具能看GPU里的具体哪个模块在工作?

这几个问题只能留着慢慢以后再解决了...

这篇关于FFMPEG下利用Intel VPP_QSV插件实现基于GPU的图像缩放和色彩空间转换 (一) - 命令行模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/300998

相关文章

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug