通过在C++程序调用Python3所提供的C接口可以实现调用Python程序所实现的功能。在C++调用深度学习训练好的模型时,如果不使用一些部署手段,这种C++调用Python接口的方式虽然大大牺牲了效率,但是可以说是也是一种取巧的方法。这里记录一下C++如何去调用Python3的接口,作为一个总结。
操作系统:Ubuntu16.04
构建工具:CMake
要想在 C++ 中调用 Python3 ,必须在 Cmake 中添加一些 Python3 的动态链接库:
可以在 include_directories中加入:
/usr/include/python3.5
在 target_link_libraries 中加入:
/usr/lib/x86_64-linux-gnu/libboost_python-py35.so
/usr/lib/x86_64-linux-gnu/libstdc++.so.6
/usr/lib/x86_64-linux-gnu/libpython3.5m.so
1. 调用Python3中的函数
int
代码中值得注意的是有很多固定写法:
Py_Initialize(): Python的初始化
PyRun_SimpleString:执行简单的Python语句
Py_DecRef`: 释放资源
Py_Finalize: 结束Python
其中`../scripts`是存放Python代码的路径,路径是相对于c++ bin文件的路径而言。
`PyImport_ImportModule("testFunction");`调用了名为 `testFunction` 的 py 程序。
PyObject *pFunction = PyObject_GetAttrString(pModule, "func2");
得到python程序中的 函数`func`的PyObjet指针 。
PyObject *args = PyTuple_New(1);
PyObject *args1 = PyUnicode_FromString("../air.jpg");
PyTuple_SetItem(args, 0, args1);
这里,是封装函数参数,放到一个Py元组中。
PyObject *pRet = PyObject_CallObject(pFunction, args);
这里是调用了刚才 `func`函数,并传递了参数,同时得到一个返回对象。程序利用了` PyArg_Parse(pRet,"i", &res);`进行对 pRet 的 Py 对象进行解析,解析成 int 类型。由此得到正确的返回值。
可以看一下testFunction.py程序:
import
Python程序对传递进来的字符串(也就是图像名)进行图像读取并显示大小,同时调用了 `TensorFlow` 完成类一个简单运算,最后打印结果,返回整型 1。
通过 C++ 调用,就能够完成该Python函数的运行,同时得到返回值。当然这是最简单的一个 C++ 调用 Python3 的例子。
2. 调用Python3中的类
一般面向对象,皆使用类,因此 C++ 调用 Python3 也需要对类的调用进行支持。
int
有了上面调用函数的例子,调用类就变得简单了。该程序主要是调用testClass.py 中的 TestDemo 类。 和调用函数不同的这两句代码:
PyObject *pInstance = PyObject_Call(pClass,args1, nullptr); //创建实例
PyObject *pRet = PyObject_CallMethod(pInstance,"evaluate", "O", args2);
其中`PyObject_Call` 是创建一个类的实例,同时可以传递 Python类中`__init__ `的参数,得到实例对象,通过 `PyObject_CallMethod` 调用类的成员方法 `evaluate`, 同样的可以传递参数。
其中`testClass.py`代码为:
from
实现的功能和之前的测试调用函数是一样的功能,只不过封装到类中。
3. 特殊返回对象处理(Map)
对于Python特殊的数据结构的返回, Python3 API也有相应的处理,使用最多的就是之前封装传参的PyTuple对象。另外还会有对列表进行处理的 PyList对象。而这里记录处理比较复杂的Map类型。
int
值得注意的是这句:
PyObject
这是对返回对象进行了字典键值的提取,真正得到键内容还得继续处理。 而代码中 `PyDict_Size(pDict)` 得到字典的键值对数量。
PyObject
`pKeys` 相当于得到所有的键,存成List类型,`PyList_GetItem` 从List中取出第 i 个元素,然后再解析这个对象,就拿到了第 i 个键。解析采用了 `PyUnicode_AsUTF8` 解析的是字符串。
PyObject
这句代码相当于从字典中 根据键拿到所谓的值,而值可以是列表形式,因此解析 `pValue`时 具体情况具体分析了。上述代码演示了值为列表的解析过程。
Python代码非常简单,返回类一个字典,键为字符串,值为列表类型:
def
4. OpenCV的Mat图像的传递
想要利用C++调用深度学习网络推断模型进行目标检测或者进行图像语义分割,若通过C++调用Python3的接口这种方式,那麽必定需要传递OpenCV的Mat图像,通过Python 的 numpy 去承接图像。
int
代码中进行了依次Mat转换成PyObject的过程,然后在传递这个PyObject到类的成员方法。如下代码所示:
cv
Python代码如下所示:
class
这里能够成功传递图像,但没有利用网络来对图像处理。
tips: 具体的相关代码能够在这里找到。