python - 如何在 Python 中通过 ZeroMQ PUB/SUB 从 Raspberry Pi 接收图像?
问题描述
我想将 Picamera 在 Raspberry Pi 上拍摄的图像发送到我的 windows 计算机。
我写了一些代码,如下(它们在这里被简化了),但是它卡在 frame = footage_socket.recv_string()
了client.py
.
我没有收到任何错误,但它总是卡在代码中,就像它冻结一样,无法转到下一行。工作正常并连续server.py
打印。'test'
如果您查看jpg_as_text
,您可以看到编码文本。
server.py
:
import picamera
import socket
import threading
import zmq
import cv2
import base64
from picamera.array import PiRGBArray
if __name__ == "__main__":
addr = 'ip_address'
camera = picamera.PiCamera() # Camera initialization
camera.resolution = (640, 480)
camera.framerate = 7
rawCapture = PiRGBArray(camera, size=(640, 480))
# FPV initialization
context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://%s:5555'%addr)
print(addr)
font = cv2.FONT_HERSHEY_SIMPLEX
for frame in camera.capture_continuous( rawCapture,
format = "bgr",
use_video_port = True ):
image = frame.array
print('test')
image = cv2.resize(image, (640, 480)) # resize the frame
encoded, buffer = cv2.imencode('.jpg', image)
jpg_as_text = base64.b64encode(buffer)
footage_socket.send(jpg_as_text)
rawCapture.truncate(0)
client.py
:
from socket import *
import sys
import time
import threading as thread
import tkinter as tk
import math
import os
import cv2
import zmq
import base64
import numpy as np
if __name__ == "__main__":
context = zmq.Context()
footage_socket = context.socket(zmq.SUB)
footage_socket.bind('tcp://*:5555')
footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))
font = cv2.FONT_HERSHEY_SIMPLEX
while 100:
try:
frame = footage_socket.recv_string() # This line of code is the problem.
print('next successfuly connected')
img = base64.b64decode(frame)
npimg = np.frombuffer(img, dtype=np.uint8)
source = cv2.imdecode(npimg, 1)
cv2.imshow("Stream", source)
cv2.waitKey(1)
except KeyboardInterrupt:
break
except:
pass
解决方案
问:如何在 Python 中通过 ZeroMQ 从树莓派接收图像?
PUB/SUB
如果您从未使用过 ZeroMQ,您可以在这里先看看“ ZeroMQ原则在不到五秒的时间内”,然后再深入了解更多细节
观察:
没有错误。
您应该使用其他数据采集策略+设置一些自卫参数。
.recv_string()
-方法以阻塞模式调用(它确实并且将永远阻塞代码执行,直到任何合理的符合规则成为可交付
使用zmq.NOBLOCK
flag 可以避免这种阻塞模式 + 使用.poll()
-method 可以帮助您设计私有事件驱动循环的逻辑,.recv( zmq.NOBLOCK )
以防万一,确实有一些东西可以交付。
SUB
-side 将一无所获,除非正确订阅以接收某些内容,默认状态 - 就像报纸一样 - 是一无所获,除非明确订阅 - 无论是朝日新闻还是其他标题 -。根据 API 记录的策略,订阅任何内容的最安全模式是使用 - 方法订阅零长度字符串.setsockopt( zmq.SUBSCRIBE, "" )
。
最后但并非最不重要的一点是,如果愿意进行 RPi-Win 流式传输,可能会有一个虎钳策略,因为除了最新的,入队/发布/传输/接收/出队没有任何价值frame
,为此.setsockopt( zmq.CONFLATE, 1 )
准备好了.
您可能需要对资源进行更多调整,以提高.Context( nIOthreads )
-instance 性能、保留队列深度、L3 堆栈参数和许多进一步可能的增强功能。
始终.setsockopt( zmq.LINGER, 0 )
为您设置永远不知道哪些版本将连接以及可能发生哪些默认设置,在这里,有机会让您崩溃的套接字实例永远挂起(通常直到操作系统重新启动),这似乎有点疯狂,未处理任何生产级软件的风险因素,不是吗?
解决方案提示:
- 避免错过 unicode 约定的风险,这些约定在 Linux 端发起者和 Windows O/S 端之间是不同的,彼此不匹配。
+
由于 unicode 对象具有广泛的表示形式,因此它们不会根据其编码存储为字节,而是以一种称为 UCS(一种较旧的固定宽度 Unicode 格式)的格式存储。在某些平台(OS X、Windows)上,存储是 UCS-2,即每个字符 2 个字节。在大多数ix 系统上,它是 UCS-4,即每个字符 4 个字节。unicode 对象的缓冲区内容不依赖于编码(总是 UCS-2 或 UCS-4),但它们依赖于平台。
...
+
这里的效率问题来自于简单的 ascii 字符串在内存中的大小是它们需要的 4 倍(在大多数 Linux 上,在其他平台上是 2 倍)。此外,转换为/从与 char 一起使用的 C 代码,您总是必须复制数据并对字节进行编码/解码。从内存的角度来看,这确实是非常低效的。本质上,在内存效率对您很重要的地方,您永远不应该使用字符串;使用字节。问题是用户几乎总是使用str
,并且在 2.x 中它们是有效的,但在 3.x 中它们不是。我们希望确保我们不会帮助用户犯这个错误,因此我们确保zmq
方法不会试图隐藏字符串的真实含义。
阅读有关 ZeroMQ 中延迟避免的更多信息,如果尝试流式传输常量和先验已知图像
( 640 x 480 x <colordepth> )
- 转换成本很高,将小规模、低分辨率、低 FPS RGB / IR 图片转换为 JPEG 文件格式仅用于传输是如果在 RPi 和 Win-device 之间使用本地 LAN 或专用 WLAN 网段,则毫无意义。以延迟为动机的设计可能会测试并可能避免任何类型的数据压缩,方法是使用cPickle.dumps()
或dill.dumps()
在二进制块 BLOB 中尽可能紧凑地发送数据,通常足以使用aNumpyObject.data
实用程序直接发送或使用/<read-write buffer for 0x7fa3cbe3f8a0, size 307200, offset 0 at 0x7f632bb2cc30>
进行一些二进制处理-methods,如果需要超越可用的-access 技巧。所有鉴于struct.pack()
.unpack()
numpy
.data
.setsockopt( zmq.CONFLATE, 1 )
两边都被激活,以避免缓冲直播数据的深度过大。出于性能和延迟的原因,您可以避免使用这
PUB/SUB
对原型,因为 ZeroMQ API v3.+ 已将 TOPIC 过滤的工作负载转移到 - 侧PUB
,这是您较弱的节点(而 RPi 有多个内核,您可以增强.Context( nIOthreads )
类固醇上的 -instance,为 I/O 提供更多功率,但与 Windows 端的 localhost 相比,RPi 的频率仅为 GHz 的一小部分,并且机器人紧密的控制回路可能已经吃掉了大部分用于控制)。对于 1 对 1 拓扑,使用PUSH/PULL
方式非常相似,而且由于在 RPi 端避免了处理,因此处理和 E2E 延迟开销也更少。
对于.poll()
基于不同优先级的事件处理程序,以及关于 Margaret HAMILTON 夫人和她的 MIT 团队的开创性工作的一些评论,可能喜欢阅读this & this。
推荐阅读
- python - 功能无法使用用户输入?
- apache-flink - 使用 Flink LocalEnvironment 进行生产
- c++ - 试图创建一个体面的基本刽子手游戏。猜到的字母不会取代短语中的字母
- python - Python-np.random.choice
- excel - VB 脚本——Excel“Activeworkbook”被打乱。如何声明显式工作簿?
- ios - 使用 firebase-auth 时在 ios 上构建应用程序会导致颤振问题
- c++ - 如何检查一个类是否完全符合给定的一组特征?
- java - 使用 java regex 抓取网站
- c# - 在 Visual Studio C# 中,如何自动添加控件的代码
- node.js - Nodejs dosent 邀请用户组(Steam)