文档
注册

JPEGE图片编码

JPEGE(JPEG Encoder)负责完成图像编码功能,将YUV格式图片编码成.jpg图片。关于JPEGE功能的详细介绍请参见JPEGE功能及约束说明

本节介绍JPEGE图片编码的接口调用流程,同时配合示例代码辅助理解该接口调用流程。

Atlas 200/300/500 推理产品上,当前版本不支持该功能。

Atlas 训练系列产品上,当前版本不支持该功能。

接口调用流程

开发应用时,如果涉及将YUV格式图片编码成JPEG压缩格式的图片文件,则应用程序中必须包含编码的代码逻辑,关于编码的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的流程说明。

图1 接口调用流程
当前系统支持将YUV格式图片编码成JPEG压缩格式的图片文件,关键接口的说明如下:
  1. 调用acl.himpi.sys_init接口进行媒体数据处理系统初始化。
  2. 调用acl.himpi.venc_create_chn函数创建完通道。

    成功创建通道之后,您可以根据实际需求设置编码的高级参数,例如场景模式、码流控制器的高级参数等,请参见acl.himpi.venc_set_jpeg_param~acl.himpi.venc_compact_jpeg_tables章节中的接口说明。

  3. 调用acl.himpi.venc_get_fd将通道ID转换为一个文件句柄。
  4. 调用acl.himpi.sys_create_epoll函数创建DVPP epoll实例。
  5. 调用acl.himpi.sys_ctl_epoll函数将编码通道的文件句柄添加到epoll实例中,由epoll实例处理。

    select或者poll方式,不需要执行该步骤。

  6. 调用acl.himpi.venc_start_chn函数通知通道准备开始编码。
  7. 调用acl.himpi.dvpp_malloc接口申请存放Device上输入数据的内存。
  8. 启动一个用户态线程,调用acl.himpi.sys_wait_epoll函数等待编码完成。
  9. 之后用户就可以调用acl.himpi.venc_send_frame函数发送待编码的码流。
  10. 一旦编码完成,acl.himpi.sys_wait_epoll函数或select函数或poll函数就会返回,用户就可以调用acl.himpi.venc_query_status接口查询编码状态,再调用acl.himpi.venc_get_stream函数获取编码结果。
  11. 用户需要注意的是,编码结果数据使用完成之后,需要及时调用acl.himpi.venc_release_stream函数释放buffer。否则会因编码buffer用完导致后续编码无法进行。
  12. 调用acl.himpi.dvpp_free接口释放输入内存。
  13. 当用户不需发送图像到目的通道继续编码时,需要调用acl.himpi.venc_stop_chn函数通知该通道不再接收新的输入图片。
  14. 调用acl.himpi.sys_ctl_epoll函数从epoll实例中删除编码通道的文件句柄。
  15. 当用户完成所有编码之后,需要调用acl.himpi.venc_destroy_chn释放编码通道以及内部内存资源。
  16. 调用acl.himpi.sys_close_epoll函数销毁DVPP epoll实例。
  17. 调用acl.himpi.sys_exit接口进行媒体数据处理系统去初始化。
支持DVPP内部管理输出内存,或用户自行管理输出内存两种方式:
  • 不需要用户管理、由DVPP内部管理输出内存时,调用acl.himpi.venc_send_frame接口发送原始图像进行图像编码。

    在调用acl.himpi.venc_create_chn接口创建通道时,必须正确设置hi_venc_chn_attr["venc_attr"]["buf_size"]参数值(参数描述请参见hi_venc_attr)。

    该方式下,相比由用户管理内存,输出结果数据的JPEG头中不存在COM注释字段,数据长度会短一点,但需要用户从DVPP返回的内存中拷贝输出结果数据到指定内存。

  • 由用户自行管理输出内存、管理内存的生命周期,调用acl.himpi.venc_send_jpege_frame接口发送原始图像进行图像编码。

    在调用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()
搜索结果
找到“0”个结果

当前产品无相关内容

未找到相关内容,请尝试其他搜索词