在C/C++和Python之间共享内存

8
有没有一种方法可以共享内存来在C++和Python之间共享openCV图像(MAT在C++中,numpy在Python中)?不需要多平台,我在Linux上运行,我考虑使用mmap或类似的方式进行共享。
我有两个正在运行的进程,一个是用C编写的,另一个是Python,我需要在它们之间共享一个图像。
我将从C进程通过套接字调用Python,但我需要通过内存发送图像。
另一种选择可能是在内存文件中写入,不确定是否会更耗时。

您可以在本地流式传输帧。 - zindarod
2个回答

3

好的,这不完全是内存共享的真正意义上的东西。你想要的是通过IPC将图像数据从一个进程发送到另一个进程。

我建议您使用Unix命名管道。您需要在C/C++中以字符串格式获取原始数据,将其通过管道或Unix套接字发送到Python,并在那里从发送的数据中获取numpy数组。也许可以使用np.fromstring()函数。

不要担心速度,管道非常快。本地和Unix套接字也同样快。大部分时间都会花费在获取字符串表示形式并将其转化回矩阵上。

有可能您可以创建真正的共享内存空间,并直接从C/C ++中的OpenCV获取数据到Python中,然后再使用Python中的OpenCV获取出numpy数组,但这会很复杂。如果您不需要光速般的速度,最好使用命名管道。


谢谢,是的,我指的是IPC。我认为将其转换为HEXASCII可能是一个非常耗时的过程,如果没有更好的解决方案,那么将其写入内存文件(通过tmpfs)可能是另一种选择。 - Mquinteiro
1
不要使用HEXASCII,天哪!这将是可怕的。别想它了。只需要干净漂亮的二进制数据。每个RGB像素3个八位字节。为了在另一端获得正确的矩阵,你只需要知道原始图像的尺寸。在OpenCV和任何其他成像库中都有函数可以让你从其对象中获取二进制字符串。在C中,如果你有多维数组,你可以将其强制转换为char类型。当然,这取决于颜色的存储方式,但基本上是强制转换和序列化。 - Dalen
1
请执行:import numpy; help(numpy.fromstring) 以查看 fromstring() 的功能。它类似于 struct.unpack() 和 array.array()。命名管道可以传输无限的数据流,但空终止符并不适用于此情况。您可以通过管道或套接字发送 "\0" 而不会出现问题。 空终止符仅适用于 C 中的 ASCII 字符串,并告诉字符串操作函数其结尾在哪里,以便它们不必获取字符串的长度。对于所有其他目的,"\0" 只是存储在内存中的另一个有效字节。 - Dalen
1
太好了!但是如果使用套接字,应该使用Unix套接字。除非您计划在网络上分发进程。 Unix套接字是同类中最快的。所有测试都表明,命名管道甚至比Unix套接字更快,但我无法确认或否认它。绝对最快的IPC方法是共享内存。但是,设置它会让您陷入很多麻烦。即使您不打算共享内部opencv缓冲区(这将是一场完全的混乱,并且您将浪费很多时间尝试它),而是在共享内存上使用tostring()和fromstring()。 - Dalen
1
在这种情况下,您需要一种机制来告诉您,在共享内存中有另一个缓冲区正在等待您捡起它。使用套接字或管道可以很好地解决此问题。您甚至可以将套接字设置为非阻塞模式,并使用select.select()检查是否应该进行任何读取操作。命名管道的一个缺点(从实际角度看)是它是单向通信。但我认为您不需要双向通信。所以对您来说都是相同的。在您的位置上,我会坚持使用套接字,但改用Unix套接字。您不必更改太多代码。 - Dalen
显示剩余5条评论

0

虽然不完全符合您的要求,但您可以使用内存数据库(例如Redis)作为在程序之间交换OpenCV图像的通道。虽然比原始内存映射方式更间接,并且引入了额外的应用层,但数据仍然严格在RAM中操作。根据我的经验,这种策略在现代计算机上足够快,接近实时。

我曾经在https://github.com/vmlaker/hello-websocket中使用过这样的架构,尽管两端都使用Python。

以下是一个类似协议的最简示例,其中源代码使用C++编写,目标应用程序使用Python。下面的C++程序使用OpenCV从磁盘文件中读取图像,并将该图像存储在Redis中的键image下:

#include <opencv4/opencv2/opencv.hpp>
#include <cpp_redis/cpp_redis>

int main(int argc, char** argv)
{
  cv::Mat image = cv::imread("input.jpg");
  std::vector<uchar> buf;
  cv::imencode(".jpg", image, buf);

  cpp_redis::client client;
  client.connect();
  client.set("image", {buf.begin(), buf.end()});
  client.sync_commit();
}

然后在Python中,您可以从数据库中获取图像并根据需要进行操作:

import cv2
import numpy as np
import redis

store = redis.Redis()
image = store.get('image')
array = np.frombuffer(image, np.uint8)
decoded = cv2.imdecode(array, flags=1)
cv2.imshow('hello', decoded)
cv2.waitKey()

反过来也很简单。你可以在这里获取cpp_redishttps://github.com/cpp-redis/cpp_redis


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接