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

如何通过pybind11实现cv::cuda::GpuMat与cv2.cuda_GpuMat无拷贝映射?

问题

我正在用C++通过pybind11从相机接收图像到cv::cuda::GpuMat结构中,希望在Python里不用拷贝数据就能把它转成cv2.cuda_GpuMat结构使用。有没有办法把cv::cuda::GpuMat的属性映射到对应的cv2.cuda_GpuMat属性,让支持CUDA的Python版OpenCV可以直接用?

最小化测试代码(C++)

#include <pybind11/pybind11.h>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudaarithm.hpp>
#include <cuda_runtime.h>

#include <iostream>
#include <string>
#include <vector>

namespace py=pybind11;
using namespace cv;
using namespace std;


struct miniGpuMat{
  miniGpuMat() { cout << "construct miniGPUmat" << endl;}
  ~miniGpuMat() { cout << "destruction miniGPUmat" << endl;}
  void *imageGpu;
  void *imageBp;
  cuda::GpuMat gpu_mat;
  void getImage(){
    cudaHostGetDevicePointer(&imageGpu, imageBp, 0);
    // Create GpuMat from the device pointer
    gpu_mat = cuda::GpuMat(16, 16, CV_8UC1, imageGpu);
  }
};

PYBIND11_MODULE(pgxcam, m) {
  py::class_<miniGpuMat>(m, "miniGpuMat")
    .def(py::init<>())
    .def_readwrite("imageGpu", &miniGpuMat::imageGpu)
    .def_readwrite("imageBp", &miniGpuMat::imageBp)
    .def_readwrite("gpu_mat", &miniGpuMat::gpu_mat)
    ;}

Python测试代码

import cv2  
import pgxcam as xc    

testMini = xc.miniGpuMat()
dir(testMini)
type(testMini.gpu_mat)
del(testMini)

运行报错

>>> construct miniGPUmat
>>> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__',
     '__format__', '__ge__', '__getattribute__', '__gt__',
 '__hash__', '__init__', '__init_subclass__', '__le__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
 '__str__', '__subclasshook__', 'gpu_mat',
 'imageBp', 'imageGpu']
>>> TypeError: Unregistered type : cv::cuda::GpuMat

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<string>", line 19, in __PYTHON_EL_eval
  File "<...>/src/mini.py", line 1, in <module>
TypeError: Unable to convert function return value to a Python type!
  The signature was
    (self: pgxcam.miniGpuMat) -> cv::cuda::GpuMat
>>> destruction miniGPUmat

已调研内容

我本质上想实现类似cv2.cuda_GpuMat(cv::cuda::GpuMat)的效果。在OpenCV的cuda.hpp里有包装CV_WRAP GpuMat(const GpuMat& m);,其实现是:

inline
GpuMat::GpuMat(const GpuMat& m)
    : flags(m.flags), rows(m.rows), cols(m.cols), step(m.step), 
      data(m.data), refcount(m.refcount), datastart(m.datastart), 
      dataend(m.dataend), allocator(m.allocator)
{
    if (refcount)
        CV_XADD(refcount, 1);
}

这个实现正是我需要的——只拷贝属性不拷贝数据,同时增加引用计数。

另外在pyopencv_cuda.hpp里还有CV_PY_FROM_CLASS(cuda::GpuMat);、CV_PY_TO_CLASS_PTR(cuda::GpuMat);的包装,定义如下:

#define CV_PY_FROM_CLASS(TYPE)           \
template<>                               \
PyObject* pyopencv_from(const TYPE& src) \
{                                        \
    Ptr<TYPE> ptr(new TYPE());           \
                                         \
    *ptr = src;                          \
    return pyopencv_from(ptr);           \
}

想请教两个问题:

  1. CV_PY_FROM_CLASS(cuda::GpuMat)是否会创建cv2.cuda_GpuMat?
  2. 能不能构造或修改持有cv2.cuda_GpuMat的PyObject的cv::cuda::GpuMat属性,从而利用GpuMat(const GpuMat& m)拷贝构造函数?

我已经查过OpenCV论坛的一个未解决类似问题(2020年)、cv::cuda::GpuMat官方文档、Python C API及数据模型文档。


解决方案

核心思路

要实现零拷贝将C++的cv::cuda::GpuMat转为Python的cv2.cuda_GpuMat,关键是复用OpenCV自身的Python绑定逻辑,而非让pybind11处理未注册的类型。

具体实现步骤

  1. 复用OpenCV的pyopencv_from转换函数
    CV_PY_FROM_CLASS(cuda::GpuMat)宏确实会生成转换cv::cuda::GpuMat到Pythoncv2.cuda_GpuMat的代码,但需要在pybind11模块中主动调用该转换逻辑,而非直接暴露gpu_mat成员。

    修改C++代码,给miniGpuMat添加返回Python可识别对象的方法:

    struct miniGpuMat{
      // ... 原有代码 ...
      py::object get_cuda_mat() {
          // 调用OpenCV的转换函数,生成cv2.cuda_GpuMat对应的Python对象
          PyObject* obj = pyopencv_from(gpu_mat);
          return py::reinterpret_borrow<py::object>(obj);
      }
    };
    
    PYBIND11_MODULE(pgxcam, m) {
      py::class_<miniGpuMat>(m, "miniGpuMat")
        .def(py::init<>())
        .def_readwrite("imageGpu", &miniGpuMat::imageGpu)
        .def_readwrite("imageBp", &miniGpuMat::imageBp)
        .def("get_cuda_mat", &miniGpuMat::get_cuda_mat) // 新增方法
        ;
    }
    
  2. 编译时链接OpenCV Python绑定库
    编译pybind11模块时,必须链接OpenCV的Python绑定库(如opencv_python3),确保能找到pyopencv_from的实现。CMake配置示例:

    find_package(OpenCV REQUIRED COMPONENTS core cudaimgproc cudaarithm python3)
    target_link_libraries(pgxcam PRIVATE ${OpenCV_LIBS} pybind11::module)
    
  3. Python端使用
    修改后的Python代码可直接获取零拷贝的cv2.cuda_GpuMat:

    import cv2  
    import pgxcam as xc    
    
    testMini = xc.miniGpuMat()
    testMini.getImage() # 初始化gpu_mat
    cuda_mat = testMini.get_cuda_mat()
    print(type(cuda_mat)) # 输出 <class 'cv2.cuda_GpuMat'>
    # 直接用OpenCV CUDA函数处理
    processed = cv2.cuda.cvtColor(cuda_mat, cv2.COLOR_BGR2GRAY)
    del(testMini)
    

关键说明

  • pyopencv_from会创建cv2.cuda_GpuMat对象,通过引用计数共享底层CUDA内存,无数据拷贝。
  • 不能直接让pybind11暴露cv::cuda::GpuMat成员,因为pybind11默认未注册该类型的转换规则,OpenCV自身维护了一套绑定逻辑。
  • 生命周期自动管理:Python的cv2.cuda_GpuMat会持有底层cv::cuda::GpuMat的引用,只要Python对象存在,C++端内存就不会被释放,避免悬空指针。

内容的提问来源于stack exchange,提问作者randy37773

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 08:10:20