VDEC视频解码
VDEC视频解码
VDEC(Video Decoder)负责将H264/H265格式的视频码流解码为YUV/RGB格式的图片。关于VDEC功能的详细介绍及约束请参见VDEC功能及约束说明。
本节介绍VDEC视频编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及视频解码,则应用程序中必须包含解码的代码逻辑,关于视频解码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用的流程

当前系统支持解码H264/H265的视频码流,关键接口的说明如下:
-
调用acl.himpi.sys_init接口进行媒体数据处理系统初始化。
-
调用acl.himpi.vdec_get_pic_buf_size接口获取解码所需的帧存大小、调用acl.himpi.vdec_get_tmv_buf_size接口获取矢量预测缓冲区的大小,在创建通道时需要使用这些数据。
-
调用acl.himpi.vdec_create_chn接口创建通道。
-
调用acl.himpi.dvpp_malloc接口申请Device上的内存,存放输入或输出数据。
-
解码前,需调用acl.himpi.vdec_start_recv_stream接口通知解码器启动接收码流,再调用acl.himpi.vdec_send_stream接口发送解码码流,acl.himpi.vdec_send_stream接口是异步接口,调用该接口仅表示任务下发成功,还需要调acl.himpi.vdec_get_frame接口获取解码结果数据,成功获取解码数据后,可以调用acl.himpi.vdec_release_frame接口释放帧相关的资源。
解码结束后,需调用acl.himpi.vdec_stop_recv_stream接口通知解码器停止接收码流。
-
调用acl.himpi.dvpp_free接口释放输入、输出内存。
-
调用acl.himpi.vdec_destroy_chn接口销毁通道。
-
调用acl.himpi.sys_exit接口进行媒体数据处理系统去初始化。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
## 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)。
run_mode, ret = acl.rt.get_run_mode()
## 2.pyACL 初始化。
ret = acl.init()
## 3.运行管理资源申请(依次申请Device、Context)。
ret = acl.rt.set_device(0)
context, ret = acl.rt.create_context(0)
## 4.初始化媒体数据处理系统。
ret = acl.himpi.sys_init()
## 5.创建通道。
channel_id = 0
buf_attr = [1920, 1080, 0, 8, HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420, HI_COMPRESS_MODE_NONE]
tmv_buf_size = acl.himpi.vdec_get_tmv_buf_size(HI_PT_H264, 1920, 1080)
attr = {'type': HI_PT_H264, 'mode': HI_VDEC_SEND_MODE_FRAME,
'pic_width': 1920, 'pic_height': 1080,
'stream_buf_size': 1920 * 1080 * 3 // 2, 'frame_buf_size': 0,
'frame_buf_cnt': 16, 'video_attr': {'ref_frame_num': 12, 'temporal_mvp_en': HI_TRUE,
'tmv_buf_size': tmv_buf_size}}
ret = acl.himpi.vdec_create_chn(channel_id, attr)
## 6.设置通道属性。
video_param_dict, ret = acl.himpi.vdec_get_chn_param(channel_id)
video_param_dict["video_param"]["dec_mode "] = HI_VIDEO_DEC_MODE_IPB
video_param_dict["video_param"]["compress_mode "] = HI_COMPRESS_MODE_HFBC
video_param_dict["video_param"]["video_format "] = HI_VIDEO_FORMAT_TILE_64x16
video_param_dict["video_param"]["out_order"] = HI_VIDEO_OUT_ORDER_DISPLAY
video_param_dict["display_frame_num"] = 3
ret = acl.himpi.vdec_set_chn_param(i, video_param_dict)
## 7.解码器启动接收码流。
ret = acl.himpi.vdec_start_recv_stream(channel_id)
## 8.发送码流。
## 8.1 申请输入内存。
## input_size表示输入图片占用的内存大小,此处以1024 Byte为例,用户需根据实际情况计算内存大小。
input_size = 1024;
input_addr, ret = acl.himpi.dvpp_malloc(0, input_size);
#如果运行模式为ACL_HOST,则需要申请Host内存,将输入图片数据读入Host内存,再通过acl.rt.memcpy接口将Host的图片数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入图片数据读入Device内存。
#直接将输入图片数据读入Device内存。
if run_mode == ACL_HOST:
# 申请Host内存。
input_buffer, ret= acl.rt.malloc_host(input_size)
# 将输入图片读入内存中。
vdec_file = np.fromfile(vdec_file_path, dtype=np.byte)
vdec_file_size = vdec_file.itemsize * vdec_file.size
bytes_data = vdec_file.tobytes()
vdec_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, vdec_file_ptr, vdec_file_size, ACL_MEMCPY_HOST_TO_DEVICE)
else:
# 将输入图片读入内存中。
vdec_file = np.fromfile(vdec_file_path, dtype=np.byte)
vdec_file_size = vdec_file.itemsize * vdec_file.size
bytes_data = vdec_file.tobytes()
vdec_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, vdec_file_ptr, vdec_file_size, ACL_MEMCPY_DEVICE_TO_DEVICE)
## 8.2 申请输出内存。
output_size = 1920 * 1080 * 3 // 2
output_addr, ret = acl.himpi.dvpp_malloc(0, output_size);
## 8.3 构造存放一帧输入码流信息的字典。
stream = {'end_of_frame': HI_TRUE, 'end_of_stream': HI_FALSE,
'need_display': HI_TRUE, 'pts': 0,
'len': input_size, 'addr': input_addr}
## 8.4 构造存放一帧输出结果信息的字典。
out_pic_info = {"width": 1920,
"height": 1080,
"width_stride": 1920,
"height_stride": 1080,
"pixel_format": HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420,
'vir_addr': output_addr,
"buffer_size": output_size}
## 8.4 发送一帧码流。
ret = acl.himpi.vdec_send_stream(channel_id, stream, out_pic_info, 0)
## 9.接收解码结果。
## 9.1 获取解码结果。
frame_info, supplement, stream, ret = acl.himpi.vdec_get_frame(channel_id, 0)
if ret == 0:
dec_result = frame_info['v_frame']['frame_flag']
if dec_result == 0: # 0: Decode success
print("Chn %u GetFrame Success, Decode Success \n"%channel_id)
elif dec_result == 1: # 1:Decode fail
print("Chn %u GetFrame Success, Decode Fail \n"%channel_id)
elif dec_result == 2: # 2:This result is returned for the second field of
print("Chn %u GetFrame Success, No Picture \n"%channel_id)
elif dec_result == 3: # 3: Reference frame number set error
print("Chn %u GetFrame Success, RefFrame Num Error \n"%channel_id)
elif dec_result == 4: # 4: Reference frame size set error
print("Chn %u GetFrame Success, RefFrame Size Error \n"% channel_id)
## 9.2 如果运行模式为ACL_HOST,且Host上需要展示VDEC输出的图片数据,则需要申请Host内存,通过acl.rt.memcpy接口将Device的输出图片数据传输到Host。
## 9.2 获取解码结果数据。
if run_mode == ACL_HOST:
# 申请Host内存。
output_buffer, ret= acl.rt.malloc_host(out_pic_info['buffer_size'])
# 数据传输。
ret = acl.rt.memcpy(output_buffer, out_pic_info['buffer_size'], frame_info['v_frame']['virt_addr'][0], out_pic_info['buffer_size'], ACL_MEMCPY_DEVICE_TO_HOST)
# ......
# 数据使用完成后,及时释放不使用的内存。
ret = acl.rt.free_host(output_buffer)
else:
# 可以直接使用JPEGD的输出图片数据,在outputPic.picture_address指向的内存中。
# ......
## 9.3 释放输入、输出内存。
ret = acl.himpi.dvpp_free(frame_info['v_frame']['virt_addr'][0])
ret = acl.himpi.dvpp_free(stream['addr'])
## 9.4 释放资源。
ret = acl.himpi.vdec_release_frame(channel_id, frame_info)
## 10.解码器停止接收码流。
ret = acl.himpi.vdec_stop_recv_stream(channel_id)
## 7.销毁通道。
ret = acl.himpi.vdec_destroy_chn(channel_id)
## 8. 媒体数据处理系统去初始化。
ret = acl.himpi.sys_exit()
## 9. 释放运行管理资源(依次释放Context、Device)。
ret = acl.rt.destroy_context(context)
ret = acl.rt.reset_device(0)
## 10.pyACL去初始化。
ret = acl.finalize()
父主题: 媒体数据处理V2
VENC视频编码
VENC(Video Encoder)将YUV420SP格式的图片编码成H264/H265格式的视频码流。关于VENC功能的详细介绍请参见VENC功能及约束说明。
本节介绍VENC视频编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
接口调用流程
开发应用时,如果涉及视频编码,则应用程序中必须包含编码的代码逻辑,关于编码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
图1 接口调用流程

