如何从C函数返回Mat至Python并转为numpy.array(未知结果尺寸)
解决Python调用C++返回未知尺寸OpenCV Mat为numpy数组的问题
你遇到的核心问题是C++函数里的指针参数是传值传递,修改descriptors的指向只会改变函数内部的局部变量,根本没法把新分配的内存地址传递回Python。要解决这个问题,我们需要调整C++的接口,让它能把Mat的数据指针、尺寸、类型都输出给Python,然后在Python端用这些信息构建numpy数组。
第一步:修改C++函数接口
我们需要把返回的关键信息(数据指针、行数、列数、数据总大小)通过指针参数输出,同时保留原有的输入参数。另外,注意内存管理——因为我们用new分配了内存,后续需要提供一个释放内存的函数给Python调用,避免内存泄漏。
#include <opencv2/opencv.hpp> #include <cstring> using namespace cv; // 把Mat转成原始字节数组,返回指针,同时输出总字节数、尺寸和类型 uchar* MatToBytes(const Mat& image, int& out_total_bytes, int& out_rows, int& out_cols, int& out_type) { out_rows = image.rows; out_cols = image.cols; out_type = image.type(); out_total_bytes = image.total() * image.elemSize(); uchar* data = new uchar[out_total_bytes]; std::memcpy(data, image.data, out_total_bytes); return data; } // 辅助函数:释放C++分配的内存 void FreeBytes(uchar* data) { delete[] data; } static SurfFeaturesFinder sf; // 修改后的FindFeatures函数:输出数据指针、尺寸、类型 void FindFeatures(const uchar* image_uchar, int image_rows, int image_cols, int image_type, uchar** out_descriptors, int* out_total_bytes, int* out_rows, int* out_cols, int* out_type) { Mat img(image_rows, image_cols, image_type, (void*)image_uchar); ImageFeatures features; sf(img, features); Mat matDescriptor = features.descriptors.clone(); // 把Mat转成字节数组,输出所有必要信息 *out_descriptors = MatToBytes(matDescriptor, *out_total_bytes, *out_rows, *out_cols, *out_type); }
关键修改说明:
- 把
descriptors参数改成uchar** out_descriptors:通过二级指针,让我们能把C++分配的内存地址传递回Python - 新增输出参数
out_total_bytes、out_rows、out_cols、out_type:告诉Python返回数据的尺寸和类型,用来构建numpy数组 - 新增
FreeBytes函数:Python用完数据后必须调用它释放C++分配的内存,防止泄漏
第二步:修改Python调用代码
在Python端,我们需要先定义好C++函数的参数类型,调用函数后获取返回的指针和尺寸信息,再用numpy.frombuffer把原始字节数据转换成numpy数组。注意要指定正确的数据类型,根据OpenCV的Mat类型对应numpy的dtype。
import numpy as np from ctypes import cdll, c_uchar, c_int, POINTER, c_size_t # 加载你的DLL comDll = cdll.LoadLibrary("your_dll_path.dll") # 定义FindFeatures函数的参数和返回值类型 comDll.FindFeatures.argtypes = [ POINTER(c_uchar), # 输入图像数据 c_int, # 输入图像行数 c_int, # 输入图像列数 c_int, # 输入图像类型 POINTER(POINTER(c_uchar)), # 输出描述符数据指针(二级指针) POINTER(c_int), # 输出总字节数 POINTER(c_int), # 输出描述符行数 POINTER(c_int), # 输出描述符列数 POINTER(c_int) # 输出描述符类型 ] comDll.FindFeatures.restype = None # 定义释放内存的函数类型 comDll.FreeBytes.argtypes = [POINTER(c_uchar)] comDll.FreeBytes.restype = None def Utils_FindFeatures(image): # 准备输入数据:转成C连续的uint8数组 img_data = image.astype(np.uint8).ctypes.data_as(POINTER(c_uchar)) img_rows = c_int(image.shape[0]) img_cols = c_int(image.shape[1]) img_type = c_int(0) # 根据你的实际图像类型传入,比如CV_8UC1对应0 # 准备输出参数的容器 out_descriptors = POINTER(c_uchar)() out_total_bytes = c_int(0) out_rows = c_int(0) out_cols = c_int(0) out_type = c_int(0) # 调用C++函数 comDll.FindFeatures( img_data, img_rows, img_cols, img_type, POINTER(POINTER(c_uchar))(out_descriptors), POINTER(c_int)(out_total_bytes), POINTER(c_int)(out_rows), POINTER(c_int)(out_cols), POINTER(c_int)(out_type) ) # 根据OpenCV类型映射numpy dtype(示例,可根据实际需求扩展) dtype_map = { 5: np.float32, # CV_32F,SURF描述符常用类型 0: np.uint8, # CV_8UC1 2: np.uint16, # CV_16UC1 6: np.float64, # CV_64F } dtype = dtype_map.get(out_type.value, np.uint8) # 把C++的字节数据转换成numpy数组 descriptors = np.frombuffer( np.ctypeslib.as_array(out_descriptors, shape=(out_total_bytes.value,)), dtype=dtype ).reshape(out_rows.value, out_cols.value) # 注意:numpy数组直接引用C++内存,若需长期保存建议先拷贝再释放原内存 # descriptors_copy = descriptors.copy() # comDll.FreeBytes(out_descriptors) # return descriptors_copy # 返回数组和指针,方便后续手动释放内存 return descriptors, out_descriptors # 使用示例 # import cv2 # img = cv2.imread("test.jpg", 0) # desc, ptr = Utils_FindFeatures(img) # # 使用desc进行后续处理... # comDll.FreeBytes(ptr) # 用完必须释放内存,避免泄漏
Python端关键说明:
- 使用
POINTER(POINTER(c_uchar))接收C++的二级指针,这样才能拿到新分配的内存地址 - 通过
np.frombuffer把C++的原始字节数据转换成numpy数组,必须匹配OpenCV Mat对应的numpy dtype - 内存管理是重中之重:numpy数组会直接引用C++分配的内存,必须在数组不再使用后调用
FreeBytes释放;如果需要长期保存数组,建议先调用copy()创建Python管理的副本,再释放原内存
关于后续KeyPoint的处理思路
KeyPoint包含坐标、尺度、角度等信息,你可以把它转换成结构体数组处理:
- 在C++里定义和OpenCV KeyPoint对齐的结构体
- 把
features.keypoints转换成结构体数组,分配内存后输出指针和数量 - Python端用
ctypes.Structure定义对应结构体,再用np.ctypeslib.as_array转换成数组或列表
内容的提问来源于stack exchange,提问作者sergman
相关产品推荐
相关产品推荐

