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

如何从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包含坐标、尺度、角度等信息,你可以把它转换成结构体数组处理:

  1. 在C++里定义和OpenCV KeyPoint对齐的结构体
  2. 把features.keypoints转换成结构体数组,分配内存后输出指针和数量
  3. Python端用ctypes.Structure定义对应结构体,再用np.ctypeslib.as_array转换成数组或列表

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:21:09