将c/c++编译成动态链接库,通过python调用,实现常见变量比如int,字符串之间的交互,以及np.ndarray和cv::Mat间的交互.
新建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 <opencv2/opencv.hpp>
#include <string>
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个文件放到同一个目录,然后在命令行中进入到该目录,依次运行,会弹出图片,单击图片,按任意键即可
mkdir build
cd build
cmake ..
make
cd ..
python test.py