JPEGE(JPEG Encoder)负责完成图像编码功能,将YUV格式图片编码成.jpg图片。关于JPEGE功能的详细介绍请参见JPEGE功能及约束说明。
本节介绍JPEGE图片编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。
Atlas 200/300/500 推理产品上,当前版本不支持该功能。
Atlas 训练系列产品上,当前版本不支持该功能。
开发应用时,如果涉及将YUV格式图片编码成JPEG压缩格式的图片文件,则应用程序中必须包含编码的代码逻辑,关于编码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。
成功创建通道之后,您可以根据实际需求设置编码的高级参数,例如场景模式、码流控制器的高级参数等,请参见acl.himpi.venc_set_jpeg_param~acl.himpi.venc_compact_jpeg_tables章节中的接口说明。
select或者poll方式,不需要执行该步骤。
在调用acl.himpi.venc_create_chn接口创建通道时,必须正确设置hi_venc_chn_attr["venc_attr"]["buf_size"]参数值(参数描述请参见hi_venc_attr)。
该方式下,相比由用户管理内存,输出结果数据的JPEG头中不存在COM注释字段,数据长度会短一点,但需要用户从DVPP返回的内存中拷贝输出结果数据到指定内存。
在调用acl.himpi.venc_create_chn接口创建通道时,需将hi_venc_chn_attr["venc_attr"]["buf_size"]参数值设置为0(参数描述请参见hi_venc_attr),然后调用acl.himpi.venc_get_jpege_predicted_size接口预估输出内存大小,调用acl.himpi.dvpp_malloc/acl.himpi.dvpp_free接口申请/释放输出内存。
该方式下,直接在调用acl.himpi.venc_send_jpege_frame接口时,设置输出内存地址,输出结果数据直接存放到用户设置的内存中,相比由系统管理内存的方式,用户可减少一次“从DVPP返回的内存中拷贝输出结果数据到指定内存”的操作,但输出结果数据的JPEG头中可能会存在COM注释字段(字段长度范围4~19Byte),数据长度会长一点。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考。
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 |
# 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 venc_attr = {'type': HI_PT_JPEG, '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} jpeg_attr = {'recv_mode':HI_VENC_PIC_RECV_SINGLE, 'mpf_cfg':{'large_thumbnail_num ': 0}} attr = {'venc_attr':venc_attr, 'jpeg_attr':jpeg_attr} ret = acl.himpi.venc_create_chn(channel_id, attr) # 6.设置JPEGE参数。 param, ret = acl.himpi.venc_get_jpeg_param(channel_id) param['qfactor'] = 100 ret = acl.himpi.venc_set_jpeg_param(channel_id, param) # 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_VGS} 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() |