【问题标题】:How to return numpy array by reference in pybind11如何在pybind11中通过引用返回numpy数组
【发布时间】:2022-06-15 18:32:07
【问题描述】:

我正在尝试使用 pybind11 从 C++ 对象返回一个 numpy 数组,其中该数组是从 C++ 类拥有的内存创建的。现在,我有缓冲区协议公开的类并返回一个 py::array:

auto raw_image_cls =
  py::class_<RawImage>(m, "RawImage", py::buffer_protocol());
....
    .def_buffer([](RawImage &img) -> py::buffer_info {
    size_t buff_sz = 0;
    return py::buffer_info(
        img.ImageData(buff_sz), img.BytesPerPixel(),
        GetFormatDescriptor(img.BytesPerPixel()), 2,
        {img.Height(), img.Width()},
        {img.Width() * img.BytesPerPixel(), img.BytesPerPixel()}

    );
  })
.def_property_readonly(
      "img",
      [](RawImage &img) -> py::array {
        size_t buff_sz = 0;
        // py::capsule buffer_handle([]() {});
        py::capsule buffer_handle(img.ImageData(buff_sz),
                                  [](void *p) { free(p); });
        return py::array(
            py::buffer_info(
                img.ImageData(buff_sz), img.BytesPerPixel(),
                GetFormatDescriptor(img.BytesPerPixel()), 2,
                {img.Height(), img.Width()},
                {img.Width() * img.BytesPerPixel(), img.BytesPerPixel()}),
            buffer_handle);

      },
      py::return_value_policy::reference_internal)
.....

当我使用 numpy 并执行以下操作时:

a = RawImage(filename)
b = numpy.array(a, copy=False)
a = 0
b

一切都按预期进行,并且 b 将保留。如果我这样做:

a = RawImage(filename)
b = a.img
a = 0
b

我得到一个段错误,这是有道理的,因为 a 被破坏了,但是我如何返回一个 py::array 并使用 numpy.array(a, copy=False) 获得相同的行为 - 这不会崩溃?

我试过了:

            return py::array(py::buffer_info(
            img.ImageData(buff_sz), img.BytesPerPixel(),
            GetFormatDescriptor(img.BytesPerPixel()), 2,
            {img.Height(), img.Width()},
            {img.Width() * img.BytesPerPixel(), img.BytesPerPixel()}));

没有缓冲区句柄,但这只会制作完整的副本,这确实会减慢速度。有没有办法告诉 py:array 我们基本上只想返回一个引用或指向我在 C++ 中的内存的对象(来自 img.ImageData(sz))来做与 numpy.array(a, copy =假)?

【问题讨论】:

    标签: python pybind11


    【解决方案1】:

    这可能不是您正在寻找的答案。

    numpy 数组以及 std::vector 的“问题”是它拥有所有权。但是在 Python 和 C++ 中,当对象被删除时,内存将被释放。

    我通过使用std::vector&lt;T&gt; * 并用它构造py::array_t&lt;T&gt; 解决了这个问题:

    #include <pybind11/functional.h>
    #include <pybind11/numpy.h>
    #include <pybind11/pybind11.h>
    #include <pybind11/stl.h>
    
    template <typename T>
    py::array Vec2NpArray(std::vector<T> *data,
                          std::vector<size_t> shape) {
    
       // calculate stride of multidimensional data from shape
       std::vector<size_t> stride(shape.size(), 0);
       size_t elm_stride = sizeof(T);
       auto shape_it = shape.rbegin();
       auto stride_it = stride.rbegin();
       for (; stride_it != stride.rend(); stride_it++, shape_it++) {
          *stride_it = elm_stride;
          elm_stride *= *shape_it;
       }
    
       // tell python to delete pointer when deconstruction
       auto capsule = py::capsule(
           data, [](void *data) { delete reinterpret_cast<std::vector<T> *>(data); });
    
       return py::array_t<T>(shape, stride, data->data(), capsule);
    }
    

    如果有人有其他解决方案,我会很感兴趣。

    【讨论】:

      猜你喜欢
      • 2017-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-11
      • 2020-02-29
      • 2021-12-31
      • 2021-03-01
      相关资源
      最近更新 更多