hex、json、cbor、protobuf编码压缩率对比

在嵌入式行业,有大量的工程师在使用header type value crc这种十六进制的编码协议。

这种协议的好处是数据紧凑,数据量小。 坏处是可读性差,维护困难。

也有很多嵌入式工程师开始使用cjson,好处不需要多讲。 坏处是json序列化之后的数据长度,是十六进制t l v格式的3~4倍。

常用的数据编码有以下:
xml: 带位置关系的文本格式 适合网页渲染
json: 3.5倍长度 适用于app 与 服务器之间、服务器与服务器之间通讯
cbor: 2倍长度 既具备json的可读性,又具备二进制的紧凑性。区块链里面有用这种数据编码,例如filecoin
protobuf: 1.2倍长度 适用于网络游戏,坏处是强耦合性(app和服务器需要随时保持同步,同过.proto文件存储位置元数据)

xml 是通过tag标签匹配。

json解析时是通过特殊符号," ’ { } [] 的匹配,来确定key:value,是位置无关编码,可读性最强,但是特殊符合过多。

cbor是通过传入是头、数据类型、key,没有特殊字符。

protobuf是把位置数据写入文件字符串,编解码时按照字符串文件来序列化/反序列化到变量。

下面以cbor为例子,实测与json、hex编码后数据压缩比例。
python demo代码:

import cbor2
import json
import re

# 原始数据
data = {
    'tag': 'gps',
    'name': 'king',
    'age': 99999,
    'addr': '上海',
    'lon': 23.12335555,
    'lat': 118.15155615,
    'timestamp': 32158520,
    'lang': ['Python', '爬虫', 'Golang', 'ssh', 'impl']
}

# JSON编码
json_data = json.dumps(data, ensure_ascii=False)
json_bytes = json_data.encode('utf-8')

# CBOR编码
cbor_bytes = cbor2.dumps(data)

# 提取JSON值部分并拼接为字节(修复版)
def extract_json_values(json_str):
    """仅提取JSON字符串中的值部分(不包括键名)"""
    # 匹配键值对的正则表达式
    key_value_pattern = re.compile(r'"([^"]+)":\s*("[^"]*"|\d+\.?\d*|true|false|null)')
    # 匹配数组的正则表达式
    array_pattern = re.compile(r'\[([^\]]*)\]')
    # 匹配数组元素的正则表达式
    array_item_pattern = re.compile(r'"([^"]*)"|\d+\.?\d*|true|false|null')
    
    byte_parts = []
    
    # 处理键值对
    for match in key_value_pattern.finditer(json_str):
        value = match.group(2)
        if value.startswith('"') and value.endswith('"'):
            # 字符串值
            byte_parts.append(value[1:-1].encode('utf-8'))
        else:
            # 数字、布尔值、null
            byte_parts.append(value.encode('utf-8'))
    
    # 处理数组
    for array_match in array_pattern.finditer(json_str):
        array_content = array_match.group(1)
        for item_match in array_item_pattern.finditer(array_content):
            item = item_match.group(0)
            if item.startswith('"') and item.endswith('"'):
                byte_parts.append(item[1:-1].encode('utf-8'))
            else:
                byte_parts.append(item.encode('utf-8'))
    
    return b''.join(byte_parts)

# 提取纯JSON值(不包括键名)
json_values_bytes = extract_json_values(json_data)

# 格式化十六进制输出(带地址偏移)
def format_hex(bytes_data, bytes_per_line=16, group_size=2):
    hex_str = bytes_data.hex(' ')
    formatted = []
    for i in range(0, len(hex_str), bytes_per_line * (group_size + 1)):
        line = hex_str[i:i + bytes_per_line * (group_size + 1)]
        addr = f"{i//(group_size + 1):08x}"
        formatted.append(f"{addr}: {line}")
    return "\n".join(formatted)

