如何基于Python imageio实现嵌入式Linux与PC间TCP低延迟视频传输?
问题
在嵌入式Linux板(如树莓派)和PC上分别运行Python程序,需求如下:
- 用imageio库从Linux板的Pi Camera捕获帧
- 通过TCP连接将帧发送到PC,PC端将帧转为numpy数组
- 追求最低延迟
此前用OpenCV将帧压缩为低质量JPEG传输,本地网络延迟0.25-0.5s,但不想依赖OpenCV;目前用pickle直接序列化帧传输,延迟超过1s,效率不足,现有代码如下:
Linux板端代码
for idx, frame in enumerate(iio.imiter("<video0>", size=(resolution[0], resolution[1]), fps=fps)): data = pkl.dumps(frame, 0) framelen = len(data) connection.write(struct.pack('<L', framelen)) connection.flush() connection.write(data)
PC端代码
while True: framelen = struct.unpack('<L', connection.read(struct.calcsize('<L')))[0] if not framelen: break data = connection.read(framelen) # raw undecoded pickle bytestring frame = pkl.loads(data, fix_imports=True, encoding="bytes")
想知道有没有更高效的实现方式,比如借助imageio编码压缩后再通过Socket发送、PC端解码?
优化方案
核心思路是用轻量级图像压缩替代pickle序列化,imageio本身支持JPEG/PNG等编码,结合高效的TCP传输逻辑,能大幅降低延迟:
1. 用imageio直接编码为JPEG(无OpenCV依赖)
JPEG压缩能大幅减小传输数据量,imageio的编码速度快,且无需额外依赖。
Linux板端(发送端)代码
import imageio.v3 as iio import struct import socket # 初始化TCP连接 HOST = 'PC的IP地址' PORT = 12345 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 关闭Nagle算法,减少小数据包延迟 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.connect((HOST, PORT)) connection = sock.makefile('wb') resolution = (640, 480) fps = 30 try: # 捕获摄像头帧,直接编码为JPEG(质量可调,越低延迟越低) for frame in iio.imiter("<video0>", size=resolution, fps=fps): # 编码为JPEG字节流,质量设为30(平衡画质和延迟) jpeg_data = iio.imwrite("<bytes>", frame, format="jpeg", quality=30) # 发送帧长度 + JPEG数据 connection.write(struct.pack('<L', len(jpeg_data))) connection.write(jpeg_data) connection.flush() # 强制发送缓冲区数据,避免攒帧延迟 finally: connection.close() sock.close()
PC端(接收端)代码
import imageio.v3 as iio import struct import socket import numpy as np HOST = '0.0.0.0' # 监听所有地址 PORT = 12345 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((HOST, PORT)) sock.listen(1) print("等待连接...") conn, addr = sock.accept() connection = conn.makefile('rb') try: while True: # 读取帧长度 len_data = connection.read(struct.calcsize('<L')) if not len_data: break framelen = struct.unpack('<L', len_data)[0] # 读取JPEG数据并解码为numpy数组 jpeg_data = connection.read(framelen) frame = iio.imread(jpeg_data, format="jpeg") # frame为numpy数组,可直接处理 print(f"接收帧形状: {frame.shape}") finally: connection.close() conn.close() sock.close()
2. 进一步压低延迟的技巧
- 降低分辨率/帧率:如果对画质要求不高,用320x240+15fps能进一步减少数据量
- 使用TCP_NODELAY:如代码中所示,关闭Nagle算法避免小数据包等待合并
- 优先单帧即时发送:不要攒帧,每捕获一帧就立即编码发送,保证实时性
- 启用硬件加速编码:树莓派上imageio默认会优先调用
libjpeg-turbo后端,可加速JPEG编码
为什么比pickle方案高效?
pickle序列化的是原始numpy数组(比如640x480x3的帧,原始大小约900KB),而JPEG压缩后(质量30)仅10-20KB,数据量减少90%以上,传输时间大幅缩短,延迟自然降低。
内容的提问来源于stack exchange,提问作者Yann Bouteiller
相关产品推荐
相关产品推荐

