如何通过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
相关产品推荐
相关产品推荐

