gpt4 book ai didi

python - 如何通过共享内存将 cv::Mat 发送到 python?

转载 作者:行者123 更新时间:2023-12-01 14:43:12 25 4
gpt4 key购买 nike

我有一个 c++ 应用程序,它通过共享内存将数据发送到 python 函数。
使用 ctypes 效果很好在 Python 中,例如 double 数和浮点数。现在,我需要添加一个 cv::Mat到功能。

我目前的代码是:

//H

#include <iostream>
#include <opencv2\core.hpp>
#include <opencv2\highgui.hpp>


struct TransferData
{
double score;
float other;
int num;
int w;
int h;
int channels;
uchar* data;

};

#define C_OFF 1000
void fill(TransferData* data, int run, uchar* frame, int w, int h, int channels)
{
data->score = C_OFF + 1.0;
data->other = C_OFF + 2.0;
data->num = C_OFF + 3;
data->w = w;
data->h = h;
data->channels = channels;
data->data = frame;
}

//.cpp
namespace py = pybind11;

using namespace boost::interprocess;

void main()
{

//python setup
Py_SetProgramName(L"PYTHON");
py::scoped_interpreter guard{};
py::module py_test = py::module::import("Transfer_py");


// Create Data
windows_shared_memory shmem(create_only, "TransferDataSHMEM",
read_write, sizeof(TransferData));

mapped_region region(shmem, read_write);
std::memset(region.get_address(), 0, sizeof(TransferData));

TransferData* data = reinterpret_cast<TransferData*>(region.get_address());


//loop
for (int i = 0; i < 10; i++)
{
int64 t0 = cv::getTickCount();

std::cout << "C++ Program - Filling Data" << std::endl;

cv::Mat frame = cv::imread("input.jpg");

fill(data, i, frame.data, frame.cols, frame.rows, frame.channels());

//run the python function

//process
py::object result = py_test.attr("datathrough")();


int64 t1 = cv::getTickCount();
double secs = (t1 - t0) / cv::getTickFrequency();

std::cout << "took " << secs * 1000 << " ms" << std::endl;
}

std::cin.get();
}

//Python
//传输数据类
import ctypes


class TransferData(ctypes.Structure):
_fields_ = [
('score', ctypes.c_double),
('other', ctypes.c_float),
('num', ctypes.c_int),
('w', ctypes.c_int),
('h', ctypes.c_int),
('frame', ctypes.c_void_p),
('channels', ctypes.c_int)
]


PY_OFF = 2000

def fill(data):
data.score = PY_OFF + 1.0
data.other = PY_OFF + 2.0
data.num = PY_OFF + 3

//主要Python函数
import TransferData
import sys
import mmap
import ctypes




def datathrough():
shmem = mmap.mmap(-1, ctypes.sizeof(TransferData.TransferData), "TransferDataSHMEM")
data = TransferData.TransferData.from_buffer(shmem)
print('Python Program - Getting Data')
print('Python Program - Filling Data')
TransferData.fill(data)

如何添加 cv::Mat帧数据到 Python 端?我将其作为 uchar* 发送来自 c++,据我了解,我需要它是 numpy数组以获取 cv2.Mat在 Python 中。从“宽度、高度、 channel 、帧数据”到 opencv python cv2.Mat 的正确方法是什么? ?

我使用共享内存是因为速度是一个因素,我已经使用 Python API 方法进行了测试,但它对于我的需求来说太慢了。

最佳答案

一般的想法(在 OpenCV Python 绑定(bind)中使用)是创建一个 numpy ndarrayMat 共享其数据缓冲区对象,并将其传递给 Python 函数。

注意:此时,我将示例仅限于连续矩阵。

