通过在C++程序调用Python3所提供的C接口可以实现调用Python程序所实现的功能。在C++调用深度学习训练好的模型时,如果不使用一些部署手段,这种C++调用Python接口的方式虽然大大牺牲了效率,但是可以说是也是一种取巧的方法。这里记录一下C++如何去调用Python3的接口,作为一个总结。
操作系统:Ubuntu16.04
构建工具:CMake
要想在 C++ 中调用 Python3 ,必须在 Cmake 中添加一些 Python3 的动态链接库:
可以在 include_directories
中加入:
/usr/include/python3.5
/usr/lib/python3/dist-packages/numpy/core/include/numpy
在 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 testFunction1(){
Py_Initialize();
if(!Py_IsInitialized()){
cout << "[Error] Init error" << endl;
return -1;
}
string change_dir = "sys.path.append('../scripts')";
PyRun_SimpleString("import sys");
PyRun_SimpleString(change_dir.c_str());
PyObject *pModule = PyImport_ImportModule("testFunction");
if (pModule == nullptr){
cout <<"[Error] Import module error" << endl;
return -1;
}
cout << "[INFO] Get Module" << endl;
PyObject *pFunction = PyObject_GetAttrString(pModule, "func");
if (pFunction == nullptr){
cout << "[Error] Import function error" << endl;
return -1;
}
cout << "[INFO] Get Function" << endl;
PyObject *args = PyTuple_New(1);
PyObject *args1 = PyUnicode_FromString("../air.jpg");
PyTuple_SetItem(args, 0, args1);
PyObject *pRet = PyObject_CallObject(pFunction, args);
int res = 999;
if (pRet){
PyArg_Parse(pRet,"i", &res);
cout << res << endl;
}
Py_DecRef(pModule);
Py_DecRef(pFunction);
Py_DecRef(args);
Py_DecRef(args1);
Py_DecRef(pRet);
Py_Finalize();
return 1;
}
代码中值得注意的是有很多固定写法:
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 numpy as np
import tensorflow as tf
import os
from PIL import Image
def func(pic):
modelDir = "/home/tyl/Code/Cprojects/TestDemo/TestCppPython/TestPython3/model"
sess = tf.Session()
saver = tf.train.import_meta_graph(os.path.join(modelDir,"model.ckpt.meta"))