JPEGE

This section describes the API call sequence of JPEGE, and sample code is also provided to help you better understand the process.

The JPEG encoder (JPEGE) encodes YUV images into .jpg images. For details about the JPEGE functions and restrictions, see Overview.

API Call Sequence

If a YUV image needs to be encoded into a JPEG image during app development, ensure that your app contains the code logic for such image encoding. For details about the API call sequence, see AscendCL API Call Sequence.

Figure 1 JPEG image encoding

The current system supports YUV to JPG image encoding. The key APIs are described as follows:

  1. Call acldvppCreateChannel to create an image processing channel.

    Before creating a channel, call acldvppCreateChannelDesc to create a channel description.

  2. Call acldvppCreateJpegeConfig to create an image encoding configuration.
  3. Before JPEG encoding, call acldvppMalloc to allocate device memory as input and output buffers.

    Before allocating output buffer, call acldvppJpegPredictEncSize to predict the required output allocation size based on the input image description and the image encoding configuration.

    The actual size may be different from the size predicated by using the acldvppJpegPredictEncSize call. To obtain the actual output allocation size, inspect the output size of the acldvppJpegEncodeAsync call.

  4. Call acldvppJpegEncodeAsync to run asynchronous encoding.

    Call aclrtSynchronizeStream to wait for the stream tasks to complete.

  5. Call acldvppDestroyJpegeConfig to destroy the image encoding configuration data.
  6. After the encoding is complete, call acldvppFree to free the input and output buffers in a timely manner.
  7. Call acldvppDestroyChannel to destroy the image processing channel.

    After destroying the channel, call acldvppDestroyChannelDesc to destroy the channel description.

Sample Code

You can view the complete code in Image Classification with ResNet-50 (Image Decoding+Cropping and Resizing+Image Encoding+Synchronous Inference).

This section focuses on the code logic for encoding JPEGE images. For details about how to initialize and deinitialize AscendCL, see Initializing AscendCL. For details about how to allocate and deallocate runtime resources, see Runtime Resource Allocation and Deallocation.

After APIs are called, you need to add exception handling branches and record error logs and info logs. The following is a code snippet of key steps only, which is not ready to be built or run.

 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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 1. Initialize AscendCL.

// 2. Allocate runtime resources.

// 3. Create description of the data processing channel. dvppChannelDesc_ is of type acldvppChannelDesc.
dvppChannelDesc_ = acldvppCreateChannelDesc();

// 4. Create a data processing channel.
aclError ret = acldvppCreateChannel(dvppChannelDesc_);

// 5. Allocate input buffer (run mode specific).
// Call aclrtGetRunMode to obtain the run mode of the software stack. If ACL_DEVICE is returned, allocate and use the device buffer. If ACL_HOST is returned, transfer the input image data to the device by using the aclrtMemcpy call. After the data transfer is complete, the buffer needs to be freed in a timely manner.
aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
// inputPicWidth and inputPicHeight respectively indicate the width and height of an image after alignment. The following uses a YUV420SP image as an example.
uint32_t PicBufferSize = inputPicWidth * inputPicHeight * 3 / 2;
if(runMode == ACL_HOST){ 
    // Allocate host buffer vpcInHostBuffer.
    void* vpcInHostBuffer = nullptr;
    vpcInHostBuffer = malloc(PicBufferSize);
    // Load the input image into the buffer. The ReadPicFile function is defined by the user.
    ReadPicFile(picName, vpcInHostBuffer, PicBufferSize);
    // Allocate the device buffer inDevBuffer_.
    ret = acldvppMalloc(&inDevBuffer_, PicBufferSize);
    // Transfer the input image data to the device by using the aclrtMemcpy call.
    ret = aclrtMemcpy(inDevBuffer_, PicBufferSize, vpcInHostBuffer, PicBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    // Free the buffer in a timely manner after data transfer is complete.
    free(vpcInHostBuffer);
} else {
    // Allocate input buffer inDevBuffer_ on the device.
    ret = acldvppMalloc(&inDevBuffer_, PicBufferSize);
    // Load the input image into the buffer. The ReadPicFile function is defined by the user.
    ReadPicFile(picName, inDevBuffer_, PicBufferSize);
}

// 6. Create the description of the input image to be encoded and set the attribute values.
// encodeInputDesc_ is of type acldvppPicDesc.
encodeInputDesc_ = acldvppCreatePicDesc();
acldvppSetPicDescData(encodeInputDesc_, reinterpret_cast<void *>(inDevBuffer_));
acldvppSetPicDescFormat(encodeInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
acldvppSetPicDescWidth(encodeInputDesc_, inputWidth_);
acldvppSetPicDescHeight(encodeInputDesc_, inputHeight_);
acldvppSetPicDescWidthStride(encodeInputDesc_, encodeInWidthStride);
acldvppSetPicDescHeightStride(encodeInputDesc_, encodeInHeightStride);
acldvppSetPicDescSize(encodeInputDesc_, inDevBufferSizeE_);

// 7. Create image encoding configuration data and set encoding quality.
// The encoding level range is [0, 100]. The quality of level 0 is similar to that of level 100. A smaller value in the range [1, 100] indicates poorer output image quality.
jpegeConfig_ = acldvppCreateJpegeConfig();
acldvppSetJpegeConfigLevel(jpegeConfig_, 100);

// 8. Allocate output buffer and device buffer encodeOutBufferDev_ to store the encoded output data.
uint32_t outBufferSize= 0;
ret = acldvppJpegPredictEncSize(encodeInputDesc_, jpegeConfig_, &outBufferSize);
ret = acldvppMalloc(&encodeOutBufferDev_, outBufferSize);

// 9. Perform asynchronous encoding and call aclrtSynchronizeStream to wait for the stream tasks to complete.
ret = acldvppJpegEncodeAsync(dvppChannelDesc_, encodeInputDesc_, encodeOutBufferDev_,
            &outBufferSize, jpegeConfig_, stream_);
ret = aclrtSynchronizeStream(stream_);

// 10. Destroy the allocations, including the description of the input and output images, input and output buffers, channel description, channel.
acldvppDestroyPicDesc(encodeInputDesc_);

if(runMode == ACL_HOST){ 
    // In this mode, the processing result is on the device. Therefore, you need to call the memory copy API to transfer the result data, and then free the device buffer.
    // Allocate host buffer outputHostBuffer.
    void* outputHostBuffer = nullptr;
    outputHostBuffer = malloc(outBufferSize_);
    // Transfer the processing result of the device to the host by using the aclrtMemcpy call.
    ret = aclrtMemcpy(outputHostBuffer, outBufferSize_, encodeOutBufferDev_, outBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
    // Free the input and output buffer on the device.
    (void)acldvppFree(inputDevBuff);
    (void)acldvppFree(encodeOutBufferDev_);
    // Free the buffer after the data is used.
    free(outputHostBuffer);
} else { 
    // The process is running on the device, and the processing result is also on the device. Free the device buffer when the data is no longer needed.
    (void)acldvppFree(inputDevBuff);
    (void)acldvppFree(encodeOutBufferDev_);
}
acldvppDestroyChannel(dvppChannelDesc_);
(void)acldvppDestroyChannelDesc(dvppChannelDesc_);
dvppChannelDesc_ = nullptr;

// 11. Deallocate runtime resources.

// 12. Deinitialize AscendCL.

// ....