VPC图片处理典型功能
本节以抠图、缩放为例说明VPC图像处理时的接口调用流程,同时配合以下典型功能的示例代码辅助理解该接口调用流程。
VPC(Vision Preprocessing Core)负责图像处理功能,支持对图片做抠图、缩放、格式转换等操作。关于VPC功能的详细介绍请参见功能说明,关于VPC功能对输入、输出的约束要求,请参见约束说明。
Atlas 200/300/500 推理产品上,当前版本不支持该功能。
Atlas 训练系列产品上,当前版本不支持该功能。
典型功能接口调用流程(以缩放为例)
开发应用时,如果涉及抠图、缩放等图片处理,则应用程序中必须包含图片处理的代码逻辑,关于图片处理的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。
如果在Host上调用DVPP接口,图像处理的结果数据都在Device的内存中,如果想访问结果数据,需要将结果数据传输回Host侧。
当前系统支持对输入图片做抠图、缩放等处理,关键接口的说明如下:
- 调用hi_mpi_sys_init接口进行媒体数据处理系统初始化。
- 调用hi_mpi_vpc_create_chn接口创建通道。
- 调用hi_mpi_dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
Atlas 200I/500 A2推理产品上,还支持使用aclrtMalloc接口申请内存。
Atlas A2训练系列产品上,还支持使用aclrtMalloc接口申请内存。
调用hi_mpi_dvpp_malloc接口申请的内存为媒体数据处理的专用内存,但专用内存的地址空间有限,若关注内存规划或内存资源有限时,建议调用aclrtMalloc接口申请内存。
- 执行抠图、缩放等,此步骤以缩放为例说明,其它功能(例如格式转换、金字塔等)请参见VPC功能下的接口说明。
调用hi_mpi_vpc_resize接口,对图片进行缩放。hi_mpi_vpc_resize接口是异步接口,调用该接口成功仅表示任务下发成功,还需要调用hi_mpi_vpc_get_process_result接口等待任务完成。
- 可以跟hi_mpi_vpc_resize接口在同一个线程中调用hi_mpi_vpc_get_process_result接口,也可以新起一个线程调用hi_mpi_vpc_get_process_result接口,后者多线程并行,提高效率,但用户需自行实现线程间同步。
- 在实现抠图、缩放等功能时,通过将输入图片和输出图片的格式设置成不同的,达到转换图片格式的目的。
- 调用aclrtGetRunMode接口获取软件栈的运行模式,如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入,模型推理的相关介绍请参见模型推理、模型动态AIPP推理。
- 调用hi_mpi_dvpp_free接口释放输入、输出内存。
Atlas 200I/500 A2推理产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
Atlas A2训练系列产品上,若使用aclrtMalloc接口申请内存,则需使用aclrtFree接口释放内存。
- 调用hi_mpi_vpc_destroy_chn接口销毁通道。
- 调用hi_mpi_sys_exit接口进行媒体数据处理系统去初始化。
图片缩放示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
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 |
// 1.AscendCL初始化 aclRet = aclInit(nullptr); // 2.运行管理资源申请(依次申请Device、Context) aclrtContext g_context; aclRet = aclrtSetDevice(0); aclRet = aclrtCreateContext(&g_context, 0); // 获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等) aclrtRunMode runMode; aclError aclRet = aclrtGetRunMode(&runMode); // 3.初始化媒体数据处理系统 int32_t ret = hi_mpi_sys_init(); // 4.创建通道 hi_vpc_chn chnId; hi_vpc_chn_attr stChnAttr; ret = hi_mpi_vpc_sys_create_chn(&chnId, &stChnAttr); // 5.执行缩放 // 5.1 构造存放输入图片信息的结构体 hi_vpc_pic_info inputPic; inputPic.picture_width = 1920; inputPic.picture_height = 1080; inputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; inputPic.picture_width_stride = 1920; inputPic.picture_height_stride = 1080; inputPic.picture_buffer_size = inputPic.picture_width_stride * inputPic.picture_height_stride * 3 / 2; // 5.2 准备输入图片数据 // 申请Device内存,用于媒体数据处理 ret = hi_mpi_dvpp_malloc(0, &inputPic.picture_address, inputPic.picture_buffer_size); // 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过aclrtMemcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存 if (runMode == ACL_HOST) { void* inputAddr = nullptr; // 申请Host内存 aclRet = aclrtMallocHost(&inputAddr, inputPic.picture_buffer_size); // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现 ReadPicFile(picName, inputAddr, inputPic.picture_buffer_size); // 数据传输 aclRet = aclrtMemcpy(inputPic.picture_address, inputPic.picture_buffer_size, inputAddr, inputPic.picture_buffer_size, ACL_MEMCPY_HOST_TO_DEVICE); // 完成数据传输后,需及时释放内存 aclrtFreeHost(inputAddr); inputAddr = nullptr; } else { // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现 ReadPicFile(picName, inputPic.picture_address, inputPic.picture_buffer_size); } // 5.3 构造存放输出图片信息的结构体 hi_vpc_pic_info outputPic; outputPic.picture_width = 960; outputPic.picture_height = 540; outputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; outputPic.picture_width_stride = 960; outputPic.picture_height_stride = 540; outputPic.picture_buffer_size = outputPic.picture_width_stride * outputPic.picture_height_stride * 3 / 2; ret = hi_mpi_dvpp_malloc(0, &outputPic.picture_address, outputPic.picture_buffer_size); // 初始化内存 if (runMode == ACL_HOST) { aclRet = aclrtMemset(outputPic.picture_address, outputPic.picture_buffer_size, 0, outputPic.picture_buffer_size); } else { memset(outputPic.picture_address, 0, outputPic.picture_buffer_size); } // 5.4 调用缩放接口 uint32_t taskID = 0; ret = hi_mpi_vpc_resize(chnId, &inputPic, &outputPic, 0, 0, 0, &taskID, -1); // 5.5 等待任务处理结束,任务处理结束后,输出图片数据在outputPic.picture_address指向的内存中 uint32_t taskIDResult = taskID; ret = hi_mpi_vpc_get_process_result(chnId, taskIDResult, -1); // 5.6 如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入 if (g_runMode == ACL_HOST) { hi_vpc_pic_info outputPicHost = outputPic; aclRet = aclrtMallocHost(&outputPicHost.picture_address, outputPic.picture_buffer_size); aclRet = aclrtMemcpy(outputPicHost.picture_address, outputPic.picture_buffer_size, outputPic.picture_address,outputPic.picture_buffer_size, ACL_MEMCPY_DEVICE_TO_HOST); // ...... // Host侧的数据使用完成后,释放Host内存 aclrtFreeHost(outputPicHost.picture_address); outputPicHost.picture_address = nullptr; } else { // 可以直接使用VPC的输出图片数据,在outputPic.picture_address指向的内存中 // TODO:推理相关的代码逻辑 } // 5.7 释放输入、输出内存 ret = hi_mpi_dvpp_free(inputPic.picture_address); ret = hi_mpi_dvpp_free(outputPic.picture_address); // 6.销毁通道 ret = hi_mpi_vpc_destroy_chn(chnId); // 7. 媒体数据处理系统去初始化 ret = hi_mpi_sys_exit(); // 8. 释放运行管理资源(依次释放Context、Device) aclRet = aclrtDestroyContext(g_context); aclRet = aclrtResetDevice(0); // 9.AscendCL去初始化 aclRet = aclFinalize(); // .... |
抠图示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 1.AscendCL初始化 aclRet = aclInit(nullptr); // 2.运行管理资源申请(依次申请Device、Context) aclrtContext g_context; aclRet = aclrtSetDevice(0); aclRet = aclrtCreateContext(&g_context, 0); 获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等) aclrtRunMode runMode; aclError aclRet = aclrtGetRunMode(&runMode); // 3.初始化媒体数据处理系统 int32_t ret = hi_mpi_sys_init(); // 4.创建通道 hi_vpc_chn chnId; hi_vpc_chn_attr stChnAttr; ret = hi_mpi_vpc_sys_create_chn(&chnId, &stChnAttr); // 5.执行抠图 // 5.1 构造存放输入图片信息的结构体 hi_vpc_pic_info inputPic; inputPic.picture_width = 1920; inputPic.picture_height = 1080; inputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; inputPic.picture_width_stride = 1920; inputPic.picture_height_stride = 1080; inputPic.picture_buffer_size = inputPic.picture_width_stride * inputPic.picture_height_stride * 3 / 2; // 5.2 准备输入图片数据 // 申请Device内存,用于媒体数据处理 ret = hi_mpi_dvpp_malloc(0, &inputPic.picture_address, inputPic.picture_buffer_size); // 如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过aclrtMemcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存 if (runMode == ACL_HOST) { void* inputAddr = nullptr; // 申请Host内存 aclRet = aclrtMallocHost(&inputAddr, inputPic.picture_buffer_size); // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现 ReadPicFile(picName, inputAddr, inputPic.picture_buffer_size); // 数据传输 aclRet = aclrtMemcpy(inputPic.picture_address, inputPic.picture_buffer_size, inputAddr, inputPic.picture_buffer_size, ACL_MEMCPY_HOST_TO_DEVICE); // 完成数据传输后,需及时释放内存 aclrtFreeHost(inputAddr); inputAddr = nullptr; } else { // 将输入图片读入内存中,该自定义函数ReadPicFile由用户实现 ReadPicFile(picName, inputPic.picture_address, inputPic.picture_buffer_size); } // 5.3 构造存放输出图片信息的结构体 // 该参数表示抠图数量 uint32_t multiCount = 1; // cropRegionInfos数组的大小与抠图数量保持一致 hi_vpc_crop_region_info cropRegionInfos[1]; hi_vpc_pic_info outputPic; for (uint32_t i = 0; i < multiCount; i++) { outputPic.picture_width = 960; outputPic.picture_height = 540; outputPic.picture_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; outputPic.picture_width_stride = 960; outputPic.picture_height_stride = 540; outputPic.picture_buffer_size = outputPic.picture_width_stride * outputPic.picture_height_stride * 3 / 2; ret = hi_mpi_dvpp_malloc(0, &outputPic.picture_address, outputPic.picture_buffer_size); // 初始化内存 if (runMode == ACL_HOST) { aclRet = aclrtMemset(outputPic.picture_address, outputPic.picture_buffer_size, 0, outputPic.picture_buffer_size); } else { memset(outputPic.picture_address, 0, outputPic.picture_buffer_size); } // 表示从输入图片中抠出以左上角为原点、分辨率960*540的子图 cropRegionInfos[i].dest_pic_info = outputPic; cropRegionInfos[i].crop_region.left_offset = 0; cropRegionInfos[i].crop_region.top_offset = 0; cropRegionInfos[i].crop_region.crop_width = 960; cropRegionInfos[i].crop_region.crop_height = 540; } // 5.4 调用抠图接口 uint32_t taskID = 0; ret = hi_mpi_vpc_crop(chnId, &inputPic, cropRegionInfos, 1, &taskID, -1); // 5.5 等待任务处理结束,任务处理结束后,输出图片数据在outputPic.picture_address指向的内存中 uint32_t taskIDResult = taskID; ret = hi_mpi_vpc_get_process_result(chnId, taskIDResult, -1); // 5.6 如果运行模式为ACL_HOST,且Host上需要展示VPC输出的图片数据,则需要申请Host内存,通过aclrtMemcpy接口将Device的输出图片数据传输到Host;如果Host上不需要展示VPC输出的图片数据,则VPC的输出图片数据可以直接作为模型推理的输入 if (g_runMode == ACL_HOST) { hi_vpc_pic_info outputPicHost = outputPic; aclRet = aclrtMallocHost(&outputPicHost.picture_address, outputPic.picture_buffer_size); aclRet = aclrtMemcpy(outputPicHost.picture_address, outputPic.picture_buffer_size, outputPic.picture_address,outputPic.picture_buffer_size, ACL_MEMCPY_DEVICE_TO_HOST); // ...... // Host侧的数据使用完成后,释放Host内存 aclrtFreeHost(outputPicHost.picture_address); outputPicHost.picture_address = nullptr; } else { // 可以直接使用VPC的输出图片数据,在outputPic.picture_address指向的内存中 // TODO:推理相关的代码逻辑 } // 5.7 释放输入、输出内存 ret = hi_mpi_dvpp_free(inputPic.picture_address); inputPic.picture_address = nullptr; for (uint32_t i = 0; i < multiCount; i++) { hi_mpi_dvpp_free(cropRegionInfos[i].dest_pic_info.picture_address); cropRegionInfos[i].dest_pic_info.picture_address = nullptr; } // 6.销毁通道 ret = hi_mpi_vpc_destroy_chn(chnId); // 7. 媒体数据处理系统去初始化 ret = hi_mpi_sys_exit(); // 8. 释放运行管理资源(依次释放Context、Device) aclRet = aclrtDestroyContext(g_context); aclRet = aclrtResetDevice(0); // 9.AscendCL去初始化 aclRet = aclFinalize(); // ....