如何用OpenCV读取树莓派客户端的低延迟视频流并集成到PyQt?
解决方案:用OpenCV接收树莓派Socket视频流并整合PyQt GUI
一、问题背景
已实现树莓派(客户端)通过Socket向PC(服务端)传输H264格式摄像头视频流,原方案用VLC播放,现需替换为OpenCV解析视频帧,并实现PyQt自定义GUI播放界面,且保持树莓派作为客户端的架构。
二、PC端用OpenCV接收并解析H264流
由于OpenCV无法直接解码H264裸流,我们通过ffmpeg作为中间层,将Socket接收的H264字节流转为OpenCV可处理的原始视频帧格式。以下是修改后的PC服务端代码:
import socket import subprocess import cv2 import numpy as np # 初始化Socket服务端 server_socket = socket.socket() server_socket.bind(('0.0.0.0', 8000)) server_socket.listen(0) connection = server_socket.accept()[0].makefile('rb') try: # 启动ffmpeg进程,将H264裸流转为rawvideo(BGR格式) ffmpeg_cmd = [ 'ffmpeg', '-i', '-', # 从标准输入读取 '-f', 'rawvideo', '-pix_fmt', 'bgr24', '-vcodec', 'rawvideo', '-' # 输出到标准输出 ] ffmpeg_proc = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=10**8) # 帧分辨率(需和树莓派客户端设置一致) width, height = 640, 480 frame_size = width * height * 3 # BGR格式每个帧的字节数 while True: # 从ffmpeg输出读取一帧数据 frame_data = ffmpeg_proc.stdout.read(frame_size) if not frame_data: break # 将字节数据转为OpenCV格式的帧 frame = np.frombuffer(frame_data, dtype=np.uint8).reshape((height, width, 3)) # 可添加自定义帧处理逻辑(如人脸识别、标注等) cv2.imshow('Raspberry Pi Video Stream', frame) # 按q键退出 if cv2.waitKey(1) & 0xFF == ord('q'): break finally: # 资源释放 connection.close() server_socket.close() ffmpeg_proc.terminate() cv2.destroyAllWindows()
注意事项
- 确保PC端已安装依赖:
pip install opencv-python sudo apt install ffmpeg # Ubuntu/Debian系 # Windows需手动下载ffmpeg并添加到系统环境变量 - 树莓派客户端代码无需修改,保持原有H264流发送逻辑即可。
三、PyQt5 GUI整合视频播放
为实现自定义界面,用PyQt5创建窗口,结合OpenCV和QTimer实现实时帧更新,避免UI线程阻塞。以下是完整的PyQt版服务端代码:
import socket import subprocess import cv2 import numpy as np from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import QTimer, Qt import sys class VideoStreamWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("树莓派视频流播放器") self.setGeometry(100, 100, 640, 480) # 创建视频显示标签 self.video_label = QLabel() self.video_label.setAlignment(Qt.AlignCenter) # 布局设置 layout = QVBoxLayout() layout.addWidget(self.video_label) central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) # 初始化Socket和ffmpeg进程 self.server_socket = socket.socket() self.server_socket.bind(('0.0.0.0', 8000)) self.server_socket.listen(0) self.connection = self.server_socket.accept()[0].makefile('rb') self.ffmpeg_cmd = [ 'ffmpeg', '-i', '-', '-f', 'rawvideo', '-pix_fmt', 'bgr24', '-vcodec', 'rawvideo', '-' ] self.ffmpeg_proc = subprocess.Popen( self.ffmpeg_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=10**8 ) self.width, self.height = 640, 480 self.frame_size = self.width * self.height * 3 # 定时读取帧并更新UI self.timer = QTimer() self.timer.timeout.connect(self.update_frame) self.timer.start(30) # 约30fps,可根据实际帧率调整 def update_frame(self): # 读取一帧数据 frame_data = self.ffmpeg_proc.stdout.read(self.frame_size) if not frame_data: self.timer.stop() return # 转为OpenCV帧并转换颜色空间(OpenCV是BGR,PyQt是RGB) frame = np.frombuffer(frame_data, dtype=np.uint8).reshape((self.height, self.width, 3)) frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 转为QPixmap并显示 h, w, ch = frame_rgb.shape bytes_per_line = ch * w q_image = QImage(frame_rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(q_image).scaled( self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation )) def closeEvent(self, event): # 窗口关闭时释放资源 self.connection.close() self.server_socket.close() self.ffmpeg_proc.terminate() self.timer.stop() event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = VideoStreamWindow() window.show() sys.exit(app.exec_())
功能说明
- 窗口可自由调整大小,视频帧自动保持比例缩放
- 关闭窗口时自动释放Socket、ffmpeg等资源
- 可在
update_frame方法中添加自定义帧处理逻辑(如实时检测、标注)
依赖安装
pip install opencv-python pyqt5
内容的提问来源于stack exchange,提问作者Dinh Trung Che
相关产品推荐
相关产品推荐

