图像处理中实现 C++ 和 Python 的高效通信——Boost.Interprocess & mmap

使用 Boost.Interprocess 在 C++ 端创建共享内存,并使用 Python 的 mmap 模块进行读写操作。


整体流程

  1. C++ 端

    • 创建共享内存并写入原始图像数据。
    • 等待 Python 端处理完成。
    • 从共享内存中读取处理后的图像数据。
  2. Python 端

    • 读取共享内存中的原始图像数据。
    • 处理图像(例如转换为灰度图)。
    • 将处理后的图像数据写回共享内存。

C++ 端代码

C++ 端负责创建共享内存、写入原始图像数据,并读取 Python 处理后的图像数据。

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace boost::interprocess;

int main() {
    // 读取原始图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_COLOR);
    if (image.empty()) {
        std::cerr << "Failed to load image!" << std::endl;
        return -1;
    }

    // 共享内存名称
    const std::string shm_name = "SharedImageMemory";

    // 创建共享内存对象
    shared_memory_object shm(open_or_create, shm_name.c_str(), read_write);

    // 设置共享内存大小(原始图像 + 处理后的图像)
    size_t image_size = image.total() * image.elemSize();
    shm.truncate(image_size * 2);  // 两倍大小,分别存储原始图像和处理后的图像

    // 映射共享内存
    mapped_region region(shm, read_write);

    // 将原始图像数据写入共享内存的前半部分
    std::memcpy(region.get_address(), image.data, image_size);
    std::cout << "Original image data written to shared memory." << std::endl;

    // 等待 Python 端处理完成
    std::cout << "Waiting for Python to process the image..." << std::endl;
    std::cin.get();

    // 从共享内存的后半部分读取处理后的图像数据
    cv::Mat processed_image(image.size(), image.type());
    std::memcpy(processed_image.data, static_cast<char*>(region.get_address()) + image_size, image_size);

    // 保存处理后的图像
    cv::imwrite("output_processed.jpg", processed_image);
    std::cout << "Processed image saved to output_processed.jpg." << std::endl;

    // 清理共享内存
    shared_memory_object::remove(shm_name.c_str());
    return 0;
}

Python 端代码

Python 端负责读取共享内存中的原始图像数据,处理图像,并将处理后的图像数据写回共享内存。

import mmap
import numpy as np
import cv2

# 共享内存名称(与 C++ 端一致)
SHARED_MEMORY_NAME = "SharedImageMemory"

# 图像尺寸和类型(需要与 C++ 端一致)
IMAGE_WIDTH = 640  # 图像的宽度
IMAGE_HEIGHT = 480  # 图像的高度
IMAGE_CHANNELS = 3  # 图像的通道数(例如,3 表示 RGB 图像)

# 计算图像的总字节数
image_size = IMAGE_WIDTH * IMAGE_HEIGHT * IMAGE_CHANNELS

# 打开共享内存
with mmap.mmap(-1, image_size * 2, tagname=SHARED_MEMORY_NAME, access=mmap.ACCESS_WRITE) as shm:
    # 读取原始图像数据(共享内存的前半部分)
    image_data = shm.read(image_size)
    image_array = np.frombuffer(image_data, dtype=np.uint8)
    original_image = image_array.reshape((IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_CHANNELS))

    # 处理图像(例如转换为灰度图)
    processed_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)

    # 将处理后的图像数据写回共享内存的后半部分
    shm.seek(image_size)  # 移动到共享内存的后半部分
    shm.write(processed_image.tobytes())

    print("Processed image data written back to shared memory.")

运行步骤

  1. C++ 端

    • 编译并运行 C++ 程序。程序会将原始图像写入共享内存,并等待 Python 端处理。
  2. Python 端

    • 运行 Python 脚本。脚本会读取共享内存中的原始图像,处理图像,并将处理后的图像写回共享内存。
  3. C++ 端

    • 在 Python 端处理完成后,按 Enter 键继续运行 C++ 程序。程序会从共享内存中读取处理后的图像并保存。

