实验要求

  1. JPEG图片解码+图片缩放 功能串联;
  2. PNG图片解码+图片缩放 功能串联;
  3. 判断输入图片格式,自动选择解码分支;
  4. 内存复用。

代码框架

根据实验要求,需要实现的功能主要就是图片的读取,JPEG和PNG图片的解码,利用VPC实现图片缩放,缩放后图片的保存。

变量定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
typedef enum Result
{
SUCCESS = 0,
FAILED = 1
} Result;

typedef struct PicDesc
{
std::string picName;
uint32_t width;
uint32_t height;
uint32_t jpegDecodeSize;
} PicDesc;

static void *GetDeviceBufferOfPicture(const PicDesc &picDesc, uint32_t &devPicBufferSize);
static char *ReadBinFile(std::string fileName, uint32_t &fileSize);
static Result SaveDvppOutputData(const char *fileName, const void *devPtr, uint32_t dataSize);
void SetInput(void *inDevBuffer, int inDevBufferSize, int inputWidth, int inputHeight);
void GetOutput(void **outputBuffer, int &outputSize);
void DestroyResource();

Result JPEGD(PicDesc testPic);
Result PNGD(PicDesc testPic);
Result resize(PicDesc testPic);
uint32_t AlignmentHelper(uint32_t origSize, uint32_t alignment);

int32_t deviceId_ = 0;
aclrtContext context_ = nullptr;
aclrtStream stream_ = nullptr;

acldvppChannelDesc *dvppChannelDesc_;

void *inDevBuffer_; // decode input buffer
uint32_t inDevBufferSize_; // dvpp input buffer size

uint32_t inputWidth_; // input pic width
uint32_t inputHeight_; // input pic height

void *decodeOutDevBuffer_; // decode output buffer
acldvppPicDesc *decodeOutputDesc_; // decode output desc

uint32_t outputWidth_; // resize pic width
uint32_t outputHeight_; // resize pic height
acldvppPicDesc *vpcOutputDesc_; // resize pic desc
bool ispng;

aclrtRunMode runMode;

初始化及资源管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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");

uint32_t image_width = 0;
uint32_t image_height = 0;
PicDesc testPic = {image_path, image_width, image_height};

INFO_LOG("start to process picture:%s", testPic.picName.c_str());
// dvpp process
/*3.Read the picture into memory. InDevBuffer_ indicates the memory for storing the input picture,
inDevBufferSize indicates the memory size, please apply for the input memory in advance*/
uint32_t devPicBufferSize;
void *picDevBuffer = GetDeviceBufferOfPicture(testPic, devPicBufferSize);
if (picDevBuffer == nullptr)
{
ERROR_LOG("get pic device buffer failed,index is 0");
return FAILED;
}
// 4.Create image data processing channel
dvppChannelDesc_ = acldvppCreateChannelDesc();
acldvppCreateChannel(dvppChannelDesc_);
INFO_LOG("dvpp init resource success");

图片读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 读取文件
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;
}

/***************************************************/
/* 获取JPEG或PNG图片信息 */
/***************************************************/


// HOST TO DEVICE
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;

保存YUV文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Result SaveDvppOutputData(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;
}

释放资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
void DestroyResource()
{
aclError ret;
inDevBuffer_ = nullptr;
if (decodeOutputDesc_ != nullptr)
{
acldvppDestroyPicDesc(decodeOutputDesc_);
decodeOutputDesc_ = nullptr;
}

if (vpcOutputDesc_ != nullptr)
{
acldvppDestroyPicDesc(vpcOutputDesc_);
vpcOutputDesc_ = nullptr;
}

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");
}

JPEG图片解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Result JPEGD(PicDesc testPic)
{
// InitDecodeOutputDesc
uint32_t decodeOutWidthStride = (inputWidth_ + 63) / 64 * 64; // 64-byte alignment
uint32_t decodeOutHeightStride = (inputHeight_ + 15) / 16 * 16; // 16-byte alignment

aclError ret = acldvppMalloc(&decodeOutDevBuffer_, testPic.jpegDecodeSize);
if (ret != ACL_SUCCESS)
{
ERROR_LOG("acldvppMalloc jpegOutBufferDev failed, ret = %d", ret);
return FAILED;
}

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);

ret = acldvppJpegDecodeAsync(dvppChannelDesc_, inDevBuffer_, inDevBufferSize_,
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;
}
}

