1.问题描述
python相比C/C++,具有很多封装好的高级的API,极大地方便了平时的程序开发。但是有的时候我们需要的某个功能模块python里面并没有实现,但是C/C++下有现有的程序,那么一种方法就是用python调用C/C++的模块.下面举的例子为:
假设我需要计算任意两个四边形的交叠率(intersection over union),这个可以利用C++下的boost库很方便的实现,现在我需要在python环境下调用这个写好的C++函数
2.问题解决
2.1 将C++程序编译成动态链接库
假设当前目录下存在文件compute_iou.h以及compute_iou.cpp, 其中compute_iou.h如下所示:
#include <vector>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
typedef struct{
float x,y;
}MyPoint;
typedef boost::geometry::model::polygon<MyPoint> Polygon;
BOOST_GEOMETRY_REGISTER_POINT_2D(MyPoint, float, boost::geometry::cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_RING(Polygon::ring_type)
extern "C" float compute_box_iou(float x11,float x12,float x13,float x14,float y11,float y12,float y13,float y14,float x21,float x22,float x23,float x24,float y21,float y22,float y23,float y24,float r1,float r2,float c1,float c2);
需要注意的是,因为python实质上只能调用C的函数,所以我们要在C++函数前面加上extern C的标志
终端执行编译命令,生成.so文件
g++ -std=c++11 -o libcompute_iou.so -shared -fPIC compute_iou.cpp -L boost
2.2 python中调用动态链接库
python中使用模块ctypes来调用C的动态链接库,如下所示:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from ctypes import *
# load动态链接库
so = cdll.LoadLibrary('libcompute_iou.so')
py_iou = so.compute_box_iou
# 设置函数返回值的类型
py_iou.restype = c_float
# 设置传入函数的参数的类型
x11 = x12 = c_float(10)
x13 = x14 = c_float(20)
y11 = y14 = c_float(10)
y12 = y13 = c_float(20)
x21 = x22 = c_float(10)
x23 = x24 = c_float(20)
y21 = y24 = c_float(10)
y22 = y23 = c_float(20)
r1 = r2 = c1 = c2 = c_float(10)
# 调用函数计算iou
iou = py_iou(x11, x12, x13, x14, y11, y12, y13, y14, x21, x22, x23, x24, y21, y22, y23, y24, r1, r2, c1, c2)
print('iou = {}'.format(iou))
# 程序运行结果,两个相同的四边形,iou=1.0,结果正确
>>>
iou = 1.0
需要注意的是,我们在用ctypes调用动态链接库的时候,需要设置传入参数以及返回参数的类型,使其符合ctypes的类型要求,比如说,如果我们需要传递一个float类型的参数到函数,我们需要在python中设置变量的类型为ctypes.c_float,函数返回值的类型可以用restype来设置,这里我们希望返回的iou是float类型。ctype-C-python之间的类型对应表如下所示: