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

使用SWIG 4对接OpenCV 4.1.2:Python向C++传图像的类型适配问题

问题:OpenCV C++库通过opencv-swig生成Python绑定后参数类型不匹配及对象兼容性问题

初始错误场景

调用Python绑定模块时出现参数类型不匹配错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/config/.local/lib/python3.10/site-packages/example.py", line 12422, in opencv_receiver
    return _libReconDMI.opencv_receiver(image)
TypeError: in method 'opencv_receiver', argument 1 of type 'cv::Mat'

对应的C++实现:

#include "opencv2/core.hpp"

void opencv_receiver(cv::Mat image){
    (void)image;
}

当前使用OpenCV 4.1.2 + SWIG 4.0.2,存在两个疑问:

  1. opencv-swig是否需要指定其他类型来接收图像?
  2. 有无兼容新版OpenCV的替代方案(例如用numpy.i接收numpy数组再转为cv::Mat)?

更新问题(2024/06/05)

模块生成的cv::Mat在Python中为<example.Mat; proxy of <Swig Object of type 'cv::Mat *' at 0x7f4b1b3e31e0> >,但无法用cv2的threshold函数处理,报错:

>>> ret2, reference_image_bin_1 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
cv2.error: OpenCV(4.9.0) :-1: error: (-5:Bad argument) in function 'threshold'
> Overload resolution failed:
>  - src is not a numpy array, neither a scalar
>  - Expected Ptr<cv::UMat> for argument 'src'

改为cv::UMat也无法解决,说明类型与Python端OpenCV的对象不匹配。


解决方案

1. opencv-swig的类型适配问题

  • 必须确保SWIG接口文件中正确引入opencv-swig的接口定义:添加%include "opencv.i",同时配置好OpenCV头文件的搜索路径,让SWIG能正确解析cv::Mat类型。
  • 注意:opencv-swig生成的cv::Mat代理对象和Python cv2库生成的numpy数组/UMat是不同类型,直接传递cv2的对象到swig绑定的函数会触发类型不匹配错误,必须传递opencv-swig自己创建的cv::Mat对象。

2. 兼容新版OpenCV的替代方案:使用numpy.i实现numpy数组与cv::Mat互转

这是更通用的跨版本方案,能直接让Python端的cv2 numpy数组和C++的cv::Mat无缝交互:

步骤1:配置SWIG接口文件

在.i接口文件中引入numpy支持,并初始化numpy:

%include "numpy.i"
%init %{
import_array();
%}

步骤2:编写类型映射(typemap)

添加numpy array转cv::Mat的输入类型映射,让SWIG自动完成转换:

%typemap(in) cv::Mat {
    if (!PyArray_Check($input)) {
        PyErr_SetString(PyExc_TypeError, "参数必须是numpy数组");
        return NULL;
    }
    PyArrayObject* arr = (PyArrayObject*)$input;
    
    // 匹配numpy dtype和OpenCV的Mat类型
    int depth;
    switch(PyArray_TYPE(arr)) {
        case NPY_UBYTE:  depth = CV_8U; break;
        case NPY_INT16:  depth = CV_16S; break;
        case NPY_FLOAT32: depth = CV_32F; break;
        case NPY_FLOAT64: depth = CV_64F; break;
        default:
            PyErr_SetString(PyExc_TypeError, "不支持的numpy数据类型");
            return NULL;
    }
    
    int channels = PyArray_NDIM(arr) == 3 ? PyArray_SHAPE(arr)[2] : 1;
    int cv_type = CV_MAKETYPE(depth, channels);
    
    // 直接复用numpy数组的内存创建cv::Mat(无拷贝,需注意内存生命周期)
    $1 = cv::Mat(
        PyArray_SHAPE(arr)[0], 
        PyArray_SHAPE(arr)[1], 
        cv_type, 
        PyArray_DATA(arr), 
        PyArray_STRIDES(arr)[0]
    );
}

如果需要从C++返回cv::Mat到Python并自动转为numpy数组,可添加输出类型映射:

%typemap(out) cv::Mat {
    int depth = $1.depth();
    int npy_type;
    switch(depth) {
        case CV_8U:  npy_type = NPY_UBYTE; break;
        case CV_16S: npy_type = NPY_INT16; break;
        case CV_32F: npy_type = NPY_FLOAT32; break;
        case CV_64F: npy_type = NPY_FLOAT64; break;
        default:
            PyErr_SetString(PyExc_TypeError, "不支持的OpenCV Mat类型");
            return NULL;
    }
    
    int ndim = $1.channels() > 1 ? 3 : 2;
    npy_intp dims[3] = { $1.rows, $1.cols, $1.channels() };
    
    PyObject* arr = PyArray_SimpleNewFromData(
        ndim, 
        dims, 
        npy_type, 
        $1.data
    );
    // 设置numpy数组的所有权,避免C++释放内存后Python访问出错
    PyArray_SetBaseObject((PyArrayObject*)arr, NULL);
    $result = arr;
}

效果

修改后,Python端可以直接传递cv2读取/生成的numpy数组到C函数,同时C返回的cv::Mat会自动转为numpy数组,可直接用cv2的函数处理,解决类型不兼容问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 18:18:09