# 对比JSON值HEX与CBOR HEX(修复版)
def compare_hex(json_hex, cbor_hex, max_compare=100):
    def extract_pure_hex(hex_str):
        return re.sub(r'^.*?: | ', '', hex_str, flags=re.MULTILINE).replace(' ', '')
    
    json_pure_hex = extract_pure_hex(json_hex)
    cbor_pure_hex = extract_pure_hex(cbor_hex)
    
    max_length = min(len(json_pure_hex) // 2, len(cbor_pure_hex) // 2, max_compare)
    json_bytes = bytes.fromhex(json_pure_hex[:max_length*2])
    cbor_bytes = bytes.fromhex(cbor_pure_hex[:max_length*2])
    
    result = []
    result.append("| 位置 | JSON值HEX              | CBOR HEX            | 对比结果              |")
    result.append("|" + "-" * 76 + "|")
    for i in range(max_length):
        jb = json_bytes[i] if i < len(json_bytes) else None
        cb = cbor_bytes[i] if i < len(cbor_bytes) else None
        jb_hex = f"0x{jb:02x}" if jb is not None else "      "
        cb_hex = f"0x{cb:02x}" if cb is not None else "      "
        remark = "相同字节" if jb == cb and jb is not None else "不同字节" if jb is not None and cb is not None else "JSON值结束" if jb is None else "CBOR结束"
        result.append(f"| {i:4d} | {jb_hex:18} | {cb_hex:18} | {remark:20} |")
    return "\n".join(result)

# 输出结果
print("原始数据:\n", data)
print("\n--- JSON编码 ---")
print(f"JSON字符串: {json_data}")
print(f"JSON字节长度: {len(json_bytes)} 字节")
print("JSON十六进制:\n", format_hex(json_bytes))
print("\n--- CBOR编码 ---")
print(f"CBOR字节长度: {len(cbor_bytes)} 字节")
print("CBOR十六进制:\n", format_hex(cbor_bytes))
print("\n--- JSON值字节拼接后的HEX ---")
print(f"JSON值hex长度: {len(json_values_bytes)} 字节")
print("JSON值拼接HEX:\n", format_hex(json_values_bytes))
print(f'len(json) / len(hex) = {len(json_bytes) / len(json_values_bytes)}倍')
print(f'len(json) / len(cbor) = {len(json_bytes) / len(cbor_bytes)}倍')
print(f'len(cbor) / len(hex) = {len(cbor_bytes) / len(json_values_bytes)}倍')

实测:

root@iZwz99zhkxxl5h6ecbm2xwZ:~# python3 cbor.py 
原始数据:
 {'tag': 'gps', 'name': 'king', 'age': 99999, 'addr': '上海', 'lon': 23.12335555, 'lat': 118.15155615, 'timestamp': 32158520, 'lang': ['Python', '爬虫', 'Golang', 'ssh', 'impl']}

--- JSON编码 ---
JSON字符串: {"tag": "gps", "name": "king", "age": 99999, "addr": "上海", "lon": 23.12335555, "lat": 118.15155615, "timestamp": 32158520, "lang": ["Python", "爬虫", "Golang", "ssh", "impl"]}
JSON字节长度: 181 字节
JSON十六进制:
 00000000: 7b 22 74 61 67 22 3a 20 22 67 70 73 22 2c 20 22 
00000010: 6e 61 6d 65 22 3a 20 22 6b 69 6e 67 22 2c 20 22 
00000020: 61 67 65 22 3a 20 39 39 39 39 39 2c 20 22 61 64 
00000030: 64 72 22 3a 20 22 e4 b8 8a e6 b5 b7 22 2c 20 22 
00000040: 6c 6f 6e 22 3a 20 32 33 2e 31 32 33 33 35 35 35 
00000050: 35 2c 20 22 6c 61 74 22 3a 20 31 31 38 2e 31 35 
00000060: 31 35 35 36 31 35 2c 20 22 74 69 6d 65 73 74 61 
00000070: 6d 70 22 3a 20 33 32 31 35 38 35 32 30 2c 20 22 
00000080: 6c 61 6e 67 22 3a 20 5b 22 50 79 74 68 6f 6e 22 
00000090: 2c 20 22 e7 88 ac e8 99 ab 22 2c 20 22 47 6f 6c 
000000a0: 61 6e 67 22 2c 20 22 73 73 68 22 2c 20 22 69 6d 
000000b0: 70 6c 22 5d 7d

--- CBOR编码 ---
CBOR字节长度: 117 字节
CBOR十六进制:
 00000000: a8 63 74 61 67 63 67 70 73 64 6e 61 6d 65 64 6b 
00000010: 69 6e 67 63 61 67 65 1a 00 01 86 9f 64 61 64 64 
00000020: 72 66 e4 b8 8a e6 b5 b7 63 6c 6f 6e fb 40 37 1f 
00000030: 94 3a b5 07 b4 63 6c 61 74 fb 40 5d 89 b3 18 90 
00000040: f0 7e 69 74 69 6d 65 73 74 61 6d 70 1a 01 ea b3 
00000050: 38 64 6c 61 6e 67 85 66 50 79 74 68 6f 6e 66 e7 
00000060: 88 ac e8 99 ab 66 47 6f 6c 61 6e 67 63 73 73 68 
00000070: 64 69 6d 70 6c

--- JSON值字节拼接后的HEX ---
JSON值hex长度: 74 字节
JSON值拼接HEX:
 00000000: 67 70 73 6b 69 6e 67 39 39 39 39 39 e4 b8 8a e6 
00000010: b5 b7 32 33 2e 31 32 33 33 35 35 35 35 31 31 38 
00000020: 2e 31 35 31 35 35 36 31 35 33 32 31 35 38 35 32 
00000030: 30 50 79 74 68 6f 6e e7 88 ac e8 99 ab 47 6f 6c 
00000040: 61 6e 67 73 73 68 69 6d 70 6c
len(json) / len(hex) = 2.445945945945946倍
len(json) / len(cbor) = 1.547008547008547倍
len(cbor) / len(hex) = 1.5810810810810811倍
root@iZwz99zhkxxl5h6ecbm2xwZ:~#
def run(self): cap = cv2.VideoCapture(self.capTarget) cap.set(cv2.CAP_PROP_FPS, 30) if self.save_video: fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) # 启动识别线程 thread = threading.Thread(target=self.recognition_thread, daemon=True) print(f'[主线程] 启动子线程 | 线程ID: {thread.ident} | 名称: {thread.name} | 存活状态: {thread.is_alive()}') thread.start() while True: ret, frame = cap.read() if not ret: break # 更新识别线程 current_time = time.time() # 时间未到1秒则等待 if current_time - self.last_process_time >= 0.5 and self.frame_queue.empty(): self.last_process_time = time.time() self.frame_queue.put(frame.copy(), block=False) # 绘制人脸框和轨迹 for name, (left, top, right, bottom), center in self.current_faces: # 添加记录 if name != "未知": self.record_list.append({ "name": name, "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), }) cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) # 绘制姓名 # cv2.putText(frame, name, (left + 6, bottom - 6), # cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) # 绘制中文姓名 # 转换BGR到RGB(PIL使用RGB) frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(frame_pil) # 加载中文字体(需提供字体文件路径) font = ImageFont.truetype("simhei.ttf", 20) # 例如使用黑体 # 计算文字位置(调整坐标) text_position = (left + 6, top + 6) # 建议顶部显示而非底部 # 绘制中文 draw.text(text_position, name, font=font, fill=(255, 255, 255)) # 转换回OpenCV格式 frame = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR) # 图片保存逻辑 if self.save_img and len(self.current_faces) > 0: save_flag = False update_names = [] check_time = time.time() # 检查所有人脸状态 for face in self.current_faces: name = face[0] if name == "未知": save_flag = True break else: last_time = self.face_last_save_time.get(name, 0) if check_time - last_time >= 60: save_flag = True update_names.append(name) # 更新已知人脸时间戳 for name in update_names: self.face_last_save_time[name] = check_time # 执行保存操作 if save_flag: os.makedirs(self.save_dir, exist_ok=True) filename = f"{datetime.now().strftime('%Y%m%d%H%M')}_{uuid.uuid4().hex[:3]}.jpg" cv2.imwrite(os.path.join(self.save_dir, filename), frame) cv2.imshow('Face Tracking', frame) if self.save_video: out.write(frame) if cv2.waitKey(1) & 0xFF == ord('q'): self.running = False saveRecordsNow(self.record_list) break if self.save_video: out.release() cap.release() cv2.destroyAllWindows()。如果我想要吧多线程改为并行,把识别功能单独拆猪来独立运行,然后当前程序调用识别服务功能并返回输入该如何实现
03-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐墨123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值