You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++与Numpy/Python绑定中复杂生命周期问题求解

C++与numpy/Python间的生命周期同步问题求解

我开发了cvnp库,基于pybind11实现cv::Mat与py::array对象的双向内存共享转换,该库已被多个项目使用。

目前遇到cv::Mat与py::array的生命周期关联问题:

  • cv::Mat → py::array方向:通过capsule让py::array持有cv::Mat的引用,运行正常;
  • py::array → cv::Mat方向:生成的cv::Mat直接访问py::array的数据,但未持有其引用,无法保证二者生命周期一致,可能导致段错误。

相关实现代码

mat_to_nparray实现

py::capsule make_capsule_mat(const cv::Mat& m)
{
    return py::capsule(new cv::Mat(m)
        , [](void *v) { delete reinterpret_cast<cv::Mat*>(v); }
    );
}

pybind11::array mat_to_nparray(const cv::Mat& m)
{
    return pybind11::array(detail::determine_np_dtype(m.depth())
        , detail::determine_shape(m)
        , detail::determine_strides(m)
        , m.data
        , detail::make_capsule_mat(m)
        );
}

nparray_to_mat实现

cv::Mat nparray_to_mat(pybind11::array& a)
{
    ...
    cv::Mat m(size, type, is_not_empty ? a.mutable_data(0) : nullptr);
    return m;
}

段错误触发场景

  • C++绑定函数:
m.def("test", [](cv::Mat mat) { return mat; });
  • Python调用代码:
img = np.zeros(shape=(480, 640, 3), dtype=np.uint8)
img = test(img)

此时py::array可能先于cv::Mat销毁,导致cv::Mat访问已释放内存,段错误是否出现取决于操作系统和Python版本。我已通过ASAN在CI中复现该问题,复现代码如下:

void test_lifetime()
{
    // We need to create a big array to trigger a segfault
    auto create_example_array = []() -> pybind11::array
    {
        constexpr int rows = 1000, cols = 1000;
        std::vector<pybind11::ssize_t> a_shape{rows, cols};
        std::vector<pybind11::ssize_t> a_strides{};
        pybind11::dtype a_dtype = pybind11::dtype(pybind11::format_descriptor<int32_t>::format());
        pybind11::array a(a_dtype, a_shape, a_strides);
        // Set initial values
        for(int i=0; i<rows; ++i)
            for(int j=0; j<cols; ++j)
                *((int32_t *)a.mutable_data(j, i)) = j * rows + i;

        printf("Created array data address =%p\n%s\n",
               a.data(),
               py::str(a).cast<std::string>().c_str());
        return a;
    };

    // Let's reimplement the bound version of the test function via pybind11:
    auto test_bound = [](pybind11::array& a) {
        cv::Mat m = cvnp::nparray_to_mat(a);
        return cvnp::mat_to_nparray(m);
    };

    // Now let's reimplement the failing python code in C++
    //    img = np.zeros(shape=(480, 640, 3), dtype=np.uint8)
    //    img = test(img)
    auto img = create_example_array();
    img = test_bound(img);

    // Let's try to change the content of the img array
    *((int32_t *)img.mutable_data(0, 0)) = 14;  // This triggers an error that ASAN catches
    printf("img data address =%p\n%s\n",
           img.data(),
           py::str(img).cast<std::string>().c_str());
}

需求与困惑

理想的解决方案是:

  • 在nparray_to_mat构造cv::Mat时调用pybind11::array.inc_ref()
  • 确保cv::Mat实例销毁时调用pybind11::array.dec_ref()

但目前不知如何实现这一点。注:cv::Mat的自定义分配器在此场景无用,因为它不分配内存,仅复用py::array的内存。

期待相关解决方案建议。

内容的提问来源于stack exchange,提问作者Pascal T.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:54:57