[Advanced Python] Call C++ libraries
导入 C++,若干话题:
Python调用 .so
传参类型转化表
图片的传入传出
python调用C++中的类
Call C++
调用动态库 .so
From: Python调用Linux下的动态库(.so)
(1) 生成.so:.c to .so
lolo@-id:workme$ gcc -Wall -g -fPIC -c linuxany.c -o linuxany.o
lolo@-id:workme$ ls linux linuxany.c linuxany.o lolo@-id:workme$ gcc -shared linuxany.o -o linuxany.so
lolo@-id:workme$ ls libmax.so linux linuxany.c linuxany.o linuxany.so
(2) 调用.so:Python call .so
#!/usr/bin/python from ctypes import * import os
//参数为生成的.so文件所在的绝对路径 libtest = cdll.LoadLibrary(os.getcwd() + '/linuxany.so')
//直接用方法名进行调用 print libtest.display('Hello,I am linuxany.com') print libtest.add(2,2010)
(3) 可能遇到的问题:
version `GLIBC_2.27' not found
Download updated version from: https://mirror.freedif.org/GNU/libc/
传参转化表
Ref: python调用动态链接库的基本过程【链接写的不错】
- 类型转化表
python传参给C函数时,可能会因为python传入实参与C函数形参类型不一致会出现问题( 一般int, string不会有问题,float要注意 )
- Python [list] --> C [array]
提前把array传入,然后在C函数中修改。
import ctypes
pyarray = [1, 2, 3, 4, 5] carrary = (ctypes.c_int * len(pyarray)) (*pyarray) print so.sum_array(carray, len(pyarray))
返回值,其实一样道理,只是:返回时再把 c array 转换为 np.array
pyarray = [1,2,3,4,5,6,7,8] carray = (ctypes.c_int*len(pyarray))(*pyarray) so.modify_array(carray, len(pyarray))
print np.array(carray) // <----
- 形参方式
"传参" 前定义函数接口。
Ref: Python OpenCV pass mat ptr to c++ code
import ctypes import numpy as np from numpy.ctypeslib import ndpointer
pyarray = np.array([1,2,3,4,5,6,7,8], dtype="int32") # numpy中的数据类型指定很重要,即dtype的设定
so = ctypes.CDLL('./sum.so')
fun = so.modify_array
/* 告知对方,将要传递怎么样的参数 */ fun.argtypes = [ndpointer(ctypes.c_int), ctypes.c_int] fun.restype = None
fun(pyarray, len(pyarray))
print( np.array(pyarray) )
图片的传入传出
- 内外的格式不同
Python numpy image 转换为 C pointer 的方法。
所以,一定要确保numpy image是numpy array数据类型
image = cv2.imread("xxx.jpg");
- 强制转化保证格式安全
(1) Crop之后的格式data有点问题,例如:
image = whl_img[y1:y2, x1:x2]
crop之后的numpy image的type虽然也为numpy array,但实际传入的image data却不正确
(2) 统一解决方案:
image = numpy.array(image)
- 一个具体的例子
可见,即使参数解决,返回值依然是个问题。返回值也通过 uint8_t* 处理,注意代表的内存段最好不会被自动回收,例如:static。
static thread_local Mat tag;
ImageWrapper ScanBuff(uint8_t* data, size_t Width, size_t Height) { Mat OldFrame= Mat(Height, Width, CV_8UC3, data);
... ...
}
一些有参考价值的代码。
#!/usr/bin/python #import os import cv2 import numpy as np import numpy.ctypeslib as npct from ctypes import * import ctypes import numpy as np from numpy.ctypeslib import ndpointer objectPath = './lolo.bmp' img = cv2.imread(objectPath) img_row = img_height = img.shape[0] img_col = img_width = img.shape[1] print('img_row: %d, img_col: %d' % (img_row, img_col) ) ################################################################################## # https://stackoverflow.com/questions/37073399/python-opencv-pass-mat-ptr-to-c-code # https://www.cnblogs.com/fariver/p/6573112.html
# (1) 定义这是一个怎样的指针 ucharPtr = npct.ndpointer(dtype=np.uint8, ndim=1, flags='CONTIGUOUS')
# (2) 加载动态库和函数 CONST_LIB_PATH = '../lib/linux/libtagdetect.so' so = cdll.LoadLibrary( CONST_LIB_PATH ) #fun = so.ScanBuffer fun = so['ScanBuffer']
# (3) 定义参数的类型 fun.argtypes = [ucharPtr, ctypes.c_int, ctypes.c_int] fun.restype = None # (4) 自定义一个符合条件的fake image in_image = np.zeros( (img_row, img_col), np.uint8, order='C' ).ravel() print(in_image.shape)
# (5) 执行动态库内的函数 fun(in_image, img_width, img_height)
调用C++中的类
因为python不能直接调用C++中的类,所以必须把C++中的类转换为C的接口
转换原则
-
- 所有的C++关键字及其特有的使用方式均不能出现在.h文件里,.h中仅有C函数的包装函数声明
- 在class.cpp中实现对类的成员函数接口转换的函数,包括对类内成员的读写函数get() and set()
- 如果要在包装函数中要实例化对象,尽量用new constructor()的将对象的内存实例化在堆中,否则对象会被析构
- 记得在所有包含函数声明的文件中加入以下关键字,声明该函数为C函数,否则该函数的符号不会记录在二进制文件中
#ifdef __cplusplus extern "C" { #endif xxxxxx function declaration xxxxx #ifdef __cplusplus } #endif
End.