ctypes实现numpy和OpenCV Mat之间的数据交互

1、目的

将c/c++编译成动态链接库,通过python调用,实现常见变量比如int,字符串之间的交互,以及np.ndarray和cv::Mat间的交互. 更简单的方式参考:pybind11实现numpy和OpenCV Mat的数据交互-CSDN博客

2 步骤

新建CMakeLists.txt,如下,需要安装opencv,安装教程参考Ubuntu 18.04 安装opencv4.2.0,如果遇到IPPICV问题参考解决编译opencv时,卡在IPPICV

cmake_minimum_required(VERSION 3.10)
project(test)
add_definitions(-std=c++11)
add_definitions(-DAPI_EXPORTS)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
find_package(OpenCV)
include_directories(${OpenCV_INCLUDE_DIRS})
add_library(myplugins SHARED ${PROJECT_SOURCE_DIR}/test.cpp)
target_link_libraries(myplugins ${OpenCV_LIBS})

新建test.cpp

#include 
#include 
using namespace std;

#if defined(_MSC_VER)
#define API __declspec(dllimport)
#else
#define API
#endif  // API_EXPORTS

typedef struct test
{
    /* data */
    int ages;
    std::string name;
    cv::Mat pic;
} Person;

extern "C" API void * init(int age_,  char * name_ptr, unsigned char *src_data, int rows, int cols)
{
    Person *handle = new Person();
    std::string name_ = name_ptr;
    handle->ages = age_;
    handle->name = name_;
    handle->pic = cv::Mat(rows, cols, CV_8UC3, src_data);
    return (void*) handle;
}

extern "C" API int get_ages(void* handle_)
{
    Person * handle = (Person*)handle_;
    return handle->ages;
}

extern "C" API char* get_name(void* handle_)
{
    Person * handle = (Person*)handle_;
    return (char*)handle->name.c_str();
}

extern "C" API void get_pic(void* handle_, int rows, int cols, unsigned char* out_data)
{
    Person * handle = (Person*)handle_;
    cv::imshow("raw", handle->pic);
    cv::waitKey();
    //数据处理
    //....
    
    //返回结果
    memcpy(out_data, handle->pic.data, rows*cols*3);
}

新建test.py,具体转换见代码注释

from ctypes import *
import cv2
import numpy as np
import numpy.ctypeslib as npct

#加载链接库
lib = CDLL('./build/libmyplugins.so')

#定义函数的输入类型和输出类型
lib.get_ages.restype = c_int
lib.get_ages.argtypes = [c_void_p] #输入句柄(指针)
lib.get_name.restype = c_void_p #返回char*
lib.get_name.argtypes = [c_void_p] #输入句柄(指针)

img = cv2.imread("oil-bin-1.png") #修改成自己的图片
ages = 23
name = bytes("kitty", "utf-8") #要对应c函数中的char*,需要转为二进制

(rows, cols) = (img.shape[0], img.shape[1])
image_type = npct.ndpointer(dtype = np.uint8, ndim = 3, shape = img.shape, flags="C_CONTIGUOUS") #指定numpy图片的格式

lib.init.restype = c_void_p #返回句柄(指针)
lib.init.argtypes = [c_int, c_void_p, image_type, c_int, c_int]

#将np格式的图片传递到c
hd = lib.init(c_int(ages), c_char_p(name), img, c_int(rows), c_int(cols))

print(lib.get_ages(hd)) #返回整数,直接打印
name = c_char_p(lib.get_name(hd)).value #返回char*, 需要转换
print(name.decode('utf-8')) #解码成字符串

##第一种读取图片的方式,使用ndpointer指定图片类型
out_image = np.zeros_like(img).astype(np.uint8) #需要先开辟内存
lib.get_pic.argtypes = [c_void_p, c_int, c_int, image_type]
lib.get_pic(hd, c_int(rows), c_int(cols), out_image)
cv2.imshow("out1", out_image)

##第二种读取图片的方式,不使用ndpointer指定图片类型
out_image = np.zeros_like(img).astype(np.uint8) #需要先开辟内存
lib.get_pic.argtypes = [c_void_p, c_int, c_int, c_void_p] #句柄(指针),rows,cols, 图片指针
lib.get_pic(hd, c_int(rows), c_int(cols), out_image.ctypes.data_as(POINTER(c_ubyte)))

cv2.imshow("out2", out_image)
cv2.waitKey()

3、测试

将上述3个文件放到同一个目录,然后在命令行中进入到该目录,依次运行,会弹出图片,单击图片,按任意键即可

mkdir build
cd build
cmake ..
make
cd ..
python test.py

你可能感兴趣的:(python,ctypes,numpy转cv,Mat,c/c++)