我一直在研究通过 wifi 从 RaspberryPi 传输图像到 Mac 的所有选项,但接收器可以是任何东西。
我决定以 YUV420p 格式传输数据,因为这需要最少的网络带宽 - HALF 是 RGB 数据所需的。我尝试禁用降噪以增加 ISO 和我能想象到的一切以使其运行得更快,并使用我以 12 英镑(15 美元)从亚马逊购买的非常酷的小型逻辑分析仪和sigrok 逻辑分析仪软件分析了整个事情。
这是我如何获得每秒 36 帧的 320x240 像素:
#!/usr/bin/python3
import io
import socket
import struct
import time
import picamera
import RPi.GPIO as GPIO
# Enable GPIO pins as output for Logic Analyser probe-based debugging
GPIO.setmode(GPIO.BCM)
GPIO.setup(20, GPIO.OUT)
GPIO.setup(21, GPIO.OUT)
GPIO.setup(22, GPIO.OUT)
GPIO.setup(23, GPIO.OUT)
WIDTH=320
HEIGHT=240
BUFFERSIZE=WIDTH*HEIGHT*3//2
PORT=8000
SERVER='192.168.0.8'
client_socket = socket.socket()
client_socket.connect((SERVER, PORT))
connection = client_socket.makefile('wb')
try:
with picamera.PiCamera() as camera:
camera.resolution = (WIDTH, HEIGHT)
camera.framerate = 60
time.sleep(2)
start = time.time()
count = 0
stream = io.BytesIO()
# Use the video-port for captures...
while True:
GPIO.output(20,GPIO.HIGH)
frame=next(camera.capture_continuous(stream, format="yuv", use_video_port=True))
GPIO.output(20,GPIO.LOW)
stream.seek(0)
GPIO.output(22,GPIO.HIGH)
connection.write(stream.read())
GPIO.output(22,GPIO.LOW)
count += 1
if time.time() - start > 15:
break
stream.seek(0)
stream.truncate()
#connection.write(struct.pack('<L', 0))
finally:
connection.close()
client_socket.close()
finish = time.time()
print('Sent %d images in %d seconds at %.2ffps' % (
count, finish-start, count / (finish-start)))
GPIO.cleanup()
在 Mac 上,作为服务器运行并显示:
#!/usr/local/bin/python3
import io
import socket
import struct
import numpy as np
import cv2
WIDTH=320
HEIGHT=240
PORT=8000
BUFFERSIZE=WIDTH*HEIGHT*3//2
print("Running on port {}: width={}, height={}".format(PORT,WIDTH,HEIGHT))
# Start a socket listening for connections on all interfaces
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', PORT))
server_socket.listen(0)
frame=0
# Accept a single connection and make a file-like object out of it
connection = server_socket.accept()[0].makefile('rb')
try:
while True:
# Construct a stream and fill with image data from network
image_stream = io.BytesIO()
image_stream.write(connection.read(BUFFERSIZE))
# Rewind the stream, and process
image_stream.seek(0)
img_str=image_stream.read()
nparr=np.fromstring(img_str,np.uint8)
# Data layout is here https://en.wikipedia.org/wiki/YUV#/media/File:Yuv420.svg
Y=nparr[0:WIDTH*HEIGHT].reshape(HEIGHT,WIDTH)
# ssU = sub-sampled U channel, ssV = sub-sampled V channel
ssU=nparr[WIDTH*HEIGHT:WIDTH*HEIGHT*5//4].reshape(HEIGHT//2,WIDTH//2)
ssV=nparr[WIDTH*HEIGHT*5//4:].reshape(HEIGHT//2,WIDTH//2)
# Up-sample the U and V channels to full size
U=cv2.resize(ssU,(WIDTH,HEIGHT),cv2.INTER_LINEAR)
V=cv2.resize(ssV,(WIDTH,HEIGHT),cv2.INTER_LINEAR)
# Combine the YUV into single image and convert to BGR
oc_YUV=cv2.merge((Y,U,V))
oc_BGR=cv2.cvtColor(oc_YUV,cv2.COLOR_YUV2BGR)
# That's actually it, but I upscale for better viewing on screen
tmp=cv2.resize(oc_BGR,(WIDTH*2,HEIGHT*2),cv2.INTER_LINEAR)
cv2.imshow('image',tmp)
cv2.waitKey(1)
frame=frame+1
finally:
connection.close()
server_socket.close()
这是它运行的一个小视频。它的响应速度非常快,而且抖动只是因为我将屏幕捕获重新采样到 2fps 以使其低于 StackOverflow 2MB 限制。
您可以放心地忽略代码中所有与 GPIO 相关的内容 - 只是在逻辑分析仪上调整引脚的高低,这样我就可以准确地计时。
YUV 数据的布局如下:
Xburge03 的原始位图版本,Qef 的 SVG 版本。 - http://en.wikipedia.org/wiki/Image:Yuv420.png 的矢量化版本 - 使用讨论页上给出的程序创建。,公共领域,https://en.wikipedia.org/w/index.php?curid=18105371
鉴于您的问题似乎与网络带宽有关,我的方法是基于发送 YUV 数据,如上所述。我还查看了 Dan 关于压缩数据的建议,虽然带宽减少显然取决于您的照明和主题,但 YUV 数据确实适合一些 lz4 压缩。我看到网络带宽进一步减少了大约 25%,并且延迟没有明显增加。代码如下所示:
import lz4.frame
...
compressed=lz4.frame.compress(data,compression_level=lz4.frame.COMPRESSIONLEVEL_MINHC))