概述
几个月前项目中需要用到一个相机,工业厂商给的SDK很全而且文档和demo都有,但是只给了cpp的接口,但主程序的图像处理算法都用python+opencv写好了。因此,开始鼓捣将相机的SDK包裹成python的模块,用了几种方案,重构了几次,总结一下各方案的优劣。
该项目经历了这么三个技术方案:在py中包裹厂商给的.dll文件,用ctypes在py中生成c风格的数据结构,直接用CDLL调用给出的SDK(.dll文件),传入生成的数据类型。
在C中包裹.dll文件,直接用C中的数据类型,打包成.dll文件,再在py中通过ctypes调用并转换成py的数据结构。
用CPython,引入Python.h将cpp文件包裹成py的模块,直接在py中import,获得的也是py的数据类型。
所以C和python之间互相通信最基本的问题就是c中的数据类型和py中的数据类型互相交换的问题,如何转换的更有效率、更安全、可复用性更强,是python的c扩展的焦点。这三个方案对于这个问题的处理如下:方案一:所有的数据类型都在py中生成,ctypes生成c风格的数据类型传给c。
方案二:c中用c的数据类型,处理成几个简单的数据类型传给py,ctypes处理部分数据类型。
方案三:在c中生成py的数据类型,传给py。
第一个方案是最开始的方案,毕竟自己python用的熟练度比c要高很多,能在py域解决的自然会用py解决。甚至为了生成一堆SDK需要的structure,专门写了一个解析器将定义那堆structure的.h文件转换的ctypes定义的.py文件。但随着SDK开发进入到一定的程度,出现了很多ctypes也没法转换的数据类型。比如厂商的某个SDK必须传一个句柄进去,句柄这个东西已经超过了ctypes的能力范围,但你也不能怪厂商写的东西没解耦,毕竟人家写这个的时候可能只打算用到MFC上面去的。
第二个方案是方案一开发不下去的产物,也是用到项目里的方案,优势是可以利用c里面的数据类型,直接include厂商给的SDK中的.h文件就行,不用自己用ctypes构建一堆struct,直接在给的demo里修改出传给py的接口就行了。这个方案凑合着跑了很久,但是这个方案仍然不够pythonic。
第三个方案是现在的使用着的,include了Python.h和numpy提供的C-API。这一方案可以将c域里程序执行的异常raise到py域中,由py的try-except捕获到。同时,在数据转换的时候也更安全。不过,运行的时候界面比之前要卡一点,不知道为什么。
ctypes使用要点
ctypes的使用没什么难的,一句 from ctypes import * 就行了。平时很少看到import * 这一用法,因为容易出现命名冲突,但这里用起来到是很符合c语言的编程风格,把import用的像include似的。
ctypes中基本数据类型是在前面加个前缀“c_”,比如int是c_int,unsigned int是c_uint,不同的是这些在c里面是数据类型,在python里都是类,因此将类实例化成对象就行了。
ctypes中有指针操作,对一个c_int对象取指针用pointer(),然后.contents就是指向的内容,如果要创造指针类型,就用POINTER(c_int),这样实例化出来的就是指针类型。
cytpes中的数组格式是“数据类型*数组长度”,比如c_char*32就是生成了一个长度为32的char数组。
最后是structure,在ctypes中是继承Structure类的一个子类,在类属性__fields__中赋值一个定义结构体成员的列表,举例如下:
from ctypes import *
class MyStructure(Structure):
_fields_ = [("aint", c_int),
("puint", POINTER(c_uint)),
("charArray", c_char*32)]
生成的c文件需要将接口函数写到extern "C"里面,一些关键语句举例如下:
#ifdef EXPORT_MINDPY_DLL#define MINDPY_API __declspec(dllexport)#else#define MINDPY_API __declspec(dllimport)#endif
#include
#ifdef _WIN64//是否导入X64的SDK#pragma comment(lib, "..\SDK_X64.lib")#else#pragma comment(lib, "..\SDK.lib")#endif
extern "C"
{
MINDPY_API int GetArray();
}
编译为dll的命令为 cl /LD Mydll.cpp 其中/不要写成,LD区分大小写,还有cl.exe需要添加到环境变量中。
在生成numpy的数组时可以直接将ctypes生成的数组用numpy初始化成一个ndarray,具体代码如下:
mydll = CDLL('Mydll.dll')#调用生成的dll
ctypeArray = c_byte * arrayLen#定义一个arrayLen长度的c_byte类型数组
arget = ctypeArray()#实例化该数组
md = mydll.GetArray(arget, arrayLen)#这里的arget就是数组的头指针了
npArray = np.array(arget, dtype=np.uint8)#生成ndarray
感觉内容有点多,剩下的和Python.h和numpy有关的内容分到下一节更新吧。
最后
以上就是文静菠萝为你收集整理的python extension_Python C Extension (ctypes)的全部内容,希望文章能够帮你解决python extension_Python C Extension (ctypes)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复