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

如何通过ctypes实现Python Numpy数组与C++ OpenCV Mat互转及调用

使用ctypes调用C++ OpenCV函数处理Numpy数组

核心思路

Numpy数组和OpenCV的cv::Mat在C连续内存布局下可直接兼容,无需额外拷贝数据,只需正确构造cv::Mat对象;处理完成后,将cv::Mat结果直接写回Python预先分配的Numpy数组即可。


C++端实现

编写opencv_dot函数,完成Numpy数组到cv::Mat的转换、矩阵运算、结果写回的逻辑:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <cstring>

using namespace cv;

extern "C" {
    // 参数说明:
    // a_data: 第一个输入数组的指针(float64类型)
    // a_rows, a_cols: 第一个数组的行数、列数
    // b_data: 第二个输入数组的指针
    // b_rows, b_cols: 第二个数组的行数、列数
    // result_data: 输出数组的指针(Python端预先分配)
    void opencv_dot(const double* a_data, size_t a_rows, size_t a_cols,
                    const double* b_data, size_t b_rows, size_t b_cols,
                    double* result_data) {
        // 1. 将Numpy数组映射为cv::Mat(无数据拷贝,共享内存)
        Mat mat_a(a_rows, a_cols, CV_64F, (void*)a_data);
        Mat mat_b(b_rows, b_cols, CV_64F, (void*)b_data);

        // 2. 执行OpenCV矩阵乘法操作
        Mat mat_result;
        gemm(mat_a, mat_b, 1.0, Mat(), 0.0, mat_result);

        // 3. 将结果写回Python分配的输出数组
        if (mat_result.isContinuous()) {
            memcpy(result_data, mat_result.data, mat_result.total() * mat_result.elemSize());
        } else {
            // 处理非连续矩阵的鲁棒性逻辑(本例中乘法结果为连续,可省略)
            Mat temp_continuous = mat_result.clone();
            memcpy(result_data, temp_continuous.data, temp_continuous.total() * temp_continuous.elemSize());
        }
    }
}

编译命令

使用g++编译为共享库:

g++ -shared -fPIC cpp_function.cpp -o cpp_function.so -I /usr/include/opencv4/ -L /usr/lib -lopencv_core -lopencv_imgproc

Python端实现

修正原代码的参数错误,预先分配输出数组,调用C++函数填充结果:

import numpy as np
import ctypes

# 加载编译好的共享库
libmatmult = ctypes.CDLL("./cpp_function.so")

# 定义函数参数类型和返回类型
libmatmult.opencv_dot.argtypes = [
    np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags="C"),  # 输入矩阵a
    ctypes.c_size_t, ctypes.c_size_t,                             # a的行数、列数
    np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags="C"),  # 输入矩阵b
    ctypes.c_size_t, ctypes.c_size_t,                             # b的行数、列数
    np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags="C")   # 输出结果数组
]
libmatmult.opencv_dot.restype = None

def opencv_mat(a, b):
    # 校验矩阵乘法维度合法性
    if a.shape[1] != b.shape[0]:
        raise ValueError("矩阵a的列数必须等于矩阵b的行数")
    
    # 预先分配输出数组(尺寸为a行 × b列)
    result = np.zeros((a.shape[0], b.shape[1]), dtype=np.float64, order="C")
    
    # 调用C++函数
    libmatmult.opencv_dot(a, a.shape[0], a.shape[1],
                          b, b.shape[0], b.shape[1],
                          result)
    
    return result

# 测试验证
if __name__ == "__main__":
    a = np.random.uniform(1, 255, (5, 5)).astype(np.float64)
    b = np.random.uniform(1, 255, (5, 5)).astype(np.float64)
    
    custom_result = opencv_mat(a, b)
    numpy_result = np.dot(a, b)
    
    print("结果最大误差:", np.max(np.abs(custom_result - numpy_result)))

关键注意事项

  • 内存共享效率:构造cv::Mat时直接复用Numpy数组的内存指针,避免数据拷贝,大幅提升性能;需确保Python数组在C++函数执行期间不会被垃圾回收。
  • 内存管理安全:由Python端分配输出数组,C++仅负责填充数据,避免跨语言内存泄漏问题。
  • 数据类型匹配:Numpy的float64对应OpenCV的CV_64F,必须严格匹配,否则会出现数据错乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:45:28