认识

图像数据预处理应用在将源图的格式与模型要求不一致时,需要将源图处理为符合模型的要求,典型应用场景有:

  • 视频解码与缩放;
  • 图片解码、缩放与格式转换;
  • 抠图、缩放与格式转换。

CANN提供了两种处理图像/视频数据的方式:

  1. AIPP(Artificial Intelligence Pre-Processing)
    AIPP是指在AI Core上完成数据预处理。AIPP区分为静态AIPP和动态AIPP。

    • 静态AIPP用于多Batch情况下共用同一份AIPP参数;
    • 动态AIPP用于多Batch情况下使用不同的AIPP参数。
  2. DVPP(Digital Vision Pre-Processing)
    DVPP是昇腾AI处理器内置的图像处理单元,通过AscendCL媒体数据处理接口提供媒体处理硬加速能力。

AIPP、DVPP可以分开独立使用,也可以组合使用。组合使用场景下,一般先使用DVPP对图片/视频进行解码、抠图、缩放等基本处理,但由于DVPP硬件上的约束,DVPP处理后的图片格式、分辨率有可能不满足模型的要求,因此还需要再经过AIPP进一步做色域转换、抠图、填充等处理。

DVPP提供的功能有:

  1. VPC(Vision Preprocessing Core):处理YUV、RGB等格式的图片,包括缩放、抠图、图像金字塔、色域转换等。
  2. JPEGD(JPEG Decoder):JPEG压缩格式–>YUV格式的图片解码。
  3. JPEGE(JPEG Encoder):YUV格式–>JPEG压缩格式的图片编码。
  4. VDEC(Video Decoder):H264/H265格式–>YUV/RGB格式的视频码流解码。
  5. VENC(Video Encoder):YUV420SP格式–>H264/H265格式的视频码流编码。
  6. PNGD(PNG decoder):PNG格式–>RGB格式的图片解码。

目前有V1和V2两个版本的接口,但两套接口不能混用。相较于V1,V2的功能更多,但昇腾310和910仅支持V1,下面说明V1的功能。

VPC图像处理典型功能

VPC的功能包括抠图、缩放、叠加、拼接、图像金字塔、直方图统计、色彩重映射、边界填充、格式转换和图像灰度化。

接口调用流程如下(以抠图缩放为例)

  1. 调用acldvppCreateChannel接口创建图片数据处理的通道。
    创建图片数据处理的通道前,需先调用acldvppCreateChannelDesc接口创建通道描述信息。
  2. 调用acldvppCreateRoiConfig接口、acldvppCreateResizeConfig接口分别创建抠图区域位置的配置、缩放配置。
  3. 实现抠图、缩放功能前,若需要申请Device上的内存存放输入或输出数据,需调用acldvppMalloc申请内存。
  4. 执行抠图、缩放。
    • 关于抠图:
      • 调用acldvppVpcCropAsync异步接口,按指定区域从输入图片中抠图,再将抠的图片存放到输出内存中,作为输出图片。
        输出图片区域与抠图区域cropArea不一致时会对图片再做一次缩放操作。
      • 当前系统还提供了acldvppVpcCropAndPasteAsync异步接口,支持按指定区域从输入图片中抠图,再将抠的图片贴到目标图片的指定位置,作为输出图片。
        • 抠图区域cropArea的宽高与贴图区域pasteArea宽高不一致时会对图片再做一次缩放操作。
        • 如果用户需要将目标图片读入内存用于存放输出图片,将贴图区域叠加在目标图片上,则需要编写代码逻辑:在申请输出内存后,将目标图片读入输出内存。
    • 关于缩放
      • 调用acldvppVpcResizeAsync异步接口,将输入图片缩放到输出图片大小。
      • 缩放后输出图片内存根据YUV420SP格式计算,计算公式:对齐后的宽对齐后的高3/2
    • 对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
  5. 调用acldvppFree接口释放输入、输出内存。
  6. 调用acldvppDestroyRoiConfig接口、acldvppDestroyResizeConfig接口分别销毁抠图区域位置的配置、缩放配置。
  7. 调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    销毁图片数据处理的通道后,再调用acldvppDestroyChannelDesc接口销毁通道描述信息。