PNG图片解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Result PNGD(PicDesc testPic)
{
// InitDecodeOutputDesc
uint32_t decodeOutWidthStride = (inputWidth_ + 63) / 64 * 64; // 64-byte alignment
uint32_t decodeOutHeightStride = (inputHeight_ + 15) / 16 * 16; // 16-byte alignment

aclError ret = acldvppMalloc(&decodeOutDevBuffer_, testPic.jpegDecodeSize);
if (ret != ACL_SUCCESS)
{
ERROR_LOG("acldvppMalloc jpegOutBufferDev failed, ret = %d", ret);
return FAILED;
}

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_RGB_888);
acldvppSetPicDescWidth(decodeOutputDesc_, inputWidth_);
acldvppSetPicDescHeight(decodeOutputDesc_, inputHeight_);
acldvppSetPicDescWidthStride(decodeOutputDesc_, decodeOutWidthStride);
acldvppSetPicDescHeightStride(decodeOutputDesc_, decodeOutHeightStride);
acldvppSetPicDescSize(decodeOutputDesc_, testPic.jpegDecodeSize);

ret = acldvppPngDecodeAsync(dvppChannelDesc_, inDevBuffer_, inDevBufferSize_,
decodeOutputDesc_, stream_);
if (ret != ACL_SUCCESS)
{
ERROR_LOG("acldvppPngDecodeAsync failed, ret = %d", ret);
return FAILED;
}

ret = aclrtSynchronizeStream(stream_);
if (ret != ACL_SUCCESS)
{
ERROR_LOG("aclrtSynchronizeStream failed");
return FAILED;
}
}

缩放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Result resize(PicDesc testPic)
{

/* processdecode */

// 输入采用JPEGD输出 decodeOutDevBuffer_ 和 decodeOutputDesc_

// 输出描述
vpcOutputDesc_ = acldvppCreatePicDesc();
// here is the VPC constraints, should be aligned to 16*2
uint32_t resizeOutWidthStride = (outputWidth_ + 15) / 16 * 16; // 16-byte alignment
uint32_t resizeOutHeightStride = (outputHeight_ + 1) / 2 * 2; // 2-byte alignment
uint32_t vpcOutBufferSize_ = resizeOutWidthStride * resizeOutHeightStride * 3 / 2;

acldvppMalloc(&inDevBuffer_, vpcOutBufferSize_);
acldvppSetPicDescData(vpcOutputDesc_, inDevBuffer_);
acldvppSetPicDescFormat(vpcOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
acldvppSetPicDescWidth(vpcOutputDesc_, outputWidth_);
acldvppSetPicDescHeight(vpcOutputDesc_, outputHeight_);
acldvppSetPicDescWidthStride(vpcOutputDesc_, resizeOutWidthStride);
acldvppSetPicDescHeightStride(vpcOutputDesc_, resizeOutHeightStride);
acldvppSetPicDescSize(vpcOutputDesc_, vpcOutBufferSize_);

// 缩放配置
acldvppResizeConfig *resizeConfig_ = acldvppCreateResizeConfig();
aclError ret = acldvppSetResizeConfigInterpolation(resizeConfig_, 0);

// 异步缩放
ret = acldvppVpcResizeAsync(dvppChannelDesc_, decodeOutputDesc_,
vpcOutputDesc_, resizeConfig_, stream_);
if (ret != ACL_SUCCESS)
{
ERROR_LOG("acldvppVpcResizeAsync failed, ret = %d", ret);
return FAILED;
}
ret = aclrtSynchronizeStream(stream_);
if (ret != ACL_SUCCESS)
{
ERROR_LOG("aclrtSynchronizeStream failed");
return FAILED;
}
return SUCCESS;
}

分支选择

对于判断输入PNG还是JPEG图片,采用后缀判断。

1
2
3
4
if (image_path.rfind(".png") >= 0 && image_path.rfind(".png") <= image_path.size())
ispng = true;
else
ispng = false;

根据标志位采取相应的图片信息读取接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
if (ispng)
{
int32_t channel;

aclError aclRet = acldvppPngGetImageInfo(inputBuff, inputBuffSize, &picDesc.width, &picDesc.height, &channel);
if (aclRet != ACL_SUCCESS)
{
ERROR_LOG("get png image info failed, errorCode is %d", static_cast<int32_t>(aclRet));
delete[] inputBuff;
fclose(fp);
return nullptr;
}

aclRet = acldvppPngPredictDecSize(inputBuff, inputBuffSize, PIXEL_FORMAT_RGB_888, &picDesc.jpegDecodeSize);
if (aclRet != ACL_SUCCESS)
{
ERROR_LOG("get png decode size failed, errorCode is %d", static_cast<int32_t>(aclRet));
delete[] inputBuff;
fclose(fp);
return nullptr;
}
}
else
{
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<int32_t>(aclRet));
delete[] inputBuff;
fclose(fp);
return nullptr;
}
// 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<int32_t>(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);
}

内存复用

内存复用体现在图片解码输入与缩放输出采用inDevBuffer_vpcOutputDesc_,解码输出和缩放输入采用decodeOutDevBuffer_decodeOutputDesc_