【2023 · CANN训练营第一季】数据预处理-JPEG数据解码
文档参考:CANN文档社区版: 6.0.RC1.alpha001
我们的模型输入是固定的图片格式、大小,但是实际上我们的想要进行推理的图片是各种各样的,在应用中,我们常使用NV12的图片作为模型的输入。如果输入是仅有Y分量的灰度图,我们便可以通过将UV分量的部分补充为灰色而满足我们的使用,但是如果我们的输入为JPEG图片,我们需要使用到ACL提供的媒体数据处理了相关API,对我们的图片进行预处理,把jpg图片进行解码成YUV格式的内容,并且获得其图片信息。
一、媒体数据处理API的V1
note:媒体数据处理V1版本与媒体数据处理V2版本的接口功能范围相同,都是描述处理媒体数据的接口,用于实现抠图、图片缩放、格式转换等功能,但两套接口不能混用
(资料图片)
在Device侧,我们对要进行媒体数据处理的相关数据的内存的申请和释放,就不可以简单的使用malloc,new或者aclrtmalloc等申请的内存了,我们需要使用dvpp接口。并且,从性能角度,为了减少拷贝,媒体数据处理的输出作为模型推理的输入,实现内存复用。我们尽量申请足够大的内存进行反复使用,尽可能避免反复使用dvpp的接口申请内存和释放,造成一定的性能损耗。
1.dvpp内存和通道
(1)内存申请与释放
note:调用该接口申请大页内存失败,仅表示系统内的大页内存不够
(2)通道的创建与释放
2.JPEGD相关接口
性能指标:
JPEGD性能指标是基于硬件解码的性能,JPEGD硬件解码不支持3个SOS的图片解码,对于硬件不支持的格式,会使用软件解码,软件解码性能参考为1080P 15fps。JPEGD解码的输出图片如果涉及旋转,则性能指标低于软件解码的参考值,例如对于1080P的图片,性能指标低于15fps。
以下性能数据,是基于一个stream上下发1个异步媒体数据处理任务后,执行一次aclrtSynchronizeStream接口。
在我们代码使用的过程中,其实最先需要了解的是我们所用的接口的性能,这直接影响到我们的使用方案,可以防止我们的代码设计直接超越了模块处理的性能。实际和官方标量有的时候会有一点实测的出入,不过最好还是按照手册中的描述进行预先规划,以下为手册中的指标:
功能说明:
JPEGD在解码图片时,支持对图片进行旋转。
JPEGD在解码图片时,支持按源图片格式解码。
图片分辨率约束:
输入图片分辨率:最大分辨率:8192x8192,最小分辨率:32x32。
输出图片分辨率:JPEGD只对图片解码,不会改变图片分辨率,因此输出与输入的图片分辨率保持一致。
输出的宽高对齐:
这里我仅写出了我们下方代码输出的yuv420SP(NV12 8bit)的情况
宽stride为宽128对齐后的值。
高stride为高16对齐后的值。
内存大小(单位Byte)≥ 宽stride * 高stride * 3/2
接口说明:
二、代码验证与学习
代码思路:
1.初始化好后,读取一个jpg图片并获得其图像信息,存入aclrtMalloc存入的内存中
2.创建dvpp通道,并将其解码为获取到的图像信息大小的yuv图片
3.按照次序销毁相关创建
好啦!让我们编译它!
有报错,查看错误代码,和该处代码:
发现是销毁时销毁顺序有问题,把Device和context都销毁了,才销毁通道。
所以,一定要注意销毁顺序!!!之前也提到过了,先创建->后销毁,后创建->先销毁!!!
修改后:
代码:
附上调试好的正确的代码:
runDecode.cpp(主函数文件)
dvpp_decode.cpp(decode处理文件)
dvpp_decode.h
cmakelist可以参考我之前的文章里有。
到这里就结束啦,想要存下来看看结果的同学也可以存成.yuv文件用yuvplayer打开看看哦!
ps:该文仅是为了记录CANN训练营的学习过程所用,不参与任何商业用途,有任何代码问题可以和我一起讨论修改