抠图缩放(一图一框)

  1. 原理

    • 调用acldvppCreateChannel接口创建图片数据处理的通道、调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    • 调用acldvppVpcCropResizeAsync异步接口,按指定区域从输入图片中抠图,再将抠的图片存放到输出内存中,作为输出图片。对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
      调用acldvppVpcCropResizeAsync接口实现抠图缩放时,支持指定缩放算法。
    • 输出图片区域与抠图区域cropArea不一致时会对图片再做一次缩放操作。
  2. 代码

    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    // 1.AscendCL初始化
    aclRet = aclInit(nullptr);

    // 2.运行管理资源申请(依次申请Device、Context、Stream)
    aclrtContext context_;
    aclrtStream stream_;
    aclrtSetDevice(0);
    aclrtCreateContext(&context_, 0);
    aclrtCreateStream(&stream_);

    // 3. 创建缩放配置数据,并指定抠图区域的位置
    // resizeConfig_是acldvppResizeConfig类型
    resizeConfig_ = acldvppCreateResizeConfig();
    aclError aclRet = acldvppSetResizeConfigInterpolation(resizeConfig_, 0);
    // cropArea_是acldvppRoiConfig类型
    cropArea_ = acldvppCreateRoiConfig(550, 749, 480, 679);

    // 4. 创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
    dvppChannelDesc_ = acldvppCreateChannelDesc();

    // 5. 创建图片数据处理的通道。
    ret = acldvppCreateChannel(dvppChannelDesc_);

    // 6. 申请输入内存(区分运行状态)
    // 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    // inputPicWidth、inputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t cropInBufferSize = inputPicWidth *inputPicHeight* 3 / 2;
    if(runMode == ACL_HOST) {
    // 申请Host内存cropInHostBuffer
    void* cropInHostBuffer = nullptr;
    cropInHostBuffer = malloc(cropInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, cropInHostBuffer, cropInBufferSize);
    // 申请Device内存cropInDevBuffer_
    aclRet = acldvppMalloc(&cropInDevBuffer_, cropInBufferSize);
    // 通过aclrtMemcpy接口图片数据传输到Device
    aclRet = aclrtMemcpy(cropInDevBuffer_, cropInBufferSize, cropInHostBuffer, cropInBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    // 数据传输完成后,及时释放内存
    free(cropInHostBuffer);
    } else {
    // 申请Device输入内存cropInDevBuffer_
    ret = acldvppMalloc(&cropInDevBuffer_, cropInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, cropInDevBuffer_, cropInBufferSize);
    }

    // 7. 申请Device输出内存cropOutBufferDev_,内存大小cropOutBufferSize_根据计算公式得出
    // outputPicWidth、outputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t cropOutBufferSize_= outputPicWidth * outputPicHeight * 3 / 2;
    ret = acldvppMalloc(&cropOutBufferDev_, cropOutBufferSize_)

    // 8. 创建输入图片的描述信息,并设置各属性值,cropInputDesc_是acldvppPicDesc类型
    cropInputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(cropInputDesc_, cropInDevBuffer_);
    acldvppSetPicDescFormat(cropInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(cropInputDesc_, inputWidth_);
    acldvppSetPicDescHeight(cropInputDesc_, inputHeight_);
    acldvppSetPicDescWidthStride(cropInputDesc_, inputWidthStride);
    acldvppSetPicDescHeightStride(cropInputDesc_, inputHeightStride);
    acldvppSetPicDescSize(cropInputDesc_, cropInBufferSize);

    // 9. 创建输出图片的描述信息,并设置各属性值,cropOutputDesc_是acldvppPicDesc类型
    // 如果抠图的输出图片作为模型推理的输入,则输出图片的宽高要与模型要求的宽高保持一致
    cropOutputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(cropOutputDesc_, cropOutBufferDev_);
    acldvppSetPicDescFormat(cropOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420); acldvppSetPicDescWidth(cropOutputDesc_, OutputWidth_);
    acldvppSetPicDescHeight(cropOutputDesc_, OutputHeight_);
    acldvppSetPicDescWidthStride(cropOutputDesc_, OutputWidthStride);
    acldvppSetPicDescHeightStride(cropOutputDesc_, OutputHeightStride);
    acldvppSetPicDescSize(cropOutputDesc_, cropOutBufferSize_);

    // 10. 执行异步抠图缩放,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
    ret = acldvppVpcCropResizeAsync(dvppChannelDesc_, cropInputDesc_,
    cropOutputDesc_, cropArea_, resizeConfig_, stream_);
    ret = aclrtSynchronizeStream(stream_);

    // 11. 抠图贴图结束后,释放资源,包括输入/输出图片的描述信息、输入/输出内存、通道描述信息、通道等
    acldvppDestroyRoiConfig(cropArea_);
    acldvppDestroyResizeConfig(resizeConfig_);
    acldvppDestroyPicDesc(cropInputDesc_);
    acldvppDestroyPicDesc(cropOutputDesc_);
    if(runMode == ACL_HOST) {
    // 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
    // 申请Host内存cropOutHostBuffer
    void* cropOutHostBuffer = nullptr;
    cropOutHostBuffer = malloc(cropOutBufferSize_);
    // 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
    aclRet = aclrtMemcpy(cropOutHostBuffer, cropOutBufferSize_, cropOutBufferDev_, cropOutBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
    // 释放掉输入输出的device内存
    (void)acldvppFree(cropInDevBuffer_);
    (void)acldvppFree(cropOutBufferDev_);
    // 数据使用完成后,释放内存
    free(cropOutHostBuffer);
    } else {
    // 此时运行在device侧,处理结果也在Device侧,可以根据需要操作抠图结果后,释放Device侧内存
    (void)acldvppFree(cropInDevBuffer_);
    (void)acldvppFree(cropOutBufferDev_);
    }
    acldvppDestroyChannel(dvppChannelDesc_);
    (void)acldvppDestroyChannelDesc(dvppChannelDesc_);
    dvppChannelDesc_ = nullptr;

    // 12. 释放运行管理资源(依次释放Stream、Context、Device)
    aclrtDestroyStream(stream_);
    aclrtDestroyContext(context_);
    aclrtResetDevice(0);

    // 13.AscendCL去初始化
    aclRet = aclFinalize();

    // ....

抠图贴图缩放(一图一框)

  1. 原理

    • 调用acldvppCreateChannel接口创建图片数据处理的通道、调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    • 调用acldvppVpcCropResizePasteAsync异步接口,按指定区域从输入图片中抠图,再将抠的图片贴到目标图片的指定位置,作为输出图片。对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
      调用acldvppVpcCropResizePasteAsync实现抠图缩放贴图时,支持指定缩放算法。
    • 抠图区域cropArea的宽高与贴图区域pasteArea宽高不一致时会对图片再做一次缩放操作。
    • 如果需要将目标图片读入内存用于存放输出图片,将贴图区域叠加在目标图片上,则需要编写代码逻辑:在申请输出内存后,将目标图片读入输出内存。
  2. 代码

    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    // 1.AscendCL初始化

    aclRet = aclInit(nullptr);

    // 2.运行管理资源申请(依次申请Device、Context、Stream)
    aclrtContext context_;
    aclrtStream stream_;
    aclrtSetDevice(0);
    aclrtCreateContext(&context_, 0);
    aclrtCreateStream(&stream_);

    // 3. 创建缩放配置数据,并指定抠图区域的位置、指定贴图区域的位置
    // resizeConfig_是acldvppResizeConfig类型
    resizeConfig_ = acldvppCreateResizeConfig();
    aclError aclRet = acldvppSetResizeConfigInterpolation(resizeConfig_, 0);
    // cropArea_和pasteArea_是acldvppRoiConfig类型
    cropArea_ = acldvppCreateRoiConfig(512, 711, 512, 711);
    pasteArea_ = acldvppCreateRoiConfig(16, 215, 16, 215);

    // 4. 创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
    dvppChannelDesc_ = acldvppCreateChannelDesc();

    // 5. 创建图片数据处理的通道。
    ret = acldvppCreateChannel(dvppChannelDesc_);

    // 6. 申请输入内存(区分运行状态)
    // 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    // inputPicWidth、inputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t vpcInBufferSize = inputPicWidth *inputPicHeight* 3 / 2;
    if(runMode == ACL_HOST) {
    // 申请Host内存vpcInHostBuffer
    void* vpcInHostBuffer = nullptr;
    vpcInHostBuffer = malloc(vpcInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, vpcInHostBuffer, vpcInBufferSize);
    // 申请Device内存vpcInDevBuffer_
    aclRet = acldvppMalloc(&vpcInDevBuffer_, vpcInBufferSize);
    // 通过aclrtMemcpy接口将输入图片数据传输到Device
    aclRet = aclrtMemcpy(vpcInDevBuffer_, vpcInBufferSize, vpcInHostBuffer, vpcInBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    // 数据传输完成后,及时释放内存
    free(vpcInHostBuffer);
    } else {
    // 申请Device输入内存vpcInDevBuffer_
    ret = acldvppMalloc(&vpcInDevBuffer_, vpcInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, vpcInDevBuffer_, vpcInBufferSize);
    }

    // 7. 申请输出内存vpcOutBufferDev_,内存大小vpcOutBufferSize_根据计算公式得出
    // outputPicWidth、outputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t vpcOutBufferSize_= outputPicWidth * outputPicHeight * 3 / 2;
    ret = acldvppMalloc(&vpcOutBufferDev_, vpcOutBufferSize_)

    // 8. 创建输入图片的描述信息,并设置各属性值
    // 此处示例将解码的输出内存作为抠图贴图的输入,vpcInputDesc_是acldvppPicDesc类型
    vpcInputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(vpcInputDesc_, decodeOutBufferDev_);
    acldvppSetPicDescFormat(vpcInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(vpcInputDesc_, inputWidth_);
    acldvppSetPicDescHeight(vpcInputDesc_, inputHeight_);
    acldvppSetPicDescWidthStride(vpcInputDesc_, jpegOutWidthStride);
    acldvppSetPicDescHeightStride(vpcInputDesc_, jpegOutHeightStride);
    acldvppSetPicDescSize(vpcInputDesc_, jpegOutBufferSize);

    // 9. 创建输出图片的描述信息,并设置各属性值
    // 如果抠图贴图的输出图片作为模型推理的输入,则输出图片的宽高要与模型要求的宽高保持一致
    // vpcOutputDesc_是acldvppPicDesc类型
    vpcOutputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(vpcOutputDesc_, vpcOutBufferDev_);
    acldvppSetPicDescFormat(vpcOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(vpcOutputDesc_, dvppOutWidth);
    acldvppSetPicDescHeight(vpcOutputDesc_, dvppOutHeight);
    acldvppSetPicDescWidthStride(vpcOutputDesc_, dvppOutWidthStride);
    acldvppSetPicDescHeightStride(vpcOutputDesc_, dvppOutHeightStride);
    acldvppSetPicDescSize(vpcOutputDesc_, vpcOutBufferSize_);

    // 10. 执行异步抠图贴图缩放,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
    ret = acldvppVpcCropResizePasteAsync(dvppChannelDesc_, vpcInputDesc_,
    vpcOutputDesc_, cropArea_, pasteArea_, resizeConfig_, stream_);
    ret = aclrtSynchronizeStream(stream_);

    // 11. 抠图贴图结束后,释放资源,包括输入/输出图片的描述信息、输入/输出内存、通道描述信息、通道等
    acldvppDestroyRoiConfig(cropArea_);
    acldvppDestroyRoiConfig(pasteArea_);
    acldvppDestroyResizeConfig(resizeConfig_);
    acldvppDestroyPicDesc(vpcInputDesc_);
    acldvppDestroyPicDesc(vpcOutputDesc_);

    if(runMode == ACL_HOST) {
    // 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
    // 申请Host内存vpcOutHostBuffer
    void* vpcOutHostBuffer = nullptr;
    vpcOutHostBuffer = malloc(vpcOutBufferSize_);
    // 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
    aclRet = aclrtMemcpy(vpcOutHostBuffer, vpcOutBufferSize_, vpcOutBufferDev_, vpcOutBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
    // 释放掉输入输出的device内存
    (void)acldvppFree(vpcInDevBuffer_);
    (void)acldvppFree(vpcOutBufferDev_);
    // 数据使用完成后,释放内存
    free(vpcOutHostBuffer);
    } else {
    // 此时运行在device侧,处理结果也在Device侧,可以根据需要操作处理结果后,释放Device侧内存
    (void)acldvppFree(vpcInDevBuffer_);
    (void)acldvppFree(vpcOutBufferDev_);
    }

    acldvppDestroyChannel(dvppChannelDesc_);
    (void)acldvppDestroyChannelDesc(dvppChannelDesc_);
    dvppChannelDesc_ = nullptr;

    // 12. 释放运行管理资源(依次释放Stream、Context、Device)
    aclrtDestroyStream(stream_);
    aclrtDestroyContext(context_);
    aclrtResetDevice(0);

    // 13.AscendCL去初始化
    aclRet = aclFinalize();

    // ....

抠图贴图(一图多框)

  1. 原理

    • 调用acldvppCreateChannel接口创建图片数据处理的通道、调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    • 调用acldvppCreateBatchPicDesc接口创建批量图片的描述信息,一图多框仅支持batchSize为1。
    • 调用acldvppGetPicDesc获取指定图片的描述信息,填充对应的图片数据。
    • 调用acldvppCreateRoiConfig创建框位置区域的描述信息,并调用acldvppSetRoiConfig系列接口设置框的位置。
    • 调用acldvppVpcBatchCropAndPasteAsync异步接口,按指定区域从输入图片中抠图,再将抠的图片贴到目标图片的指定位置,作为输出图片。对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
    • 抠图区域cropArea的宽高与贴图区域pasteArea宽高不一致时会对图片再做一次缩放操作。
    • 如果需要将目标图片读入内存用于存放输出图片,将贴图区域叠加在目标图片上,则需要编写代码逻辑:在申请输出内存后,将目标图片读入输出内存。
  2. 代码

    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    // 1.AscendCL初始化

    aclRet = aclInit(nullptr);

    // 2.运行管理资源申请(依次申请Device、Context、Stream)
    aclrtContext context_;
    aclrtStream stream_;
    aclrtSetDevice(0);
    aclrtCreateContext(&context_, 0);
    aclrtCreateStream(&stream_);

    // 3.指定批量抠图区域的位置、指定批量贴图区域的位置,cropAreas_和pasteAreas_是acldvppRoiConfig类型
    acldvppRoiConfig *cropAreas_[2], pasteAreas_[2];
    cropAreas_[0] = acldvppCreateRoiConfig(512, 711, 512, 711);
    cropAreas_[1] = acldvppCreateRoiConfig(512, 711, 512, 711);
    pasteAreas_[0] = acldvppCreateRoiConfig(16, 215, 16, 215);
    pasteAreas_[1] = acldvppCreateRoiConfig(16, 215, 16, 215);

    // 4. 创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
    dvppChannelDesc_ = acldvppCreateChannelDesc();

    // 5. 创建图片数据处理的通道。
    aclError ret = acldvppCreateChannel(dvppChannelDesc_);

    // 6. 申请输入内存(区分运行状态)
    // 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    // inputPicWidth、inputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t vpcInBufferSize = inputPicWidth *inputPicHeight* 3 / 2;
    if(runMode == ACL_HOST) {
    // 申请Host内存vpcInHostBuffer
    void* vpcInHostBuffer = nullptr;
    vpcInHostBuffer = malloc(vpcInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, vpcInHostBuffer, vpcInBufferSize);
    // 申请Device内存vpcInDevBuffer_
    aclRet = acldvppMalloc(&vpcInDevBuffer_, vpcInBufferSize);
    // 通过aclrtMemcpy接口将Host的图片数据传输到Device
    aclRet = aclrtMemcpy(vpcInDevBuffer_, vpcInBufferSize, vpcInHostBuffer, vpcInBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    // 数据传输完成后,及时释放内存
    free(vpcInHostBuffer);
    } else {
    // 申请Device输入内存vpcInDevBuffer_
    ret = acldvppMalloc(&vpcInDevBuffer_, vpcInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, vpcInDevBuffer_, vpcInBufferSize);
    }

    // 7. 申请输出内存vpcOutBufferDev_,内存大小vpcOutBufferSize_根据计算公式得出
    // outputPicWidth、outputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t vpcOutBufferSize_= outputPicWidth * outputPicHeight * 3 / 2;
    ret = acldvppMalloc(&vpcOutBufferDev_, vpcOutBufferSize_)

    // 8. 创建输入图片的描述信息,并设置各属性值
    // 此处示例将解码的输出内存作为抠图贴图的输入,vpcInputDesc_是acldvppPicDesc类型
    vpcInputBatchDesc_ = acldvppCreateBatchPicDesc(1);
    vpcInputDesc_= acldvppGetPicDesc(vpcInputBatchDesc_, 0);
    acldvppSetPicDescData(vpcInputDesc_, decodeOutBufferDev_);
    acldvppSetPicDescFormat(vpcInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(vpcInputDesc_, inputWidth_);
    acldvppSetPicDescHeight(vpcInputDesc_, inputHeight_);
    acldvppSetPicDescWidthStride(vpcInputDesc_, jpegOutWidthStride);
    acldvppSetPicDescHeightStride(vpcInputDesc_, jpegOutHeightStride);
    acldvppSetPicDescSize(vpcInputDesc_, jpegOutBufferSize);

    // 9. 创建批量输出图片的描述信息,并设置各属性值
    // 如果抠图贴图的输出图片作为模型推理的输入,则输出图片的宽高要与模型要求的宽高保持一致
    // vpcOutputDesc_是acldvppPicDesc类型
    vpcOutputBatchDesc_ = acldvppCreateBatchPicDesc(2);
    for (index=0; index<2; ++index){
    vecOutPtr_.push_back(vpcOutBufferDev_);
    vpcOutputDesc_= acldvppGetPicDesc(vpcInputBatchDesc_, index);
    acldvppSetPicDescData(vpcOutputDesc_, vpcOutBufferDev_);
    acldvppSetPicDescFormat(vpcOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(vpcOutputDesc_, dvppOutWidth);
    acldvppSetPicDescHeight(vpcOutputDesc_, dvppOutHeight);
    acldvppSetPicDescWidthStride(vpcOutputDesc_, dvppOutWidthStride);
    acldvppSetPicDescHeightStride(vpcOutputDesc_, dvppOutHeightStride);
    acldvppSetPicDescSize(vpcOutputDesc_, vpcOutBufferSize_);
    }

    // 10. 创建roiNums,每张图对应需要抠图和贴图的数量

    uint32_ttotalNum = 0;
    std::unique_ptr<uint32_t[]> roiNums(new (std::nothrow) uint32_t[1]);
    roiNums[0]=2;
    // 11. 执行异步抠图贴图,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
    ret = acldvppVpcBatchCropAndPasteAsync(dvppChannelDesc_, vpcInputBatchDesc_, roiNums.get(), 1,
    vpcOutputBatchDesc_, cropAreas_, pasteAreas_, stream_);
    ret = aclrtSynchronizeStream(stream_);

    // 12. 抠图贴图结束后,释放资源,包括输入/输出图片的描述信息、输入/输出内存、通道描述信息、通道等
    acldvppDestroyRoiConfig(cropAreas_[0]);
    acldvppDestroyRoiConfig(cropAreas_[1]);
    acldvppDestroyRoiConfig(pasteAreas_[0]);
    acldvppDestroyRoiConfig(pasteAreas_[1]);
    (void)acldvppFree(vpcInDevBuffer_);
    for(index=0; index<2; ++index){
    if(runMode == ACL_HOST) {
    // 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
    // 申请Host内存vpcOutHostBuffer
    void* vpcOutHostBuffer = nullptr;
    vpcOutHostBuffer = malloc(vpcOutBufferSize_);
    // 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
    aclRet = aclrtMemcpy(vpcOutHostBuffer, vpcOutBufferSize_, vpcOutBufferDev_, vpcOutBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
    // 释放掉输入输出的device内存
    (void)acldvppFree(vpcOutBufferDev_);
    // 数据使用完成后,释放内存
    free(vpcOutHostBuffer);
    } else {
    // 此时运行在device侧,处理结果也在Device侧,可以根据需要操作处理结果后,释放Device侧内存

    (void)acldvppFree(vpcOutBufferDev_);
    }
    }
    acldvppDestroyBatchPicDesc(vpcInputDesc_);
    acldvppDestroyBatchPicDesc(vpcOutputDesc_);
    acldvppDestroyChannel(dvppChannelDesc_);
    (void)acldvppDestroyChannelDesc(dvppChannelDesc_);
    dvppChannelDesc_ = nullptr;

    // 13. 释放运行管理资源(依次释放Stream、Context、Device)
    aclrtDestroyStream(stream_);
    aclrtDestroyContext(context_);
    aclrtResetDevice(0);

    // 14.AscendCL去初始化
    aclRet = aclFinalize();

    // ....

图片缩放

  1. 原理

    • 调用acldvppCreateChannel接口创建图片数据处理的通道、调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    • 调用acldvppVpcResizeAsync异步接口,将输入图片缩放到输出图片大小。对于异步接口,还需调用aclrtSynchronizeStream接口阻塞应用程序运行,直到指定Stream中的所有任务都完成。
    • 调用acldvppCreateResizeConfig接口创建图片缩放配置数据。
  2. 代码

    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    // 1.AscendCL初始化
    aclRet = aclInit(nullptr);

    // 2.运行管理资源申请(依次申请Device、Context、Stream)
    aclrtContext context_;
    aclrtStream stream_;
    aclrtSetDevice(0);
    aclrtCreateContext(&context_, 0);
    aclrtCreateStream(&stream_);

    // 3. 创建图片缩放配置数据、指定缩放算法
    // resizeConfig_是acldvppResizeConfig类型
    acldvppResizeConfig *resizeConfig_ = acldvppCreateResizeConfig();
    aclError ret = acldvppSetResizeConfigInterpolation(resizeConfig_, 0);

    // 4. 创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
    dvppChannelDesc_ = acldvppCreateChannelDesc();

    // 5. 创建图片数据处理的通道。
    ret = acldvppCreateChannel(dvppChannelDesc_);

    // 6. 申请输入内存(区分运行状态)
    // 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    // inputPicWidth、inputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t resizeInBufferSize = inputPicWidth * inputPicHeight * 3 / 2;
    if(runMode == ACL_HOST) {
    // 申请Host内存vpcInHostBuffer
    void* vpcInHostBuffer = nullptr;
    vpcInHostBuffer = malloc(resizeInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, vpcInHostBuffer, resizeInBufferSize);
    // 申请Device内存resizeInDevBuffer_
    aclRet = acldvppMalloc(&resizeInDevBuffer_, resizeInBufferSize);
    // 通过aclrtMemcpy接口将输入图片数据传输到Device
    aclRet = aclrtMemcpy(resizeInDevBuffer_, resizeInBufferSize, vpcInHostBuffer, resizeInBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    // 数据传输完成后,及时释放内存
    free(vpcInHostBuffer);
    } else {
    // 申请Device输入内存resizeInDevBuffer_
    ret = acldvppMalloc(&resizeInDevBuffer_, resizeInBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, resizeInDevBuffer_, resizeInBufferSize);
    }

    // 7. 申请缩放输出内存resizeOutBufferDev_,内存大小resizeOutBufferSize_根据计算公式得出
    // outputPicWidth、outputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t resizeOutBufferSize_ = outputPicWidth * outputPicHeight * 3 / 2;
    ret = acldvppMalloc(&resizeOutBufferDev_, resizeOutBufferSize_)

    // 8. 创建缩放输入图片的描述信息,并设置各属性值
    // resizeInputDesc_是acldvppPicDesc类型
    resizeInputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(resizeInputDesc_, resizeInDevBuffer_);
    acldvppSetPicDescFormat(resizeInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(resizeInputDesc_, inputWidth_);
    acldvppSetPicDescHeight(resizeInputDesc_, inputHeight_);
    acldvppSetPicDescWidthStride(resizeInputDesc_, inputWidthStride);
    acldvppSetPicDescHeightStride(resizeInputDesc_, inputHeightStride);
    acldvppSetPicDescSize(resizeInputDesc_, resizeInBufferSize);

    // 9. 创建缩放输出图片的描述信息,并设置各属性值
    // 如果缩放的输出图片作为模型推理的输入,则输出图片的宽高要与模型要求的宽高保持一致
    // resizeOutputDesc_是acldvppPicDesc类型
    resizeOutputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(resizeOutputDesc_, resizeOutBufferDev_);
    acldvppSetPicDescFormat(resizeOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(resizeOutputDesc_, resizeOutputWidth_);
    acldvppSetPicDescHeight(resizeOutputDesc_, resizeOutputHeight_);
    acldvppSetPicDescWidthStride(resizeOutputDesc_, resizeOutputWidthStride);
    acldvppSetPicDescHeightStride(resizeOutputDesc_, resizeOutputHeightStride);
    acldvppSetPicDescSize(resizeOutputDesc_, resizeOutBufferSize_);

    // 10. 执行异步缩放,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
    ret = acldvppVpcResizeAsync(dvppChannelDesc_, resizeInputDesc_,
    resizeOutputDesc_, resizeConfig_, stream_);
    ret = aclrtSynchronizeStream(stream_);

    // 11. 缩放结束后,释放资源,包括缩放输入/输出图片的描述信息、缩放输入/输出内存
    acldvppDestroyPicDesc(resizeInputDesc_);
    acldvppDestroyPicDesc(resizeOutputDesc_);

    if(runMode == ACL_HOST) {
    // 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
    // 申请Host内存vpcOutHostBuffer
    void* vpcOutHostBuffer = nullptr;
    vpcOutHostBuffer = malloc(resizeOutBufferSize_);
    // 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
    aclRet = aclrtMemcpy(vpcOutHostBuffer, resizeOutBufferSize_, resizeOutBufferDev_, resizeOutBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
    // 释放掉输入输出的device内存
    (void)acldvppFree(resizeInDevBuffer_);
    (void)acldvppFree(resizeOutBufferDev_);
    // 数据使用完成后,释放内存
    free(vpcOutHostBuffer);
    } else {
    // 此时运行在device侧,处理结果也在Device侧,可以根据需要操作处理结果后,释放Device侧内存
    (void)acldvppFree(resizeInDevBuffer_);
    (void)acldvppFree(resizeOutBufferDev_);
    }

    // 12. 释放运行管理资源(依次释放Stream、Context、Device)
    aclrtDestroyStream(stream_);
    aclrtDestroyContext(context_);
    aclrtResetDevice(0);

    // 13.AscendCL去初始化
    aclRet = aclFinalize();

    // ....

格式转换

  1. 原理

    • 调用acldvppCreateChannel接口创建图片数据处理的通道、调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    • 格式转换支持以下两种实现方式:
      • 在实现抠图、缩放等功能时,调用对应的接口(例如acldvppVpcCropAsync接口)时,通过将输入图片和输出图片的格式设置成不同的,达到转换图片格式的目的。
      • 如果仅仅做图片格式转换,也可以直接调用acldvppVpcConvertColorAsync接口。
    • 对于异步接口,还需调用aclrtSynchronizeStream接口阻塞应用程序运行,直到指定Stream中的所有任务都完成。
  2. 代码

    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
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    // 1.AscendCL初始化
    aclRet = aclInit(nullptr);

    // 2.运行管理资源申请(依次申请Device、Context、Stream)
    aclrtContext context_;
    aclrtStream stream_;
    aclrtSetDevice(0);
    aclrtCreateContext(&context_, 0);
    aclrtCreateStream(&stream_);

    // 3. 创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
    dvppChannelDesc_ = acldvppCreateChannelDesc();

    // 4. 创建图片数据处理的通道。
    aclError ret = acldvppCreateChannel(dvppChannelDesc_);

    // 5. 申请输入内存(区分运行状态)
    // 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
    aclrtRunMode runMode;
    ret = aclrtGetRunMode(&runMode);
    // inputPicWidth、inputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t inBufferSize = inputPicWidth * inputPicHeight * 3 / 2;
    if(runMode == ACL_HOST){
    // 申请Host内存vpcInHostBuffer
    void* vpcInHostBuffer = nullptr;
    vpcInHostBuffer = malloc(inBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, vpcInHostBuffer, inBufferSize);
    // 申请Device内存inDevBuffer_
    aclRet = acldvppMalloc(&inDevBuffer_, inBufferSize);
    // 通过aclrtMemcpy接口将输入图片数据传输到Device
    aclRet = aclrtMemcpy(inDevBuffer_, inBufferSize, vpcInHostBuffer, inBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
    // 数据传输完成后,及时释放内存
    free(vpcInHostBuffer);
    } else {
    // 申请Device输入内存inDevBuffer_
    ret = acldvppMalloc(&inDevBuffer_, inBufferSize);
    // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
    ReadPicFile(picName, inDevBuffer_, inBufferSize);
    }

    // 6. 申请色域转换输出内存outBufferDev_,内存大小outBufferSize_根据计算公式得出
    // outputPicWidth、outputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
    uint32_t outBufferSize_ = outputPicWidth * outputPicHeight * 3 / 2
    ret = acldvppMalloc(&outBufferDev_, outBufferSize_)

    // 7. 创建色域转换输入图片的描述信息,并设置各属性值
    // inputDesc_是acldvppPicDesc类型
    inputDesc_ = acldvppCreatePicDesc();
    acldvppSetPicDescData(inputDesc_, inDevBuffer_);
    acldvppSetPicDescFormat(inputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
    acldvppSetPicDescWidth(inputDesc_, inputWidth_);
    acldvppSetPicDescHeight(inputDesc_, inputHeight_);
    acldvppSetPicDescWidthStride(inputDesc_, inputWidthStride);
    acldvppSetPicDescHeightStride(inputDesc_, inputHeightStride);
    acldvppSetPicDescSize(inputDesc_, inBufferSize);

    // 8. 创建色域转换的输出图片的描述信息,并设置各属性值, 输出的宽和高要求和输入一致
    // 如果色域转换的输出图片作为模型推理的输入,则输出图片的宽高要与模型要求的宽高保持一致
    // outputDesc_是acldvppPicDesc类型
    outputDesc_= acldvppCreatePicDesc();
    acldvppSetPicDescData(outputDesc_, outBufferDev_);
    acldvppSetPicDescFormat(outputDesc_, PIXEL_FORMAT_YUV_400);
    acldvppSetPicDescWidth(outputDesc_, outputWidth_);
    acldvppSetPicDescHeight(outputDesc_, outputHeight_);
    acldvppSetPicDescWidthStride(outputDesc_, outputWidthStride);
    acldvppSetPicDescHeightStride(outputDesc_, outputHeightStride);
    acldvppSetPicDescSize(outputDesc_, outBufferSize_);

    // 9. 执行异步色域转换,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
    ret = acldvppVpcConvertColorAsync(dvppChannelDesc_, inputDesc_, outputDesc_, stream_);
    ret = aclrtSynchronizeStream(stream_);

    // 10. 色域转换结束后,释放资源,包括输入/输出图片的描述信息、输入/输出内存
    acldvppDestroyPicDesc(inputDesc_);
    acldvppDestroyPicDesc(outputDesc_);

    if(runMode == ACL_HOST) {
    // 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
    // 申请Host内存vpcOutHostBuffer
    void* vpcOutHostBuffer = nullptr;
    vpcOutHostBuffer = malloc(outBufferSize_);
    // 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
    aclRet = aclrtMemcpy(vpcOutHostBuffer, outBufferSize_, outBufferDev_, outBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
    // 释放掉输入输出的device内存
    (void)acldvppFree(inDevBuffer_);
    (void)acldvppFree(outBufferDev_);
    // 数据使用完成后,释放内存
    free(vpcOutHostBuffer);
    } else {
    // 此时运行在device侧,处理结果也在Device侧,可以根据需要操作处理结果后,释放Device侧内存
    (void)acldvppFree(inDevBuffer_);
    (void)acldvppFree(outBufferDev_);
    }

    // 11. 释放运行管理资源(依次释放Stream、Context、Device)
    aclrtDestroyStream(stream_);
    aclrtDestroyContext(context_);
    aclrtResetDevice(0);

    // 12.AscendCL去初始化
    aclRet = aclFinalize();

    // ....

JPEG解码

接口调用流程

  1. 调用acldvppCreateChannel接口创建图片数据处理的通道。
    创建图片数据处理的通道前,需先调用acldvppCreateChannelDesc接口创建通道描述信息。
  2. 实现JPEG图片解码功能前,若需要申请Device上的内存存放输入或输出数据,需调用acldvppMalloc申请内存。
    在申请输出内存前,可根据存放JPEG图片数据的内存,调用acldvppJpegPredictDecSize接口预估JPEG图片解码后所需的输出内存的大小。
  3. 调用acldvppJpegDecodeAsync异步接口进行解码。
    对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
  4. 在解码结束后,需及时调用acldvppFree接口释放输入、输出内存。
  5. 调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    销毁图片数据处理的通道后,再调用acldvppDestroyChannelDesc接口销毁通道描述信息。

代码

示例代码

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
91
92
93
// 1.AscendCL初始化
aclRet = aclInit(nullptr);

// 2.运行管理资源申请(依次申请Device、Context、Stream)
aclrtContext context_;
aclrtStream stream_;
aclrtSetDevice(0);
aclrtCreateContext(&context_, 0);
aclrtCreateStream(&stream_);

// 3.创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
dvppChannelDesc_ = acldvppCreateChannelDesc();

// 4.创建图片数据处理的通道。
aclError ret = acldvppCreateChannel(dvppChannelDesc_);

// 5. 申请输入内存(区分运行状态)
// 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
if(runMode == ACL_HOST){
// 申请Host内存inputHostBuff,并将输入图片读入该地址,inDevBufferSize为读入图片大小
void* inputHostBuff = nullptr;
inputHostBuff = malloc(inDevBufferSize);
// 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
ReadPicFile(picName, inputHostBuff, inDevBufferSize);
// 申请Device内存inDevBuffer_
aclRet = acldvppMalloc(&inDevBuffer_, inDevBufferSize);
// 通过aclrtMemcpy接口将输入图片数据传输到Device
aclRet = aclrtMemcpy(inDevBuffer_, inDevBufferSize, inputHostBuff, inDevBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);

} else {
// 申请Device输入内存inDevBuffer_
ret = acldvppMalloc(&inDevBuffer_, inBufferSize);
// 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
ReadPicFile(picName, inDevBuffer_, inBufferSize);
}

// 6. 申请解码输出内存decodeOutDevBuffer_
// 预估JPEGD处理结果所需的内存大小
uint32_t decodeOutBufferSize = 0;
ret = acldvppJpegPredictDecSize(inputHostBuff, inDevBufferSize, PIXEL_FORMAT_YVU_SEMIPLANAR_420,&decodeOutBufferSize)
ret = acldvppMalloc(&decodeOutDevBuffer_, decodeOutBufferSize)
// 及时释放内存
free(inputHostBuff);

// 7. 创建解码输出图片的描述信息,设置各属性值
// decodeOutputDesc是acldvppPicDesc类型
decodeOutputDesc_ = acldvppCreatePicDesc();
acldvppSetPicDescData(decodeOutputDesc_, decodeOutDevBuffer_);
acldvppSetPicDescFormat(decodeOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420);
acldvppSetPicDescWidth(decodeOutputDesc_, inputWidth_);
acldvppSetPicDescHeight(decodeOutputDesc_, inputHeight_);
acldvppSetPicDescWidthStride(decodeOutputDesc_, decodeOutWidthStride);
acldvppSetPicDescHeightStride(decodeOutputDesc_, decodeOutHeightStride);
acldvppSetPicDescSize(decodeOutputDesc_, decodeOutBufferSize);

// 8. 执行异步解码,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
ret = acldvppJpegDecodeAsync(dvppChannelDesc_, inDevBuffer_, inDevBufferSize, decodeOutputDesc_, stream_);
ret = aclrtSynchronizeStream(stream_);

// 9. 解码结束后,释放资源,包括解码输出图片的描述信息、解码输出内存、通道描述信息、通道等
acldvppDestroyPicDesc(decodeOutputDesc_);

if(runMode == ACL_HOST) {
// 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
// 申请Host内存vpcOutHostBuffer
void* vpcOutHostBuffer = nullptr;
vpcOutHostBuffer = malloc(decodeOutBufferSize);
// 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
aclRet = aclrtMemcpy(vpcOutHostBuffer, decodeOutBufferSize, decodeOutDevBuffer_, decodeOutBufferSize, ACL_MEMCPY_DEVICE_TO_HOST);
// 释放掉输入输出的device内存
(void)acldvppFree(inDevBuffer_);
(void)acldvppFree(decodeOutDevBuffer_);
// 数据使用完成后,释放内存
free(vpcOutHostBuffer);
} else {
// 此时运行在device侧,处理结果也在Device侧,可以根据需要操作处理结果后,释放Device侧内存
(void)acldvppFree(inDevBuffer_);
(void)acldvppFree(decodeOutDevBuffer_);
}
acldvppDestroyChannel(dvppChannelDesc_);
(void)acldvppDestroyChannelDesc(dvppChannelDesc_);
dvppChannelDesc_ = nullptr;

// 10. 释放运行管理资源(依次释放Stream、Context、Device)
aclrtDestroyStream(stream_);
aclrtDestroyContext(context_);
aclrtResetDevice(0);

// 11.AscendCL去初始化
aclRet = aclFinalize();
// ....

JPEG编码

接口调用流程

  1. 调用acldvppCreateChannel接口创建图片数据处理的通道。
    创建图片数据处理的通道前,需先调用acldvppCreateChannelDesc接口创建通道描述信息。
  2. 调用acldvppCreateJpegeConfig接口创建图片编码配置数据。
  3. 实现JPEG图片编码功能前,若需要申请Device上的内存存放输入或输出数据,需调用acldvppMalloc申请内存。
    在申请输出内存前,可调用acldvppJpegPredictEncSize接口根据输入图片描述信息、图片编码配置数据可预估图片编码后所需的输出内存的大小。
  4. 调用acldvppJpegEncodeAsync异步接口进行编码。
    对于异步接口,还需调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成。
  5. 调用acldvppDestroyJpegeConfig接口销毁图片编码配置数据。
  6. 在编码结束后,需及时调用acldvppFree接口释放输入、输出内存。
  7. 调用acldvppDestroyChannel接口销毁图片数据处理的通道。
    销毁图片数据处理的通道后,再调用acldvppDestroyChannelDesc接口销毁通道描述信息。

代码

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
91
92
93
94
95
96
97
98
99
// 1.AscendCL初始化
aclRet = aclInit(nullptr);

// 2.运行管理资源申请(依次申请Device、Context、Stream)
aclrtContext context_;
aclrtStream stream_;
aclrtSetDevice(0);
aclrtCreateContext(&context_, 0);
aclrtCreateStream(&stream_);

// 3.创建图片数据处理通道时的通道描述信息,dvppChannelDesc_是acldvppChannelDesc类型
dvppChannelDesc_ = acldvppCreateChannelDesc();

// 4.创建图片数据处理的通道
aclRet = acldvppCreateChannel(dvppChannelDesc_);

// 5.申请输入内存(区分运行状态)
// 调用aclrtGetRunMode接口获取软件栈的运行模式,如果调用aclrtGetRunMode接口获取软件栈的运行模式为ACL_HOST,则需要通过aclrtMemcpy接口将输入图片数据传输到Device,数据传输完成后,需及时释放内存;否则直接申请并使用Device的内存
aclrtRunMode runMode;
ret = aclrtGetRunMode(&runMode);
// inputPicWidth、inputPicHeight分别表示图片的对齐后宽、对齐后高,此处以YUV420SP格式的图片为例
uint32_t PicBufferSize = inputPicWidth * inputPicHeight * 3 / 2;
if(runMode == ACL_HOST){
// 申请Host内存vpcInHostBuffer
void* vpcInHostBuffer = nullptr;
vpcInHostBuffer = malloc(PicBufferSize);
// 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
ReadPicFile(picName, vpcInHostBuffer, PicBufferSize);
// 申请Device内存inDevBuffer_
aclRet = acldvppMalloc(&inDevBuffer_, PicBufferSize);
// 通过aclrtMemcpy接口将输入图片数据传输到Device
aclRet = aclrtMemcpy(inDevBuffer_, PicBufferSize, vpcInHostBuffer, PicBufferSize, ACL_MEMCPY_HOST_TO_DEVICE);
// 数据传输完成后,及时释放内存
free(vpcInHostBuffer);
} else {
// 申请Device输入内存inDevBuffer_
ret = acldvppMalloc(&inDevBuffer_, PicBufferSize);
// 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现
ReadPicFile(picName, inDevBuffer_, PicBufferSize);
}

// 6. 创建编码输入图片的描述信息,并设置各属性值
// encodeInputDesc_是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. 创建图片编码配置数据,设置编码质量
// 编码质量范围[0, 100],其中level 0编码质量与level 100差不多,而在[1, 100]内数值越小输出图片质量越差。
jpegeConfig_ = acldvppCreateJpegeConfig();
acldvppSetJpegeConfigLevel(jpegeConfig_, 100);

// 8. 申请输出内存,申请Device内存encodeOutBufferDev_,存放编码后的输出数据
uint32_t outBufferSize= 0;
ret = acldvppJpegPredictEncSize(encodeInputDesc_, jpegeConfig_, &outBufferSize);
ret = acldvppMalloc(&outBufferDev_, outBufferSize)

// 9. 执行异步编码,再调用aclrtSynchronizeStream接口阻塞程序运行,直到指定Stream中的所有任务都完成
aclRet = acldvppJpegEncodeAsync(dvppChannelDesc_, encodeInputDesc_, encodeOutBufferDev_,
&outBufferSize, jpegeConfig_, stream_);
aclRet = aclrtSynchronizeStream(stream_);

// 10. 编码结束后,释放资源,包括编码输入/输出图片的描述信息、编码输入/输出内存、通道描述信息、通道等
acldvppDestroyPicDesc(encodeInputDesc_);

if(runMode == ACL_HOST) {
// 该模式下,由于处理结果在Device侧,因此需要调用内存复制接口传输结果数据后,再释放Device侧内存
// 申请Host内存outputHostBuffer
void* outputHostBuffer = nullptr;
outputHostBuffer = malloc(outBufferSize_);
// 通过aclrtMemcpy接口将Device的处理结果数据传输到Host
aclRet = aclrtMemcpy(outputHostBuffer, outBufferSize_, encodeOutBufferDev_, outBufferSize_, ACL_MEMCPY_DEVICE_TO_HOST);
// 释放掉输入输出的device内存
(void)acldvppFree(inputDevBuff);
(void)acldvppFree(encodeOutBufferDev_);
// 数据使用完成后,释放内存
free(outputHostBuffer);
} else {
// 此时运行在device侧,处理结果也在Device侧,可以根据需要操作处理结果后,释放Device侧内存
(void)acldvppFree(inputDevBuff);
(void)acldvppFree(encodeOutBufferDev_);
}
acldvppDestroyChannel(dvppChannelDesc_);
(void)acldvppDestroyChannelDesc(dvppChannelDesc_);
dvppChannelDesc_ = nullptr;

// 11. 释放运行管理资源(依次释放Stream、Context、Device)
aclrtDestroyStream(stream_);
aclrtDestroyContext(context_);
aclrtResetDevice(0);

// 12.AscendCL去初始化
aclRet = aclFinalize();
// ....

参考资料

CANN文档