我们可以利用 pybind11::array 类(class)。

  • 我们需要确定合适的dtype供numpy数组使用。这是一个简单的一对一映射,我们可以使用 switch :
    py::dtype determine_np_dtype(int depth)
    {
    switch (depth) {
    case CV_8U: return py::dtype::of<uint8_t>();
    case CV_8S: return py::dtype::of<int8_t>();
    case CV_16U: return py::dtype::of<uint16_t>();
    case CV_16S: return py::dtype::of<int16_t>();
    case CV_32S: return py::dtype::of<int32_t>();
    case CV_32F: return py::dtype::of<float>();
    case CV_64F: return py::dtype::of<double>();
    default:
    throw std::invalid_argument("Unsupported data type.");
    }
    }
  • 确定 numpy 数组的形状。为了使其行为类似于 OpenCV,让我们让它映射 1 channel Mat s 到 2D numpy 数组和多 channel Mat s 到 3D numpy 数组。
    std::vector<std::size_t> determine_shape(cv::Mat& m)
    {
    if (m.channels() == 1) {
    return {
    static_cast<size_t>(m.rows)
    , static_cast<size_t>(m.cols)
    };
    }

    return {
    static_cast<size_t>(m.rows)
    , static_cast<size_t>(m.cols)
    , static_cast<size_t>(m.channels())
    };
    }
  • 提供将共享缓冲区的生命周期延长到 numpy 数组的生命周期的方法。我们可以创建一个 pybind11::capsule围绕源的浅拷贝Mat -- 由于对象的实现方式,这有效地增加了所需时间的引用计数。
    py::capsule make_capsule(cv::Mat& m)
    {
    return py::capsule(new cv::Mat(m)
    , [](void *v) { delete reinterpret_cast<cv::Mat*>(v); }
    );
    }

  • 现在,我们可以执行转换了。
    py::array mat_to_nparray(cv::Mat& m)
    {
    if (!m.isContinuous()) {
    throw std::invalid_argument("Only continuous Mats supported.");
    }

    return py::array(determine_np_dtype(m.depth())
    , determine_shape(m)
    , m.data
    , make_capsule(m));
    }

    假设,我们有一个 Python 函数,比如
    def foo(arr):
    print(arr.shape)

    在 pybind 对象中捕获 fun .然后使用 Mat 从 C++ 调用此函数作为一个来源,我们会做这样的事情:
    cv::Mat img; // Initialize this somehow

    auto result = fun(mat_to_nparray(img));

    示例程序
    #include <pybind11/pybind11.h>
    #include <pybind11/embed.h>
    #include <pybind11/numpy.h>
    #include <pybind11/stl.h>

    #include <opencv2/opencv.hpp>

    #include <iostream>

    namespace py = pybind11;

    // The 4 functions from above go here...

    int main()
    {
    // Start the interpreter and keep it alive
    py::scoped_interpreter guard{};

    try {
    auto locals = py::dict{};

    py::exec(R"(
    import numpy as np

    def test_cpp_to_py(arr):
    return (arr[0,0,0], 2.0, 30)
    )");

    auto test_cpp_to_py = py::globals()["test_cpp_to_py"];


    for (int i = 0; i < 10; i++) {
    int64 t0 = cv::getTickCount();

    cv::Mat img(cv::Mat::zeros(1024, 1024, CV_8UC3) + cv::Scalar(1, 1, 1));

    int64 t1 = cv::getTickCount();

    auto result = test_cpp_to_py(mat_to_nparray(img));

    int64 t2 = cv::getTickCount();

    double delta0 = (t1 - t0) / cv::getTickFrequency() * 1000;
    double delta1 = (t2 - t1) / cv::getTickFrequency() * 1000;

    std::cout << "* " << delta0 << " ms | " << delta1 << " ms" << std::endl;
    }
    } catch (py::error_already_set& e) {
    std::cerr << e.what() << "\n";
    }

    return 0;
    }

    控制台输出
    * 4.56413 ms | 0.225657 ms
    * 3.95923 ms | 0.0736127 ms
    * 3.80335 ms | 0.0438603 ms
    * 3.99262 ms | 0.0577587 ms
    * 3.82262 ms | 0.0572 ms
    * 3.72373 ms | 0.0394603 ms
    * 3.74014 ms | 0.0405079 ms
    * 3.80621 ms | 0.054546 ms
    * 3.72177 ms | 0.0386222 ms
    * 3.70683 ms | 0.0373651 ms

    关于python - 如何通过共享内存将 cv::Mat 发送到 python?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60949451/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com