树莓派PiCamera基于Python向同网Python服务器无线推流技术问询
我来给你一套针对性的解决方案,既能解决OpenCV帧率低的问题,又能实现无线推流,完全符合你不用VNC的要求。核心思路是利用树莓派PiCamera的硬件编码能力(避免OpenCV软件解码的性能损耗),结合Socket网络通信实现低延迟、高帧率的视频传输。
下面分两种可行方案,你可以根据需求选择:
方案1:Socket + MJPEG 实时推流(易调试、高帧率)
这个方案用PiCamera的视频端口(硬件加速)捕获帧,通过JPEG压缩后用Socket传输,帧率轻松达到30fps,延迟极低。
服务器端(接收视频的电脑)
先安装依赖:
pip install opencv-python numpy
然后运行以下Python代码作为服务器:
import socket import cv2 import numpy as np # 配置服务器监听地址和端口 HOST = '0.0.0.0' # 监听本机所有网络接口 PORT = 8000 # 初始化Socket服务器 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(1) print(f"等待树莓派连接... 当前监听端口: {PORT}") # 接受树莓派的连接请求 client_socket, addr = server_socket.accept() print(f"已成功连接设备: {addr}") # 循环接收并解码视频帧 data_buffer = b'' frame_size_bytes = 4 # 用4字节存储每一帧的大小 while True: # 先接收帧的大小信息 while len(data_buffer) < frame_size_bytes: data_buffer += client_socket.recv(4096) # 解析帧大小 packed_size = data_buffer[:frame_size_bytes] data_buffer = data_buffer[frame_size_bytes:] frame_size = int.from_bytes(packed_size, byteorder='big') # 接收完整的帧数据 while len(data_buffer) < frame_size: data_buffer += client_socket.recv(4096) frame_data = data_buffer[:frame_size] data_buffer = data_buffer[frame_size:] # 解码JPEG帧并显示 frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR) cv2.imshow('Raspberry Pi Video Stream', frame) # 按Q键退出程序 if cv2.waitKey(1) & 0xFF == ord('q'): break # 清理资源 client_socket.close() server_socket.close() cv2.destroyAllWindows()
客户端(树莓派)
先安装依赖(注意:如果是树莓派4B及以上,推荐用picamera2,这里先给传统picamera的代码):
pip install picamera[array] opencv-python
替换代码中的HOST为你的电脑在局域网内的IP地址,然后运行:
import socket import cv2 from picamera import PiCamera from picamera.array import PiRGBArray import time # 替换为你的电脑局域网IP HOST = '192.168.1.100' PORT = 8000 # 初始化PiCamera(启用硬件加速) camera = PiCamera() camera.resolution = (640, 480) # 可根据需求调整,分辨率越低帧率越高 camera.framerate = 30 raw_capture = PiRGBArray(camera, size=(640, 480)) time.sleep(0.1) # 等待摄像头预热完成 # 建立Socket连接 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((HOST, PORT)) # 循环捕获并发送视频帧 for frame in camera.capture_continuous(raw_capture, format='bgr', use_video_port=True): # 获取当前帧的数组数据 image = frame.array # 编码为JPEG(压缩率可调,80是平衡画质和传输速度的取值) _, encoded_frame = cv2.imencode('.jpg', image, [cv2.IMWRITE_JPEG_QUALITY, 80]) # 先发送帧的大小,再发送帧数据 frame_size = len(encoded_frame).to_bytes(4, byteorder='big') client_socket.sendall(frame_size + encoded_frame.tobytes()) # 清空缓冲区,准备下一帧 raw_capture.truncate(0) # 按Ctrl+C终止程序 try: pass except KeyboardInterrupt: break # 清理资源 client_socket.close() camera.close()
方案2:H.264 硬件编码推流(更低带宽占用)
如果你的局域网带宽有限,或者需要更高的帧率,可以用PiCamera的H.264硬件编码(比MJPEG压缩率更高),进一步降低网络负载。
服务器端(电脑)
同样安装opencv-python,代码如下:
import socket import cv2 HOST = '0.0.0.0' PORT = 8000 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(1) client_socket, addr = server_socket.accept() print(f"已连接设备: {addr}") # 将Socket流转换为OpenCV可读取的视频流 stream = client_socket.makefile('rb') cap = cv2.VideoCapture(stream) while cap.isOpened(): ret, frame = cap.read() if not ret: break cv2.imshow('H.264 Video Stream', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # 清理资源 cap.release() cv2.destroyAllWindows() client_socket.close() server_socket.close()
客户端(树莓派)
无需额外安装依赖(picamera自带H.264编码),代码更简洁:
import socket from picamera import PiCamera import time HOST = '192.168.1.100' # 替换为你的电脑IP PORT = 8000 camera = PiCamera() camera.resolution = (640, 480) camera.framerate = 30 # 直接将H.264流发送到服务器 camera.start_recording( socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((HOST, PORT)), format='h264' ) # 可以设置录制时长,或者用循环保持连接 try: while True: time.sleep(1) except KeyboardInterrupt: camera.stop_recording()
为什么你之前用OpenCV帧率低?
大概率是因为你没有启用PiCamera的use_video_port=True参数,默认用的是图片捕获端口(软件处理),帧率被限制在几fps;另外如果直接传输未压缩的帧,网络带宽不足也会导致帧率下降。上面的方案都用到了硬件加速和帧压缩,完美解决这个问题。
优化小技巧
- 调整分辨率:如果需要更高帧率,可将分辨率降到
(320, 240),帧率能达到60fps - 调整JPEG质量:方案1中的
cv2.IMWRITE_JPEG_QUALITY可以设为60-90,数值越低压缩率越高,传输越快 - 端口选择:如果8000端口被占用,可替换为其他未被使用的端口(比如8080)
内容的提问来源于stack exchange,提问作者JeyPay




