【CANN训练营】【2022第二季】【进阶班】 hw37497260 图像处理应用开发(解码,缩放,编码功能串接)
一、基本概念
二、功能、流程及接口调用
1.JPEG图片解码(JPEGD)
功能:将.jpg、.jpeg、.JPG、.JPEG图片解码成YUV格式图片
流程:
2. JPEG图片编码(JPEGE):
功能:将YUV格式图片编码成.jpg图片,用于直接展示等场景。
流程:
3. 视觉与处理模块(VPC)
流程:
4.接口调用:
- 调用acldvppCreateChannel接口创建图片数据处理的通道。 创建图片数据处理的通道前,需先调用acldvppCreateChannelDesc接口创建通道描述信息。
- 调用acldvppCreateResizeConfig接口创建缩放配置。
- 调用acldvppCreateJpegeConfig接口创建图片编码配置数据。
- 若需要申请Device上的内存存放输入或输出数据,需调用acldvppMalloc申请内存。 在申请输出内存前,可根据存放JPEG图片数据的内存,调用acldvppJpegPredictDecSize接口预估JPEG图片解码后所需的输出内存的大小。
- 调用acldvppJpegDecodeAsync异步接口进行解码。 对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
- 调用acldvppVpcResizeAsync异步接口,将输入图片缩放到输出图片大小。缩放后输出图片内存根据YUV420SP格式计算,计算公式:对齐后的宽 * 对齐后的高 * 3 / 2;对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
- 调用acldvppJpegEncodeAsync异步接口进行编码。 对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
- 调用acldvppFree接口释放输入、输出内存。
- 调用acldvppDestroyResizeConfig接口销毁缩放配置。
- 调用acldvppDestroyJpegeConfig接口销毁图片编码配置数据。
- 调用acldvppDestroyChannel接口销毁图片数据处理的通道。销毁图片数据处理的通道后,再调用acldvppDestroyChannelDesc接口销毁通道描述信息。
三、思路总结
要实现功能串接,内存复用,申请内存需求:输入图片读入,解码后的图片,缩放后图片,解码后图片。其中解码后的图片和缩放后的图片需要使用acldvppCreatePicDesc()接口创建输出图片描述信息。解码后,可以释放读入图片内存;执行缩放处理后,可以释放解码后图片内存。
流程:
- 运行管理资源申请:device,context,stream创建
- 将图片读入内存
- 创建图片数据处理通道描述信息及通道
- Device侧申请内存存放解码后图片,创建图片描述信息及图片描述属性设置
- 执行异步解码,同步等待
- 创建缩放配置
- Device侧申请内存存放缩放后图片,创建图片描述信息及图片描述属性设置
- 执行异步缩放处理,同步等待
- 创建图片编码配置数据
- 申请内存存放编码后图片
- 执行异步编码,同步等待
- 保存编码后图片
- 释放内存及资源
四、代码分析
1.为了能处理不同路径下的图片,并且得到不同大小的输出图片,将输入图片路径、输出图片的宽、输出图片的高作为main函数的参数:
//1.acl init
const char *aclConfigPath = "../src/acl.json";
aclInit(aclConfigPath);
INFO_LOG("acl init success");
//2.create Device,Context,Stream
aclrtSetDevice(deviceId_);
INFO_LOG("open device %d success", deviceId_);
// create context (set current)
aclrtCreateContext(&context_, deviceId_);
// create stream
aclrtCreateStream(&stream_);
aclrtGetRunMode(&runMode);
INFO_LOG("create stream success");
3.将图片读入内存,使用如下函数实现
void* GetDeviceBufferOfPicture(PicDesc &picDesc, uint32_t &devPicBufferSize)
{
if (picDesc.picName.empty()) {
ERROR_LOG("picture file name is empty");
return nullptr;
}
FILE *fp = fopen(picDesc.picName.c_str(), "rb");
if (fp == nullptr) {
ERROR_LOG("open file %s failed", picDesc.picName.c_str());
return nullptr;
}
fseek(fp, 0, SEEK_END);
uint32_t fileLen = ftell(fp);
fseek(fp, 0, SEEK_SET);
uint32_t inputBuffSize = fileLen;
char* inputBuff = new(std::nothrow) char[inputBuffSize];
size_t readSize = fread(inputBuff, sizeof(char), inputBuffSize, fp);
if (readSize < inputBuffSize) {
ERROR_LOG("need read file %s %u bytes, but only %zu readed",
picDesc.picName.c_str(), inputBuffSize, readSize);
delete[] inputBuff;
fclose(fp);
return nullptr;
}
acldvppJpegFormat format;
aclError aclRet = acldvppJpegGetImageInfoV2(inputBuff, inputBuffSize, &picDesc.width, &picDesc.height,
nullptr, &format);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("get jpeg image info failed, errorCode is %d", static_cast(aclRet));
delete[] inputBuff;
fclose(fp);
return nullptr;
}
INFO_LOG("get jpeg image info successed, width=%d, height=%d, format=%d, jpegDecodeSize=%d", picDesc.width, picDesc.height, format, picDesc.jpegDecodeSize);
// when you run, from the output, we can see that the original format is ACL_JPEG_CSS_420,
// so it can be decoded as PIXEL_FORMAT_YUV_SEMIPLANAR_420 or PIXEL_FORMAT_YVU_SEMIPLANAR_420
aclRet = acldvppJpegPredictDecSize(inputBuff, inputBuffSize, PIXEL_FORMAT_YUV_SEMIPLANAR_420, &picDesc.jpegDecodeSize);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("get jpeg decode size failed, errorCode is %d", static_cast(aclRet));
delete[] inputBuff;
fclose(fp);
return nullptr;
}
void *inBufferDev = nullptr;
aclError ret = acldvppMalloc(&inBufferDev, inputBuffSize);
if (ret != ACL_SUCCESS) {
delete[] inputBuff;
ERROR_LOG("malloc device data buffer failed, aclRet is %d", ret);
fclose(fp);
return nullptr;
}
if (runMode == ACL_HOST) {
ret = aclrtMemcpy(inBufferDev, inputBuffSize, inputBuff, inputBuffSize, ACL_MEMCPY_HOST_TO_DEVICE);
}
else {
ret = aclrtMemcpy(inBufferDev, inputBuffSize, inputBuff, inputBuffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);
}
if (ret != ACL_SUCCESS) {
ERROR_LOG("memcpy failed. Input host buffer size is %u",
inputBuffSize);
acldvppFree(inBufferDev);
delete[] inputBuff;
fclose(fp);
return nullptr;
}
delete[] inputBuff;
devPicBufferSize = inputBuffSize;
fclose(fp);
return inBufferDev;
}
4.创建图片数据处理通道描述信息及通道
dvppChannelDesc_ = acldvppCreateChannelDesc();
acldvppCreateChannel(dvppChannelDesc_);
INFO_LOG("dvpp init resource success");
5.计算图片内存
uint32_t decodeOutWidthStride = AlignmentHelper(inputWidth_, 128);
uint32_t decodeOutHeightStride = AlignmentHelper(inputHeight_, 16);
uint32_t decodeOutBufferSize = decodeOutWidthStride * decodeOutHeightStride * 3 / 2; // yuv format size
对齐后高宽通过AlignmentHelper函数计算
uint32_t AlignmentHelper(uint32_t origSize, uint32_t alignment)
{
if (alignment == 0) {
return 0;
}
uint32_t alignmentH = alignment - 1;
return (origSize + alignmentH) / alignment * alignment;
}
6.申请解码后图片内存
ret = acldvppMalloc(&decodeOutDevBuffer_, testPic.jpegDecodeSize);
if (ret != ACL_SUCCESS) {
ERROR_LOG("acldvppMalloc jpegOutBufferDev failed, ret = %d", ret);
return FAILED;
}
7.调用acldvppCreatePicDesc接口创建图片描述信息。调用acldvppSetPicDesc系列接口设置解码后图片的内存地址、内存大小、格式、widthStride、heightStride。
decodeOutputDesc_ = acldvppCreatePicDesc();
if (decodeOutputDesc_ == nullptr) {
ERROR_LOG("acldvppCreatePicDesc decodeOutputDesc failed");
return FAILED;
}
acldvppSetPicDescData(decodeOutputDesc_, decodeOutDevBuffer_);
// here the format shoud be same with the value you set when you get decodeOutBufferSize from
acldvppSetPicDescFormat(decodeOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
acldvppSetPicDescWidth(decodeOutputDesc_, inputWidth_);
acldvppSetPicDescHeight(decodeOutputDesc_, inputHeight_);
acldvppSetPicDescWidthStride(decodeOutputDesc_, decodeOutWidthStride);
acldvppSetPicDescHeightStride(decodeOutputDesc_, decodeOutHeightStride);
acldvppSetPicDescSize(decodeOutputDesc_, testPic.jpegDecodeSize);
8.执行异步解码,同步等待,释放读入图片内存
ret = acldvppJpegDecodeAsync(dvppChannelDesc_, picDevBuffer, devPicBufferSize,decodeOutputDesc_, stream_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("acldvppJpegDecodeAsync failed, ret = %d", ret);
return FAILED;
}
ret = aclrtSynchronizeStream(stream_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("aclrtSynchronizeStream failed");
return FAILED;
}
destroy_buffer(picDevBuffer);
destory_PicDesc(decodeOutputDesc_);
9.创建缩放配置
acldvppResizeConfig *resizeConfig_ = acldvppCreateResizeConfig();
10.计算缩放后图片内存大小
int resizeOutWidthStride = AlignmentHelper(outw, 16);
int resizeOutHeightStride = AlignmentHelper(outh, 2);
uint32_t vpcOutBufferSize_ = resizeOutWidthStride * resizeOutHeightStride * sizeAlignment / sizeNum;
11.申请缩放后图片内存
void *vpcOutBufferDev_ = nullptr;
acldvppMalloc(&vpcOutBufferDev_, vpcOutBufferSize_);
12.调用acldvppCreatePicDesc接口创建图片描述信息。调用acldvppSetPicDesc系列接口设置缩放后图片的内存地址、内存大小、格式、widthStride、heightStride。
vpcOutputDesc_ = acldvppCreatePicDesc();
if (vpcOutputDesc_ == nullptr) {
ERROR_LOG("acldvppCreatePicDesc decodeOutputDesc failed");
return FAILED;
}
acldvppSetPicDescData(vpcOutputDesc_, vpcOutBufferDev_);
// the format should be under the VPC constraints(only support the following 2):
// PIXEL_FORMAT_YUV_SEMIPLANAR_420 = 1
// PIXEL_FORMAT_YVU_SEMIPLANAR_420 = 2
acldvppSetPicDescFormat(vpcOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
acldvppSetPicDescWidth(vpcOutputDesc_, outw);
acldvppSetPicDescHeight(vpcOutputDesc_, outh);
acldvppSetPicDescWidthStride(vpcOutputDesc_, resizeOutWidthStride);
acldvppSetPicDescHeightStride(vpcOutputDesc_, resizeOutHeightStride);
acldvppSetPicDescSize(vpcOutputDesc_, vpcOutBufferSize_);
13.执行异步缩放处理,同步等待,释放解码后图片内存
ret = acldvppVpcResizeAsync(dvppChannelDesc_, decodeOutputDesc_,
vpcOutputDesc_, resizeConfig_, stream_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("acldvppVpcResizeAsync failed, ret = %d", ret);
return FAILED;
}
aclrtSynchronizeStream(stream_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("aclrtSynchronizeStream failed");
return FAILED;
}
destroy_buffer(decodeOutDevBuffer_);
14.编码配置
uint32_t encodeLevel = 100; // default optimal level (0-100)
jpegeConfig_ = acldvppCreateJpegeConfig();
INFO_LOG("Call acldvppCreateJpegeConfig success");
acldvppSetJpegeConfigLevel(jpegeConfig_, encodeLevel);
15.申请编码后图片内存
acldvppJpegPredictEncSize(vpcOutputDesc_, jpegeConfig_, &encode_outbuffer_size_);
aclError aclRet = acldvppMalloc(&encode_out_buffer_dev_, encode_outbuffer_size_);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("malloc encodeOutBufferDev_ failed, aclRet is %d", aclRet);
return FAILED;
}
16.执行异步编码,同步等待
aclRet = acldvppJpegEncodeAsync(dvppChannelDesc_, vpcOutputDesc_, encode_out_buffer_dev_,
&encode_outbuffer_size_, jpegeConfig_, stream_);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("acldvppJpegEncodeAsync failed, aclRet = %d", aclRet);
return FAILED;
}
INFO_LOG("Call acldvppJpegEncodeAsync success");
aclRet = aclrtSynchronizeStream(stream_);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("encode aclrtSynchronizeStream failed, aclRet = %d", aclRet);
return FAILED;
}
17.输出保存路径设置
int dir_tail_index = image_path.find("/data");
std::string outfile_dir = image_path.substr(0, dir_tail_index) + "/" + "out/output/";
std::string outfile_path = outfile_dir + image_path.substr(dir_tail_index+5+1, image_path.rfind(".yuv")-dir_tail_index-5-1) + "_jpege_" + std::to_string(inputWidth_) + "_" + std::to_string(inputHeight_) + ".jpg";
INFO_LOG("outfile_path=%s", outfile_path.c_str());
18.保存编码后图片,使用以下函数实现
Result save_dvpp_outputdata(const char *fileName, const void *devPtr, uint32_t dataSize)
{
FILE * outFileFp = fopen(fileName, "wb+");
if (nullptr == outFileFp) {
ERROR_LOG("fopen out file %s failed.", fileName);
return FAILED;
}
if (runMode == ACL_HOST) {
void* hostPtr = nullptr;
aclError aclRet = aclrtMallocHost(&hostPtr, dataSize);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("malloc host data buffer failed, aclRet is %d", aclRet);
fclose(outFileFp);
return FAILED;
}
aclRet = aclrtMemcpy(hostPtr, dataSize, devPtr, dataSize, ACL_MEMCPY_DEVICE_TO_HOST);
if (aclRet != ACL_SUCCESS) {
ERROR_LOG("dvpp output memcpy to host failed, aclRet is %d", aclRet);
(void)aclrtFreeHost(hostPtr);
fclose(outFileFp);
return FAILED;
}
size_t writeSize = fwrite(hostPtr, sizeof(char), dataSize, outFileFp);
if (writeSize != dataSize) {
ERROR_LOG("need write %u bytes to %s, but only write %zu bytes.",
dataSize, fileName, writeSize);
(void)aclrtFreeHost(hostPtr);
fclose(outFileFp);
return FAILED;
}
(void)aclrtFreeHost(hostPtr);
}
else {
size_t writeSize = fwrite(devPtr, sizeof(char), dataSize, outFileFp);
if (writeSize != dataSize) {
ERROR_LOG("need write %u bytes to %s, but only write %zu bytes.",
dataSize, fileName, writeSize);
fclose(outFileFp);
return FAILED;
}
}
fflush(outFileFp);
fclose(outFileFp);
return SUCCESS;
}
19.内存及资源释放
destory_PicDesc(vpcOutputDesc_);
destroy_buffer(vpcOutBufferDev_);
destroy_buffer(encode_out_buffer_dev_ );
destory_Resizeconfig(resizeConfig_);
destory_Jpegeconfig(jpegeConfig_);
acldvppDestroyChannel(dvppChannelDesc_);
destory_channelDesc(dvppChannelDesc_);
destroy_resource();
其中:
void destroy_resource()
{
aclError ret;
if (stream_ != nullptr) {
ret = aclrtDestroyStream(stream_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("destroy stream failed");
}
stream_ = nullptr;
}
INFO_LOG("End to destroy stream");
if (context_ != nullptr) {
ret = aclrtDestroyContext(context_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("destroy context failed");
}
context_ = nullptr;
}
INFO_LOG("End to destroy context");
ret = aclrtResetDevice(deviceId_);
if (ret != ACL_SUCCESS) {
ERROR_LOG("reset device failed");
}
INFO_LOG("End to reset device is %d", deviceId_);
ret = aclFinalize();
if (ret != ACL_SUCCESS) {
ERROR_LOG("finalize acl failed");
}
INFO_LOG("End to finalize acl");
}
void destory_PicDesc(acldvppPicDesc *picDesc)
{
if (picDesc != nullptr) {
(void)acldvppDestroyPicDesc(picDesc);
picDesc = nullptr;
}
}
其他函数类似,调用相关接口释放资源
注:先调用acldvppDestroyChannel接口销毁图片数据处理的通道。销毁图片数据处理的通道后,再调用acldvppDestroyChannelDesc接口销毁通道描述信息。
五、运行过程及结果
1. 购买ECS云服务器
2. 通过MobaXterm访问购买的云服务器
3. 切换到HwHiAiUser用户
4. 获取源码:命令行运行:git clone https://gitee.com/ascend/samples.git
可以看到目录中有samples文件夹
5. 上传准备好的图像处理应用文件夹
6. 编译应用:
进入scrpts目录下:bash sample_build.sh
同时测试图片已被下载到data路径下
原图:分辨率为1024*683
7. 运行应用
bash sample_run.sh
输出图片已保存到out/output路径下
输出图片:分辨率为224*224
- 点赞
- 收藏
- 关注作者
评论(0)