关键点说明

  1. 共享内存布局
    • 共享内存的前半部分存储原始图像,后半部分存储处理后的图像。
  2. 同步机制
    • 示例中使用简单的 std::cin.get() 等待用户输入作为同步机制。在实际应用中,可以使用更复杂的同步机制(如信号量或互斥锁)。
  3. 共享内存清理
    • C++ 端在程序结束前调用 shared_memory_object::remove 清理共享内存。
  4. 共享内存名称
    • C++ 和 Python 必须使用相同的共享内存名称(如 SharedImageMemory)。
  5. 图像尺寸和类型
    • Python 端需要知道图像的宽度、高度和通道数,以便正确解析共享内存中的数据。
  6. 共享内存大小
    • C++ 端通过 shm.truncate() 设置共享内存的大小,Python 端需要确保读取的字节数与 C++ 端一致。
  7. 数据格式
    • 共享内存中的数据是原始的字节流,Python 端需要使用 np.frombuffer 将其转换为 NumPy 数组,并重塑为图像形状。
  8. 同步
    • 在实际应用中,可能需要额外的同步机制(如信号量或互斥锁)来确保 C++ 和 Python 之间的数据一致性。

注意事项

  • 确保 C++ 和 Python 端的图像尺寸、通道数和数据类型一致。
  • 在实际应用中,可能需要处理更复杂的图像数据(如多通道、浮点类型等)。
  • 如果需要更高的性能,可以考虑使用 ZeroMQ 或 gRPC 等网络通信方式。
