Python使用ctypes调用动态库dll/so,关于opencv图片Mat对应的数据uchar*

官方给的定义是 “ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.” —— 引自 Python 3.5 chm 文档。其大意就是——ctypes 是一个为 Python 准备的外部函数库。它提供兼容C的数据类型,并允许调用DLL或共享库中的函数。通过它,可以使用纯粹的 Python 包装这些函数库(这样你就可以直接 import xxx 来使用这些函数库了)。
python 可以通过使用 ctypes 模块调用 c 函数,这其中必定包括可以定义 c 的变量类型(包括结构体类型、指针类型)。

一、python通过ctypes 加载 c 动态库

使用 ctypes.CDLL ,其定义如下(引自 Python 3.5 chm 文档 )

ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)  

另外,在 windows 平台上会忽略 modes 参数。对于 windows 平台来说还可以调用 ctypes.WinDLL,与上面的 CDLL 几乎一样,唯一不同点是它假定库中函数遵循 Windows stdcall 调用约定,其他参数的意义见官方文档。
如果要调用libopencv_core.so可以写作 :

from ctypes import *
SO = cdll.LoadLibrary("/usr/lib/libopencv_core.so")#windows和linux通用
DLL = windll.LoadLibrary("x64/Release/opencv_core320.dll")#windows下还可以使用

**注意:**在windows下调用dll,如果依赖其他第三方库,则要一起写进来,特别要写在对应库前面:
比如下面这样就会报错:

from ctypes import *
cdll.LoadLibrary("x64/Release/opencv_imgproc320.dll")
cdll.LoadLibrary("x64/Release/opencv_core320.dll")	#windows下还可以使用

在这里插入图片描述
如果调换顺序就可以正常通过:

from ctypes import *
cdll.LoadLibrary("x64/Release/opencv_core320.dll")
cdll.LoadLibrary("x64/Release/opencv_imgproc320.dll")	#windows下还可以使用

所以如果出现上诉错误,就要查看一下自己还缺什么库,顺序自己慢慢测试了。

二、ctypes 怎么样调用 c 的函数库

ctype只支持C标准,所以一些C++的标准是不能用的,比如函数重载。
按照下面的方式,我们就定义了两个函数,我们需要注意这些函数的输入和输出,这样方便python调用。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int flag;
// 外部申明
extern "C"{
	int flag_add(int a) 
	{
		flag += a;
		return flag;
	}
	int LPR_InitEx(const  char *pbyRootPath);
    char* mattostring(uchar* matrix, int rows, int cols, int channels)
    {
        ....
		//省略
    }
    
}

python调用.dll/.so中的函数

在 ctypes 读取 so/dll 时只知道存在这个函数,但是并不知到函数的形参类型和返回值的类型。
两个属性 restype 和 argtypes 赋值了,它们分别对应返回类型和参数类型:

from ctypes import *
import cv2
soFile = 'XXX.so'
MYDLL= cdll.LoadLibrary(soFile)

MYDLL.flag_add.argtypes = (c_int,) # 写明flag_add的输入类型,一定要加“,”号
MYDLL.flag_add.restype = (c_int,) # 写明flag_add的返回类型

MYDLL.LPR_InitEx.argtypes=(c_char_p,)
MYDLL.LPR_InitEx.restype = c_int	

MYDLL.mattostring.argtypes = (POINTER(ctypes.c_ubyte), c_int,c_int,c_int) 
MYDLL.mattostring.restype = (c_void_p, )

//举例调用mattostring函数
img = cv2.imread('image.jpg')
cols = img.shape[1]
rows = img.shape[0]
channels = 0
if 3==len(img.shape):
	channels = 3
pubyIm = img.ctypes.data_as(POINTER(c_ubyte))
ret = MYDLL.mattostring(pubyIm, rows, cols, channels)

如果函数的返回值是 void 那么你可以赋值为 None,至于其他的类型可以查看下面的对应表格

C数据类型与ctypes之间的转换表格:

在这里插入图片描述

三、结构体对应

如果在c中定义了结构体

#define _NL_TC_MAX 20
#define _NL_TC_NAME_MAX 100
typedef struct NLDJ_TC_Out
{
	int dwNum;
	float fScales;
	char model[128];
	char dsClassName[_NL_TC_MAX][_NL_TC_NAME_MAX];
} NLDJ_TC_Out;

则python中要定义成class

class Struct_NLDJ_TC_Out(Structure):
    _fields_ = [("dwNum", c_int),("fScales", c_float), ("model", c_char*128),("dsClassName",c_char * 100 *20)]

//声明一个结构体变量   
djTCVarOut = Struct_NLDJ_TC_Out() 

特别注意:二维数组的时候要倒着写维度

四、指针

函数 说明
byref(x [, offset]) 返回 x 的地址,x 必须为 ctypes 类型的一个实例。相当于 c 的 &x 。 offset 表示偏移量。
pointer(x) 创建并返回一个指向 x 的指针实例, x 是一个实例对象。
POINTER(type) 返回一个类型,这个类型是指向 type 类型的指针类型, type 是 ctypes 的一个类型。

byref 很好理解,传递参数的时候就用这个,用 pointer 创建一个指针变量也行,不过 byref 更快。
而 pointer 和 POINTER 的区别是,pointer 返回一个实例,POINTER 返回一个类型。甚至你可以用 POINTER 来做 pointer 的工作:

>>> a = c_int(66)         # 创建一个 c_int 实例
>>> b = pointer(a)        # 创建指针
>>> c = POINTER(c_int)(a) # 创建指针
>>> b
<__main__.LP_c_long object at 0x00E12AD0>
>>> c
<__main__.LP_c_long object at 0x00E12B20>
>>> b.contents            # 输出 a 的值
c_long(66)
>>> c.contents            # 输出 a 的值
c_long(66)

其实前面我们已经有用到了POINTER,看二部分的代码

五、opencv中的Mat

Mat并不是c的语法,uchar是,所以一般有两种方式进行转换uchar
第一种:

pubyIm = img.ctypes.data_as(POINTER(c_ubyte))  

第二种:

pubyIm = img.astype(np.uint8).tostring()   

第一种前面案例已经用到过了,而第二种借助numpy来进行转换,两者的区别就是,第一种传的是指针,如果参数进去,在mattostring函数内对变量pubyIm进行修改则会影响最终输出的内容,第二种方式不会有影响。

主要参考:

windows下python调用含有opencv Mat类型的dll文件的方法
python ctypes 探究 ---- python 与 c 的交互
python 与c++动态库之间传递opencv图片数据

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章