如何通过ctypes将C++端OpenCV3的cv::Mat转为Python的Numpy数组并显示?
用ctypes将C++ OpenCV的cv::Mat转为Python Numpy数组并显示
我来帮你搞定这个跨语言图像转换的问题!结合你提到的两个导出函数,咱们分步骤实现,避开那些容易踩的坑。
第一步:确认C++端导出函数的正确性
首先要确保你的C函数是C语言调用约定导出的,不然ctypes会找不到函数名(C的名字修饰会搞乱函数名)。另外,建议把cv::Mat的type也返回,因为Numpy需要知道数据类型和通道数才能正确解析。
示例C++代码(适配你的共享内存逻辑):
#include <opencv2/opencv.hpp> #include <cstdint> // 假设这是你从共享内存读取到的图像,要确保它在Python使用期间不会被释放 cv::Mat g_shared_mat; // 用extern "C"导出C风格函数,避免名字修饰 extern "C" { // 定义存储cv::Mat属性的结构体 struct MatProperties { int rows; // 图像高度 int cols; // 图像宽度 size_t step; // 每行字节数(含对齐) int mat_type; // cv::Mat的type,比如CV_8UC3 }; // 导出获取属性的函数 __declspec(dllexport) MatProperties get_mat_properties() { MatProperties props; props.rows = g_shared_mat.rows; props.cols = g_shared_mat.cols; props.step = g_shared_mat.step; props.mat_type = g_shared_mat.type(); return props; } // 导出获取数据指针的函数 __declspec(dllexport) uint8_t* get_mat_data() { return g_shared_mat.data; } }
注意:
__declspec(dllexport)是Windows下的导出标记,Linux/macOS下编译时加上-fPIC -shared参数即可,不需要这个标记。
第二步:Python端实现转换与显示
在Python里,我们用ctypes对接C++函数,再用Numpy直接映射C内存(避免拷贝,提升效率),最后显示图像。
示例Python代码:
import ctypes import numpy as np import cv2 # 1. 定义与C++对应的结构体类型 class MatProperties(ctypes.Structure): _fields_ = [ ("rows", ctypes.c_int), ("cols", ctypes.c_int), ("step", ctypes.c_size_t), ("mat_type", ctypes.c_int) ] # 2. 加载共享库(Windows用.dll,Linux用.so,macOS用.dylib) lib = ctypes.CDLL("./your_shared_lib.so") # 替换成你的库路径 # 3. 声明函数的返回类型,让ctypes正确解析返回值 lib.get_mat_properties.restype = MatProperties lib.get_mat_data.restype = ctypes.POINTER(ctypes.c_uint8) # 4. 获取图像属性和数据指针 props = lib.get_mat_properties() data_ptr = lib.get_mat_data() # 5. 将C内存映射为Numpy数组(无拷贝,直接使用原内存) # 先创建一个ctypes数组,再转成Numpy数组 total_bytes = props.step * props.rows c_buffer = ctypes.cast(data_ptr, ctypes.POINTER(ctypes.c_uint8 * total_bytes))[0] img_raw = np.frombuffer(c_buffer, dtype=np.uint8) # 6. 根据cv::Mat的type解析出正确的图像形状和通道数 # 从mat_type中提取深度和通道数 depth = props.mat_type & 0xFF channels = 1 + (props.mat_type >> 8) & 0xFF # 处理不同通道的情况 if channels == 1: # 单通道:直接截取每行有效宽度(排除对齐的额外字节) img_np = img_raw.reshape((props.rows, props.step))[:, :props.cols] elif channels == 3: # 三通道:先截取有效字节,再转成(height, width, 3)的形状 valid_bytes = props.cols * channels img_np = img_raw.reshape((props.rows, props.step))[:, :valid_bytes].reshape((props.rows, props.cols, channels)) # 如果用cv2.imshow可以跳过这个转换;如果用matplotlib显示,需要转BGR到RGB img_np = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB) # 7. 显示图像 cv2.imshow("Shared Memory Image", img_np) cv2.waitKey(0) cv2.destroyAllWindows()
关键注意事项
- 内存生命周期:必须保证Python使用图像期间,C++端的
g_shared_mat不能被释放或覆盖,否则Python里的Numpy数组会变成野指针,导致程序崩溃或显示乱码。 - 数据类型适配:如果你的cv::Mat不是8位无符号类型(比如CV_16UC1或CV_32FC3),需要修改Numpy的
dtype:比如np.uint16对应16位无符号,np.float32对应32位浮点。 - 字节对齐:OpenCV的
step可能比cols*channels大(为了内存对齐提升效率),所以必须用step*rows计算总字节数,再截取有效部分,否则图像会出现错位。
内容的提问来源于stack exchange,提问作者Frey
相关产品推荐
相关产品推荐

