我有三个python文件:glob_var.py,read_cam.py,read_globVar.py。其内容如下: glob_var.py:
globVar = {}
def set(name, val):
globVar[name] = val
def get(name):
val = globVar.get(name, None)
return val
read_cam.py
import cv2
import glob_var
if __name__ == '__main__':
cam = cv2.VideoCapture(0)
key = 0
while key != 27:
ret, img = cam.read()
cv2.imshow('img', img)
key = cv2.waitKey(1) & 0xFF
glob_var.set('image', img)
read_globVar.py
import glob_var
import cv2
from time import sleep
if __name__ == '__main__':
key = 0
while key != 27:
img = glob_var.get('image')
if img is None:
print(f"no image in globVar")
sleep(1)
continue
print(f"read image with shape {img.shape}")
cv2.imshow('image', img)
key = cv2.waitKey(1) & 0xFF
从那三只蟒蛇的苍蝇中,我想你们知道我想要做什么。是的,我希望read_cam.py从相机读取图像并将其广播到全局变量。然后read_globVar.py可以显示该图像。 我在一个终端中运行read_cam.py,在另一终端中运行read_globVar.py。 但是我没有使其正常工作。我在想什么可能吗?我该如何管理?非常感谢!
====== update1:python中的Pub和Sub =====
我已经使用ROS(机器人操作系统)系统已有一段时间了。它提供发布和订阅功能,以在不同程序或所谓的节点之间交换变量。 所以我的问题是python中是否有任何提供此功能的软件包? Redis提供此功能,这是最快还是最好的方法?
答案 0 :(得分:1)
您可以使用 Redis 进行此操作。它是一种非常快速的内存中数据结构服务器,可以为字符串,整数,哈希,列表,队列,集合,有序集合,图像提供服务。它是免费且简单的安装在 macOS , Linux 和 Windows 上。
此外,您可以使用bash
,Python,PHP,C / C ++或许多其他语言来读取或写入Redis值。此外,您只需在初始连接中更改IP地址,就可以在网络或世界范围内的服务器上进行读写操作。因此,有效地,您可以在Linux下的Raspberry Pi上用Python获取图像,然后将其存储并在Windows下以C / C ++在PC上进行处理。
然后,您只需将图像放入名为Camera1
或Entrance
的 Redis 中,或将它们放入经过排序的哈希中,以便可以按帧号缓冲图像。您还可以为图像(或其他数据结构)提供“生存时间” ,这样您的RAM就不会用完。
以下是使用 Redis 重写的代码的基本内容。目前没有严重的错误检查或灵活性。一切正常。
这里是read_cam.py
:
#!/usr/bin/env python3
import cv2
import struct
import redis
import numpy as np
def toRedis(r,a,n):
"""Store given Numpy array 'a' in Redis under key 'n'"""
h, w = a.shape[:2]
shape = struct.pack('>II',h,w)
encoded = shape + a.tobytes()
# Store encoded data in Redis
r.set(n,encoded)
return
if __name__ == '__main__':
# Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)
cam = cv2.VideoCapture(0)
key = 0
while key != 27:
ret, img = cam.read()
cv2.imshow('img', img)
key = cv2.waitKey(1) & 0xFF
toRedis(r, img, 'image')
这是read_globvar.py
:
#!/usr/bin/env python3
import cv2
from time import sleep
import struct
import redis
import numpy as np
def fromRedis(r,n):
"""Retrieve Numpy array from Redis key 'n'"""
encoded = r.get(n)
h, w = struct.unpack('>II',encoded[:8])
a = np.frombuffer(encoded, dtype=np.uint8, offset=8).reshape(h,w,3)
return a
if __name__ == '__main__':
# Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)
key = 0
while key != 27:
img = fromRedis(r,'image')
print(f"read image with shape {img.shape}")
cv2.imshow('image', img)
key = cv2.waitKey(1) & 0xFF
请注意,您可以将图像的高度和宽度同样存储在JSON中,并将其存储在Redis中,而不是我所做的struct.pack
和struct.unpack
。
也请注意,您可以将图像编码为JPEG在内存中,并将JPEG存储在Redis(而不是Numpy数组)中,这样可以节省内存和网络带宽。
无论哪种方式,使用Redis的概念都是相同的。
答案 1 :(得分:1)
我在此处编写了一个如何使用内存映射文件共享图像的示例:https://github.com/off99555/python-mmap-ipc
此功能已在大多数语言中提供。 基本思想是我们将映像写入虚拟文件,然后在另一个进程中读取它。它具有约3-4ms的延迟,与相机固有的延迟相比,这是最小的。这种方法比TCP / IP,HTTP等互联网协议要快。我已经使用gRPC和ZeroMQ进行了测试。它们都比内存映射文件方法慢。
答案 2 :(得分:0)
您可以使用Python的 multiprocessing 模块中的共享数组在进程之间快速共享大量数据。我没有像我建议的Redis答案那样为您提供完整的,经过测试的代码,但我有足够的希望希望您入门。
因此您将使用:
from multiprocessing import Process, Queue
from multiprocessing.sharedctypes import Array
from ctypes import c_uint8
然后在您的main
中,您将声明一个大数组,可能足够大,可以容纳2-4个大图像:
bufShape = (1080, 1920,3) # 1080p
和
# Create zeroed out shared array
buffer = Array(c_uint8, bufShape[0] * bufShape[1] * bufShape[2])
# Make into numpy array
buf_arr = np.frombuffer(buffer.get_obj(), dtype=c_uint8)
buf_arr.shape = bufShape
# Create a list of workers
workers = [Worker(1, buffer, str(i)) for i in range(2)]
# Start the workers
for worker in workers:
worker.start()
然后,您将像这样从 Process 类派生您的工人:
class Worker(Process):
def __init__(self, q_size, buffer, name=''):
super().__init__()
self.queue = Queue(q_size)
self.buffer = buffer
self.name = name
def run(self,):
buf_arr = np.frombuffer(self.buffer.get_obj(), dtype=c_uint8)
buf_arr.shape = bufShape
while True:
item = self.queue.get()
...
您可以在run()
的开头看到工作程序只是从大共享缓冲区中创建了一个Numpy数组,因此工作程序正在读取主程序正在编写的内容,但希望您进行同步,以便当main是写第2-4帧,工人正在阅读第1帧。
然后希望,您可以看到主程序可以通过以下方式将一个简单的帧索引写入到工作人员的队列中(而不是发送整个帧本身)来告诉工作人员该数据帧:
worker.queue.put(i)