当前系统支持H264/H256格式的视频码流,关键接口的说明如下:
-
调用acl.himpi.sys_init接口进行媒体数据处理系统初始化。
-
调用acl.himpi.venc_create_chn函数创建完通道。
成功创建通道之后,您可以根据实际需求设置编码的高级参数,例如场景模式、码流控制器的高级参数等,请参见acl.himpi.venc_set_jpeg_param~acl.himpi.venc_compact_jpeg_tables章节中的接口说明。
-
调用acl.himpi.venc_get_fd将通道ID转换为一个文件句柄。
-
调用acl.himpi.sys_create_epoll函数创建DVPP epoll实例。
-
调用acl.himpi.sys_ctl_epoll函数将编码通道的文件句柄添加到epoll实例中,由epoll实例处理。
select或者poll方式,不需要执行该步骤。
-
调用acl.himpi.venc_start_chn函数通知通道准备开始编码。
-
调用acl.himpi.dvpp_malloc接口申请存放Device上输入数据的内存。
-
启动一个用户态线程,调用acl.himpi.sys_wait_epoll函数等待编码完成。
-
之后用户就可以调用acl.himpi.venc_send_frame函数发送待编码的码流。
-
一旦编码完成,acl.himpi.sys_wait_epoll函数或select函数或poll函数就会返回,用户就可以调用acl.himpi.venc_query_status接口查询编码状态,再调用acl.himpi.venc_get_stream函数获取编码结果。
-
用户需要注意的是,编码结果数据使用完成之后,需要及时调用acl.himpi.venc_release_stream函数释放buffer。否则会因编码buffer用完导致后续编码无法进行。
-
调用acl.himpi.dvpp_free接口释放输入内存。
-
当用户不需发送图像到目的通道继续编码时,需要调用acl.himpi.venc_stop_chn函数通知该通道不再接收新的输入图片。
-
调用acl.himpi.sys_ctl_epoll函数从epoll实例中删除编码通道的文件句柄。
-
当用户完成所有编码之后,需要调用acl.himpi.venc_destroy_chn释放编码通道以及内部内存资源。
-
调用acl.himpi.sys_close_epoll函数销毁DVPP epoll实例。
-
调用acl.himpi.sys_exit接口进行媒体数据处理系统去初始化。
优化视频编码质量
在实现VENC视频编码功能时,可在创建通道时设置基本参数、或调用对应的set接口设置高级参数,优化视频编码质量,以下调整手段可以叠加使用,效果是叠加的,例如:
- H264视频数据获取场景,分辨率720P,gop = 60,帧率30fps,码率1M需要提升编码质量,可以使用如下优化手段组合:CBR模式、HI_VENC_SCENE_0、stats_time等于2、profile等于2、关闭宏块级码控。
- H265电影场景,分辨率1080P,gop=30,帧率25fps,码率2M需要提升编码质量,可以使用如下优化手段组合:CBR模式、HI_VENC_SCENE_1、stats_time等于1、关闭宏块级码控。
当前支持以下方式优化视频编码质量:
-
设置基本参数,优化视频编码质量
不同分辨率的视频,其编码质量与视频的帧率、GOP(Group of pictures)、码率有关,在调用acl.himpi.venc_create_chn接口创建通道时,可设置编码的等级、设置H.264/H.265协议编码场景下CBR/VBR/AVBR/CVBR/QVBR模式的帧率、GOP、码率等参数,来调整视频编码质量:
- 编码等级,通过通过hi_venc_chn_attr["venc_attr"]字典内的“profile”属性来确定。
- 帧率,通过hi_venc_chn_attr["rc_attr"]字典内的“src_frame_rate”输入帧率参数、“dst_frame_rate”输出帧率属性来确定。
- GOP,通过hi_venc_chn_attr["rc_attr"]字典内的“gop”属性来确定。
- 码率,通过hi_venc_chn_attr["rc_attr"]字典内的“bit_rate”、“max_bit_rate”或“target_bit_rate”属性来确定。
表1 典型场景下帧率、GOP、码率的取值
画质/分辨率 帧率 GOP 码率(mbps) 4K 3840*2160/4096*2160 25或30 50或60 - 视频数据获取场景 H264/H265码流,码率取值8 12。12。 H264码流,不涉及。
- 秀场/主播/短视频场景 H265码流,码率取值6
- 游戏视频场景 H264/H265码流,码率取值10~16。2K 2560*1440 25或30 50或60 - 视频数据获取场景 H264/H265码流,码率取值6 10。8。 H264码流,不涉及。
- 秀场/主播/短视频场景 H265码流,码率取值4.8
- 游戏视频场景 H264/H265码流,码率取值6~10。1080P(蓝光) 1920*1080 25或30 50或60 - 视频数据获取场景 H265码流,码率取值1 4。 H264码流,码率取值26。
- 秀场/主播/短视频场景 H265码流,码率取值1.43.6。 H264码流,码率取值24.8。
- 游戏视频场景 H264/H265码流,码率取值3~6。720P(高清) 1280*720 25或30 50或60 - 视频数据获取场景 H265码流,码率取值0.8 2。 H264码流,码率取值13。
- 秀场/主播/短视频场景 H265码流,码率取值12。 H264码流,码率取值13。
- 游戏视频场景 H264/H265码流,码率取值2~4。480P/D1_N(标清) 854*480/720*480 25或30 50或60 - 视频数据获取场景 H265码流,码率取值0.3 0.7。 H264码流,码率取值0.61.4。
- 秀场/主播/短视频场景 H265码流,码率取值0.250.6。 H264码流,码率取值0.30.7。
- 游戏视频场景 不涉及。576P/D1(标清) 720*576 25或30 50或60 - 视频数据获取场景 H265码流,码率取值0.3 0.7。 H264码流,码率取值0.61.4。
- 秀场/主播/短视频场景 H265码流,码率取值0.250.6。 H264码流,码率取值0.30.7。
- 游戏视频场景 不涉及。270P(流畅) 480*270 25或30 50或60 - 视频数据获取场景 不涉及。
- 秀场/主播/短视频场景 H265码流,码率取值0.2。 H264码流,码率取值0.3。
- 游戏视频场景 不涉及。CIF P/N 352*288/320*240 25或30 50或60 - 视频数据获取场景 H264/H265码流,码率取值0.25。
- 秀场/主播/短视频场景 不涉及。
- 游戏视频场景 不涉及。 -
设置高级参数,调整视频编码细节
您可以调用接口设置码控模式、宏块级码率控制参数、编码场景模式等,来调整视频编码的细节,进一步改善编码质量。
表2 高级配置项列表
配置项 接口 参数名 说明 码控模式 acl.himpi.venc_create_chn hi_venc_chn_attr["rc_attr"]字典内的“rc_mode”属性 追求码率平稳或追求PSNR大且码率符合目标值,配置为CBR。 追求节省码率,对主观编码质量有一定要求,配置为VBR。 追求节省码率,对主观编码质量有一定要求,且场景中有较多静止画面,配置为AVBR。 追求PSNR且对码率上浮没有严格要求,配置为QVBR。 追求节省码率,对主观编码质量有一定要求,且可以根据带宽、存储空间要求进行更多调整,配置为CVBR。 码率控制模型统计时间 acl.himpi.venc_create_chn hi_venc_chn_attr["rc_attr"]字典内各模式属性值字典内的“stats_time”属性 关注长期码率稳定,短期波动不在意的可以设置大一些,例:DVR存盘。设大可以提高重编码判决的门槛,重编码次数会减少,但是码率波动会加大。 宏块级码率控制参数 acl.himpi.venc_set_rc_param hi_venc_rc_param字典内的“threshold_i”、“threshold_p”、“threshold_b”、“direction”、“row_qp_delta”属性。 如果图像内容复杂、细节较多或用户关注PSNR等客观指标时,需关闭宏块级码率控制。 第一帧的起始Qp值 acl.himpi.venc_create_chn hi_venc_rc_param字典内的“first_frame_start_qp”属性 典型场景下,用户配置的码率小于表1中给的参考值,且编码后的视频第一帧明显模糊,则建议配置“first_frame_start_qp”属性,参数值取[min_i_qp, max_i_qp]的中间值,例如,[min_i_qp, max_i_qp]为[30, 40],则“first_frame_start_qp”配置为“35”,同时将“max_reencode_times”配置为“0”,会获得较好的编码质量。 编码场景模式 acl.himpi.venc_set_scene_mode hi_venc_scene_mode类 安防场景配置为HI_VENC_SCENE_0;自动驾驶、直播、游戏、动画、电影配置为HI_VENC_SCENE_1。
示例代码
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
## 1.获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)。
run_mode, ret = acl.rt.get_run_mode()
## 2.pyACL 初始化。
ret = acl.init()
## 3.运行管理资源申请(依次申请Device、Context)。
ret = acl.rt.set_device(0)
context, ret = acl.rt.create_context(0)
## 4.初始化媒体数据处理系统。
ret = acl.himpi.sys_init()
## 5.设置VENC模块参数。
param = {'mod_type':HI_VENC_MOD_H265}
param, ret = acl.himpi.venc_get_mod_param(param)
param['jpeg_mod_param']['one_stream_buf'] = 1
ret = acl.himpi.venc_set_mod_param(param)
## 6.创建通道。
channel_id = 0
venc_attr = {'type': HI_VENC_MOD_H265, 'profile': 0,
'max_pic_width': 128, 'pic_width': 128,
'max_pic_height': 128, 'pic_height': 128,
'buf_size': 1024 * 1024 * 2, 'is_by_frame': 1}
rc_attr = {'rc_mode':HI_VENC_RC_MODE_H265_VBR,
'h265_vbr':{'gop': 30, 'stats_time': 1,
'src_frame_rate': 30, 'dst_frame_rate': 30,
'max_bit_rate': 4000}}
gop_attr = {'gop_mode':0, 'normal_p':{'ip_qp_delta':3}}
attr = {'venc_attr':venc_attr, 'rc_attr':rc_attr, 'gop_attr':gop_attr}
ret = acl.himpi.venc_create_chn(channel_id, attr)
## 7.通知编码器开始接收输入数据。
recv_param = {'recv_pic_num':-1}
ret = acl.himpi.venc_start_chn(channel_id, recv_param)
## 8.发送输入数据。
## 8.1 申请输入内存。
input_size = 128 * 128 * 3 // 2
input_addr, ret = acl.himpi.dvpp_malloc(0, input_size);
## 如果运行模式为ACL_HOST,则需要申请Host内存,将输入数据读入Host内存,再通过acl.rt.memcpy接口将Host的数据传输到Device,数据传输完成后,需及时释放Host内存;否则直接将输入数据读入Device内存。
## 直接将输入数据读入Device内存。
if run_mode == ACL_HOST:
# 将输入图片读入内存中。
jpege_file = np.fromfile(jpege_filee_path, dtype=np.byte)
jpege_file_size = jpege_file.itemsize * jpege_file.size
bytes_data = jpege_file.tobytes()
jpege_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, jpege_file_ptr, jpege_file_size, ACL_MEMCPY_HOST_TO_DEVICE)
else:
# 将输入图片读入内存中。
jpege_file = np.fromfile(jpege_file_path, dtype=np.byte)
jpege_file = jpege_file.itemsize * jpege_file.size
bytes_data = jpege_file.tobytes()
jpege_file_ptr = acl.util.bytes_to_ptr(bytes_data)
# 数据传输。
ret = acl.rt.memcpy(input_addr, input_size, jpege_file_ptr, jpege_file_size, ACL_MEMCPY_DEVICE_TO_DEVICE)
## 8.2 发送输入数据,开始编码。
v_frame = {'width': 128,
'height': 128,
'field': HI_VIDEO_FIELD_FRAME,
'pixel_format': HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420,
'video_format': HI_VIDEO_FORMAT_LINEAR,
'compress_mode': HI_COMPRESS_MODE_NONE,
'dynamic_range': HI_DYNAMIC_RANGE_SDR8,
'color_gamut': HI_COLOR_GAMUT_BT709,
'header_stride': [0, 0, 0],
'width_stride': [128, 0, 0],
'height_stride': [0, 0, 0],
'header_phys_addr': [0, 0, 0],
'phys_addr': [0, 0, 0],
'header_virt_addr': [0, 0, 0],
'virt_addr': [input_addr, 0, 0],
'time_ref': 0,'pts': cur_time}
frame = {'v_frame':v_frame, 'pool_id':0, 'mod_id':HI_ID_VENC}
ret = acl.himpi.venc_send_frame(channel_id, frame, 0)
## 9.获取编码结果。
## 9.1 通过EPOLL处理编码完成事件。
fd = acl.himpi.venc_get_fd(channel_id)
epoll_fd, ret = acl.himpi.sys_create_epoll(10)
event['data'] = fd
event['events'] = HI_DVPP_EPOLL_IN
ret = acl.himpi.sys_ctl_epoll(epoll_fd, HI_DVPP_EPOLL_CTL_ADD, fd, event)
## 编码完成前,会超时阻塞在这里,一旦完成,才会往下执行。
events, eventCount, ret = acl.himpi.sys_wait_epoll(epoll_fd, 3, 1000);
## 9.2 获取编码结果。
status, ret = acl.himpi.venc_query_status(channel_id)
stream = {'pack_cnt': status['cur_packs']}
stream, ret = acl.himpi.venc_get_stream(self.channel_id, stream, 1000)
## 9.3 如果运行模式为ACL_HOST,且Host上需要使用编码输出的码流,则需要申请Host内存,通过acl.rt.memcpy接口将Device的输出码流传输到Host。
## 9.3 获取编码输出码流数据。
if run_mode == ACL_HOST:
# 申请Host内存。
output_buffer, ret= acl.rt.malloc_host(output_ize)
# 数据传输。
ret = acl.rt.memcpy(output_buffer, output_ize, stream['pack'][0]['addr'], output_ize, ACL_MEMCPY_DEVICE_TO_HOST)
# ......
# 数据使用完成后,及时释放不使用的内存。
ret = acl.rt.free_host(output_buffer)
else:
# 可以直接使用编码输出码流数据,在stream['pack'][0]['addr']指向的内存中。
# ......
## 10.释放输入内存和输出码流。
ret = acl.himpi.dvpp_free(input_addr)
ret = acl.himpi.venc_release_stream(channel_id, stream)
## 11.通知编码器停止接收输入数据。
ret = acl.himpi.venc_stop_chn(channel_id)
ret = acl.himpi.sys_ctl_epoll(epoll_fd, HI_DVPP_EPOLL_CTL_DEL, fd, event)
ret = acl.himpi.sys_close_epoll(epoll_fd)
## 12.销毁通道。
ret = acl.himpi.venc_destroy_chn(channel_id)
## 13. 媒体数据处理系统去初始化。
ret = acl.himpi.sys_exit()
## 14. 释放运行管理资源(依次释放Context、Device)。
ret = acl.rt.destroy_context(context)
ret = acl.rt.reset_device(0)
## 15.pyACL去初始化。
ret = acl.finalize()
父主题: 媒体数据处理V2
简介
单算子调用的使用场景
如果AI应用中不仅仅包括模型推理,还有数学运算(例如BLAS基础线性代数运算)、数据类型转换等功能,也想使用昇腾的算力,昇腾CANN还能支持吗?
答案是肯定的,昇腾CANN提供了单算子调用的方式,直接通过pyACL接口加载并执行单个算子,省去模型构建、训练的过程,相对轻量级,又可以使用昇腾的算力。
另外,自定义的算子,也可以通过单算子调用的方式来验证算子的功能。
单算子调用与模型推理的差别
在解释单算子调用与模型推理的差别前,我们先观察下面这个开发流程图,找出基本的共同点、不同点。
- 共同点:
- 需要pyACL初始化和去初始化、运行管理资源申请和释放。
- 涉及加载、执行的步骤,但是要注意,两者的加载、执行是调用不同的pyACL接口。
- 不同点:
- 模型推理涉及模型卸载的步骤,单算子调用不涉及。
图1 单算子调用与模型推理的流程对比