#include &lt;chrono&gt; #include &lt;iostream&gt; #include &lt;csignal&gt; #include &lt;cstring&gt; #include &lt;memory&gt; #include &quot;act_L515.h&quot; #include &quot;cuda_runtime.h&quot; #include &lt;boost/interprocess/managed_shared_memory.hpp&gt; #include &lt;boost/interprocess/sync/named_mutex.hpp&gt; #include &lt;boost/interprocess/sync/scoped_lock.hpp&gt; #include &lt;boost/atomic.hpp&gt; #include &lt;atomic&gt; #include &lt;opencv2/opencv.hpp&gt; namespace bip = boost::interprocess; using namespace std; using namespace cv; // 共享内存数据结构 #pragma pack(push, 1) // 确保结构体按字节对齐 struct SharedImageData { boost::atomic&lt;bool&gt; ready; //数据就绪标志 int32_t width; //图像宽度 int32_t height; //图像高度 int32_t type; //图像类型 uint64_t dataSize; //图像数据大小 }; #pragma pack(pop) atomic&lt;bool&gt; should_exit(false); cv::Mat Imgframe; // 共享内存名称互斥锁名称 const char* shmName = &quot;L515_SharedMem&quot;; // 系统自动在/dev/shm/目录下创建对应文件,/dev/shm/[shmName] const char* mutexName = &quot;L515_Mutex&quot;; // 用于管理共享内存的全局对象 std::unique_ptr&lt;bip::managed_shared_memory&gt; g_segment; std::unique_ptr&lt;bip::named_mutex&gt; g_mutex; void sigint_handler(int signum) { cout &lt;&lt; &quot;Received SIGINT signal (&quot; &lt;&lt; signum &lt;&lt; &quot;)&quot; &lt;&lt; endl; should_exit = true; // 确保清理共享内存资源 if(g_mutex) g_mutex.reset(); if(g_segment) g_segment.reset(); bip::shared_memory_object::remove(shmName); bip::named_mutex::remove(mutexName); cout &lt;&lt; &quot;Shared memory and mutex removed.&quot; &lt;&lt; endl; } void init_shared_memory() { try { // 清理先前的共享内存 bip::shared_memory_object::remove(shmName); bip::named_mutex::remove(mutexName); // 创建共享内存 g_segment = std::make_unique&lt;bip::managed_shared_memory&gt;(bip::create_only, shmName, 100 * 1024 * 1024); // 创建互斥锁 g_mutex = std::make_unique&lt;bip::named_mutex&gt;(bip::create_only, mutexName); // 创建头信息 SharedImageData* header = g_segment-&gt;construct&lt;SharedImageData&gt;(&quot;Header&quot;)(); header-&gt;ready = false; cout &lt;&lt; &quot;共享内存初始化完成&quot; &lt;&lt; endl; size_t expected_size = sizeof(SharedImageData); size_t actual_size = g_segment-&gt;get_size(); cout &lt;&lt; &quot;头信息大小: &quot; &lt;&lt; expected_size &lt;&lt; &quot; 字节&quot; &lt;&lt; endl; cout &lt;&lt; &quot;实际共享内存大小: &quot; &lt;&lt; actual_size &lt;&lt; &quot; 字节&quot; &lt;&lt; endl; } catch(const bip::interprocess_exception&amp; e) { cerr &lt;&lt; &quot;共享内存初始化失败: &quot; &lt;&lt; e.what() &lt;&lt; endl; } } void update_shared_memory(const cv::Mat&amp; image) { if(!g_segment || !g_mutex) { cerr &lt;&lt; &quot;共享内存未初始化&quot; &lt;&lt; endl; return; } try { // 获取头信息 SharedImageData* header = g_segment-&gt;find&lt;SharedImageData&gt;(&quot;Header&quot;).first; if(!header) { cerr &lt;&lt; &quot;头信息未找到&quot; &lt;&lt; endl; return; } // 确保数据连续 cv::Mat frameToShare = image.clone(); // 计算数据大小 size_t dataSize = frameToShare.total() * frameToShare.elemSize(); // 加锁 bip::scoped_lock&lt;bip::named_mutex&gt; lock(*g_mutex); // 更新头信息 header-&gt;ready = false; header-&gt;width = frameToShare.cols; header-&gt;height = frameToShare.rows; header-&gt;type = frameToShare.type(); header-&gt;dataSize = static_cast&lt;uint64_t&gt;(dataSize); // 创建或重建图像数据区 g_segment-&gt;destroy&lt;uchar&gt;(&quot;ImageData&quot;); // 清除旧数据 uchar* sharedData = g_segment-&gt;construct&lt;uchar&gt;(&quot;ImageData&quot;)[dataSize](0); if(sharedData) { // 复制图像数据到共享内存 std::memcpy(sharedData, frameToShare.data, dataSize); } else { cerr &lt;&lt; &quot;无法创建或访问共享内存&quot; &lt;&lt; endl; return; } // 标记数据可用 header-&gt;ready = true; cout &lt;&lt; &quot;更新共享内存&quot; &lt;&lt; &quot; 宽度= &quot; &lt;&lt; frameToShare.cols &lt;&lt; &quot; 高度= &quot; &lt;&lt; frameToShare.rows &lt;&lt; &quot; 类型= &quot; &lt;&lt; frameToShare.type() &lt;&lt; &quot; 数据大小= &quot; &lt;&lt; dataSize &lt;&lt; &quot; 字节&quot; &lt;&lt; endl; } catch(const bip::interprocess_exception&amp; e) { cerr &lt;&lt; &quot;更新共享内存失败: &quot; &lt;&lt; e.what() &lt;&lt; endl; } } int main(int argc, char ** argv) { signal(SIGINT, sigint_handler); cudaSetDevice(0); // 初始化共享内存 init_shared_memory(); ActL515 L515; L515.Init(); while(!should_exit) { L515.Update(); resize(L515.GetSrcImage(), Imgframe, Size(640, 480), 0, 0, INTER_AREA); update_shared_memory(Imgframe); cout &lt;&lt; &quot;头信息大小&quot; &lt;&lt; sizeof(SharedImageData) &lt;&lt; &quot;字节&quot; &lt;&lt; endl; // cv::namedWindow(&quot;L515&quot;, cv::WINDOW_NORMAL); // cv::imshow(&quot;L515&quot;, Imgframe); // cv::waitKey(1); } sigint_handler(0); return 0; } 这是我写入共享内存的数据,帮我看看怎么用python调取共享内存中的数据
最新发布
06-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值