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圖片數據

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