父主题: 单算子调用
开发流程
图1 开发流程

-
创建代码目录。
在开发应用前,您需要先创建目录,存放代码文件、脚本、测试图片数据、模型文件等。
-
准备算子。
使用ATC工具编译算子生成om模型文件。
该种方式,需要先构造*.json格式单算子描述文件(描述算子的输入、输出及属性等信息),借助ATC工具,将单算子描述文件编译成om模型文件,再分别调用pyACL接口加载om模型文件、执行算子。
关于ATC工具的使用说明,请参见《ATC工具使用指南》。
-
开发应用。
单算子调用的流程请参见接口调用流程及相关的示例代码。
-
运行应用,请参见应用调试。
父主题: 单算子调用
接口调用流程
开发应用时,如果涉及执行单个算子,则应用程序中必须包含执行单个算子的代码逻辑。关于执行单个算子的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
系统支持的算子请参见《算子清单》。
图1 算子调用流程

关键接口的说明如下:
-
加载算子模型文件。
支持以下2种方式中的一种加载单算子模型文件:
- 调用acl.op.set_model_dir接口,设置加载模型文件的目录,目录下存放单算子模型文件(*.om文件)。
- 调用acl.op.load接口,从内存中加载单算子模型数据,由用户管理内存。单算子模型数据是指“单算子编译成*.om文件后,再将om文件读取到内存中”的数据。
-
调用acl.rt.malloc接口申请Device上的内存,存放执行算子的输入、输出数据。
-
动态Shape场景,如果无法明确算子的输出Shape时,在执行算子前,还需推导或预估算子的输出Shape。
需用户调用acl.op.infer_shape接口、acl.get_tensor_desc_num_dims接口、acl.get_tensor_desc_dim_v2接口、acl.get_tensor_desc_dim_range等接口,推导或预估算子的输出Shape,作为算子执行接口acl.op.execute_v2的输入。
-
执行算子。
- 对于被封装成pyACL接口的算子(参见调用CBLAS接口),包括GEMM算子、Cast算子,目前支持以下两种执行方式:
- 不以handle方式执行算子,接口名称中不包含“Handle”关键字,例如,调用acl.blas.gemm_ex接口(封装GEMM算子)、acl.op.cast接口(封装Cast算子)等执行算子。
- 以handle方式执行算子,接口名称中包含“Handle”关键字,例如,调用acl.blas.create_handle_for_gemm_ex接口、acl.op.create_handle_for_cast接口等创建handle后,还需要调用acl.op.execute_with_handle接口执行算子。
- 对于未被封装成pyACL接口的算子,目前执行以下两种执行方式:
- 不以handle方式执行算子,调用acl.op.execute_v2接口执行算子。
- 以handle方式执行算子,调用acl.op.create_handle接口创建handle,再调用acl.op.execute_with_handle接口执行算子。
:::note 说明 不以handle方式执行算子时,每次执行算子时,系统内部都会根据算子描述信息匹配内存中的模型。
以handle方式执行算子时,系统内部将算子描述信息匹配到内存中的模型,并缓存在Handle中,每次执行算子时,无需重复匹配算子与模型,因此在涉及多次执行同一个算子时,效率更高。但Handle使用结束后,需调用acl.op.destroy_handle接口释放。 :::
- 对于被封装成pyACL接口的算子(参见调用CBLAS接口),包括GEMM算子、Cast算子,目前支持以下两种执行方式:
-
调用acl.rt.synchronize_stream接口阻塞应用运行,直到指定Stream中的所有任务都完成。
-
调用acl.rt.free接口释放内存。
父主题: 单算子调用
在线提单