如何在两个python程序中共享OpenCV图像?

时间:2019-07-09 02:12:42

标签: python opencv

我有三个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提供此功能,这是最快还是最好的方法?

3 个答案:

答案 0 :(得分:1)

您可以使用 Redis 进行此操作。它是一种非常快速的内存中数据结构服务器,可以为字符串,整数,哈希,列表,队列,集合,有序集合,图像提供服务。它是免费且简单的安装在 macOS Linux Windows 上。

此外,您可以使用bash,Python,PHP,C / C ++或许多其他语言来读取或写入Redis值。此外,您只需在初始连接中更改IP地址,就可以在网络或世界范围内的服务器上进行读写操作。因此,有效地,您可以在Linux下的Raspberry Pi上用Python获取图像,然后将其存储并在Windows下以C / C ++在PC上进行处理。

然后,您只需将图像放入名为Camera1Entrance 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.packstruct.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)