如何提升TCP套接字传输2K视频的速度?含YOLO处理优化
优化TCP视频传输+YOLO处理性能方案
当前核心瓶颈
- 2K帧通过
pickle序列化后体积过大(单帧约10MB),TCP传输带宽占用高、延迟显著 - YOLO属于计算密集型任务,单线程下接收帧与处理帧串行执行,进一步放大卡顿问题
以下是针对性的优化方案:
1. 替换帧序列化方式:用图像编码替代Pickle
pickle会完整保存像素数组,而JPEG编码可将2K帧压缩至几百KB,直接减少90%以上的传输数据量。
服务器端修改
import socket, cv2, struct, imutils # Socket Create server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) host_name = socket.gethostname() host_ip ='192.168.xx.xxx' print('HOST IP:',host_ip) port = 1234 socket_address = (host_ip,port) source = 'video.mp4' # Socket Bind server_socket.bind(socket_address) # Socket Listen server_socket.listen(5) print("LISTENING AT:",socket_address) # Socket Accept while True: client_socket,addr = server_socket.accept() print('GOT CONNECTION FROM:',addr) if client_socket: vid = cv2.VideoCapture(source) while(vid.isOpened()): ret, frame = vid.read() if not ret: break # 压缩帧为JPEG,quality参数0-100,值越小压缩率越高 _, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 70]) # 打包数据长度+压缩帧 message = struct.pack("Q", len(buffer)) + buffer.tobytes() client_socket.sendall(message) cv2.imshow('TRANSMITTING VIDEO', frame) key = cv2.waitKey(1) & 0xFF if key == ord('q'): client_socket.close() break vid.release() cv2.destroyAllWindows()
客户端修改
import socket,cv2, struct, numpy as np # create socket client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) host_ip = '192.168.xx.xxx' port = 1234 client_socket.connect((host_ip,port)) data = b"" payload_size = struct.calcsize("Q") while True: while len(data) < payload_size: packet = client_socket.recv(4096) if not packet: break data += packet packed_msg_size = data[:payload_size] data = data[payload_size:] msg_size = struct.unpack("Q", packed_msg_size)[0] while len(data) < msg_size: data += client_socket.recv(4096) frame_data = data[:msg_size] data = data[msg_size:] # 解码JPEG帧 frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR) # 后续加入YOLO处理(建议放在单独线程) # runYolo(frame) cv2.imshow("RECEIVING VIDEO",frame) key = cv2.waitKey(1) & 0xFF if key == ord('q'): break client_socket.close() cv2.destroyAllWindows()
2. 分离接收与YOLO处理:多线程并行解耦
YOLO单帧处理耗时较长,单线程会阻塞帧接收。用队列+多线程实现"接收-处理"并行:
客户端多线程版本
import socket,cv2, struct, numpy as np from threading import Thread from queue import Queue # 帧队列,最大缓存10帧避免内存溢出 frame_queue = Queue(maxsize=10) def receive_frames(): client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) host_ip = '192.168.xx.xxx' port = 1234 client_socket.connect((host_ip,port)) data = b"" payload_size = struct.calcsize("Q") while True: while len(data) < payload_size: packet = client_socket.recv(4096) if not packet: frame_queue.put(None) # 发送结束信号 client_socket.close() return data += packet packed_msg_size = data[:payload_size] data = data[payload_size:] msg_size = struct.unpack("Q", packed_msg_size)[0] while len(data) < msg_size: data += client_socket.recv(4096) frame_data = data[:msg_size] data = data[msg_size:] frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR) # 队列未满则放入,满了丢弃旧帧避免阻塞接收 if not frame_queue.full(): frame_queue.put(frame) def process_frames(): # 初始化YOLO模型(仅初始化一次) def runYolo(frame): # 替换为你的YOLO检测逻辑 processed_frame = frame return processed_frame while True: frame = frame_queue.get() if frame is None: # 接收线程结束 break # 处理帧 processed_frame = runYolo(frame) # 显示处理结果 cv2.imshow("PROCESSED VIDEO", processed_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows() # 启动双线程 recv_thread = Thread(target=receive_frames) process_thread = Thread(target=process_frames) recv_thread.start() process_thread.start() recv_thread.join() process_thread.join()
3. YOLO模型与推理优化
这是降低处理延迟的核心:
- 用轻量化模型:选择YOLOv5n、YOLOv8n等nano版本,推理速度比大模型快5-10倍
- 启用GPU加速:有NVIDIA显卡时,用PyTorch CUDA或OpenCV DNN的CUDA后端,推理速度提升数倍
- 降低输入分辨率:将2K帧resize到640×640再喂模型,大幅提升推理速度(精度可接受的前提下)
- 减少推理频率:每2-3帧检测一次,用前一次结果补帧,平衡速度与精度
4. 可选:改用UDP传输(牺牲可靠性换实时性)
TCP的重传机制会在丢包时拖慢速度,视频对少量丢包容忍度高,可尝试UDP:
- 服务器端用
socket.SOCK_DGRAM替代SOCK_STREAM,直接发送压缩帧 - 客户端绑定端口接收,无需建立连接
- 注意:UDP可能丢帧,适合实时性优先的场景
内容的提问来源于stack exchange,提问作者Yadu S
相关产品推荐
相关产品推荐

