c++调用python

这里写目录标题

  • c++调用python
    • 头文件包含
    • 初始化python调用
    • 具体的函数调用
    • 链接CMakeLists.txt
    • Example
    • c++ python的数据类型转化
      • 图像数据格式cv::Mat传递
      • numpy数据格式转化
    • 一些奇怪的错误

c++调用python

头文件包含

#include //应该是调用什么版本解使用什么版本的python
#include //使用的方式和cmakelist有些关系

初始化python调用

Py_Initialize(); 初始化
PyRun_SimpleString("import sys");   //设置文件路径
PyRun_SimpleString("sys.path.append('./')");//./表示当前执行程序的路径
//中间在使用下面的一些具体的函数调用方式实现调用python
Py_Finalize();

具体的函数调用

  • 直接调用代码
PyRun_SimpleString(); //使用字符串实现直接写的python程序
  • 指向python的对象的指针
PyObject *object;//创建python的object的指针,可以是model class fun arg return
Py_DECREF(object);//销毁object,感觉和c++的new和delete比较像
  • 引入python文件的model
pModule = PyImport_ImportModule("test"); // 导入python的文件modle,不需要写后缀名.py
pDict = PyModule_GetDict(pModule); // 以字典形式存储model的信息,用于后续的调用
  • python数据创建
//创建python的数据类型的object
PyObject *Arg = Py_BuildValue("(s)", "dawd a");
PyObject *Arg = Py_BuildValue("(i, i)", 1, 2);
//创建好多参数
PyObject* cons_args = PyTuple_New(2);
PyObject* cons_arg1 = PyLong_FromLong(1);
PyObject* cons_arg2 = PyLong_FromLong(999);
PyTuple_SetItem(cons_args, 0, cons_arg1);
PyTuple_SetItem(cons_args, 1, cons_arg2)

  • 函数调用
PyObject *pFunc = PyObject_GetAttrString(pModule, "Hello"); //从字符串创建python函数object
PyObject *pFunc = PyDict_GetItemString(pDict, "Add"); //从字典创建python函数object
PyObject *result = PyEval_CallObject(pFunc, pArg); //实现python的函数object的调用并接受return
int c;
PyArg_Parse(result, "i", &c); // 数据类型转化python -> c++
  • class调用
  1. 就离谱网上好多教程把构造函数当成class的实例化对象使用,后面在class的def __init__()中加了个print()发现没有输出才发现这个问题。
  2. 还有一个问题,对于python代码中调用classfunc貌似在c++的调用中会有问题,我这里发现不能这么调用,所有解决方案就是把class的声明、构造、实例化全部拆开到c++中调用
  3. PyObject_CallMethod()这个函数传递参数"s"的时候不能使用string需要转化为const char* image_name1 = querys[i].data();才能正常传递参数
PyObject *pClass = PyDict_GetItemString(pDict, "HFNet");//获取class
PyObject *pConstruct = PyInstanceMethod_New(pClass); //获取class构造函数
PyObject *cons_args = Py_BuildValue("(s)", "./Examples/python/model/hfnet");//设置实例化所需要的参数
PyObject *pInstance = PyObject_CallObject(pConstruct, cons_args); //实例化class
// PyObject_CallMethod(pInstance, methodname, "O", args) 
// 参数0:[class的object]
// 参数1:[class的fun的名称]
// 参数2:[输入参数的类型] [O表示object][s表示char*,string的类型需要进行转化] 
// ? 这里有个疑问,self参数需要输入吗?网上的example我看到过有的也有不传递的
// 参数...:[class内的fun的各个参数]
std::vector querys;
const char*  image_name1 =  querys[i].data();
result1 = PyObject_CallMethod(pInstance, "inference", "s", image_name1);

链接CMakeLists.txt

  • 最终版本以项目目录下的CMakeLists.txt为准好事
#set(PYTHON_INCLUDE_DIR /usr/include/python3.6)#这个好像并不影响
#python的虚拟环境,需要添加对应的.so,bash中 source /venv/bin/activate
set(PYTHON_LIBRARY /usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/libpython3.6.so)
#看到有人添加
#include_directories( /usr/include/python3.6)
#的这里我没弄感觉也能编译	
add_executable(usepython
        Examples/python/usepython.cc)
target_link_libraries(usepython -lpython3.6m)
##写法2还是用下面这种吧,这样头文件包含就可以直接#include 不用指定python版本
#下面这种写法写可以
find_package(PythonLibs REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
add_executable(usepython
        Examples/python/usepython.cc)
target_link_libraries(usepython ${PYTHON_LIBRARIES})

Example

简单的调用例子如下
c++

#include 
#include 
//#include 

int main()
{

    // - 基础的调用

    // 初始化Python
    //在使用Python系统前,必须使用Py_Initialize对其
    //进行初始化。它会载入Python的内建模块并添加系统路
    //径到模块搜索路径中。这个函数没有返回值,检查系统
    //是否初始化成功需要使用Py_IsInitialized。
    Py_Initialize();

    // 检查初始化是否成功
    if (!Py_IsInitialized())
    {
        return -1;
    }

    // - 添加当前路径
    // PyRun_SimpleString(FILE *fp, char *filename);
    //直接运行python的代码
    PyRun_SimpleString("import sys");
    // PyRun_SimpleString("print '---import sys---'");
    //下面这个./表示当前运行程序的路径,如果使用../则为上级路径,根据此来设置
    PyRun_SimpleString("sys.path.append('./Examples/python/')");
    PyRun_SimpleString("print(sys.path)");
    // - 引入模块
    PyObject *pModule, *pDict, *preturn; // python的对象的指针
    //要调用的python文件名
    pModule = PyImport_ImportModule("testpy"); //加载模型不用加后缀名.py
    if (!pModule)
    {
        printf("can't find your_file.py");
        getchar();
        return -1;
    }
    //获取模块字典属性
    pDict = PyModule_GetDict(pModule);
    if (!pDict)
    {
        return -1;
    }
    std::cout << "加载模型成功" << std::endl;

    PyObject *pFunc;
    // - 直接调用函数
    //直接获取模块中的函数
    PyRun_SimpleString("print('使用string获取模块中的函数')");
    pFunc = PyObject_GetAttrString(pModule, "Hello"); //从字符串创建python函数对象

    //参数类型转换,传递一个字符串
    //将c/c++类型的字符串转换为python类型,元组中的python类型查看python文档
    PyRun_SimpleString("print('生成python的数据类型')");
    PyObject *pArg = Py_BuildValue("(s)", "Hello");

    PyRun_SimpleString("print('实现py函数调用')");
    //调用直接获得的函数,并传递参数
    PyEval_CallObject(pFunc, pArg);

    // - 从字典属性中获取函数
    PyRun_SimpleString("print('使用字典获取模块中的函数')");
    pFunc = PyDict_GetItemString(pDict, "Add"); //从字典创建python函数对象
    //参数类型转换,传递两个整型参数
    pArg = Py_BuildValue("(i, i)", 1, 2); //创建python的数据类型
    //调用函数,并得到python类型的返回值
    PyObject *result = PyEval_CallObject(pFunc, pArg); //实现python的函数调用
    // c用来保存c/c++类型的返回值
    int c;
    //将python类型的返回值转换为c/c++类型
    PyArg_Parse(result, "i", &c); // 数据类型转化python -> c++
    //输出返回值
    std::cout << "a + b = c =" << c << std::endl;

    // - 通过字典属性获取模块中的类
    PyRun_SimpleString("print('---------通过字典属性获取模块中的class-----------')");
    PyObject *pClass = PyDict_GetItemString(pDict, "Test");

    //实例化获取的类
    PyRun_SimpleString("print('实例化获取的class')");
    PyObject *pInstance = PyInstanceMethod_New(pClass);
    //调用类的方法
    PyRun_SimpleString("print('调用class中Method')");
    result = PyObject_CallMethod(pInstance, "SayHello", "(Oss)", pInstance, "zyh", "12");
    //输出返回值
    char *name = NULL;
    PyRun_SimpleString("print('输出返回结果')");
    PyArg_Parse(result, "s", &name);
    printf("%s\n", name);

    // - 使用文件路径传递图像参数
     PyRun_SimpleString("print('---------使用文件路径传递图像参数-----------')");
    pFunc = PyDict_GetItemString(pDict, "show_image"); //从字典创建python函数对象
    pArg = Py_BuildValue("(s)", "Data/db1.jpg");
    preturn = PyEval_CallObject(pFunc, pArg);
    name = NULL;
    PyArg_Parse(preturn, "s", &name);
    printf("%s\n", name);

    Py_DECREF(pModule);
    Py_DECREF(pDict);
    Py_DECREF(pFunc);
    Py_DECREF(pArg);
    Py_DECREF(result);
    Py_DECREF(pClass);
    Py_DECREF(pInstance);
    Py_DECREF(preturn);
    //释放python
    Py_Finalize();
    // getchar();

    return 0;
}

python

import sys
import cv2


def Hello(s):
    print("Hello World")
    print(s)


def Add(a, b):
    print('a=', a)
    print('b=', b)
    return a + b


class Test:
    def __init__(self):
        print("Init")

    def SayHello(self, name, old):
        print("Hello,", name, old)
        return name


def show_image(name):
    print(name)
    image = cv2.imread(name)
    cv2.imshow("image", image)
    cv2.waitKey(0)
    return name

c++ python的数据类型转化

图像数据格式cv::Mat传递

PyObject *cvmat2py(cv::Mat &image)
{
    import_array();
    int row, col;
    col = image.cols; //列宽
    row = image.rows; //行高
    int channel = image.channels();
    int irow = row, icol = col * channel;
    npy_intp Dims[3] = {row, col, channel}; //图像维度信息
    PyObject *pyArray = PyArray_SimpleNewFromData(channel, Dims, NPY_UBYTE, image.data);
    PyObject *ArgArray = PyTuple_New(1);
    PyTuple_SetItem(ArgArray, 0, pyArray);
    return ArgArray;
}

numpy数据格式转化

  • numpy.ndarray中的numpy.float32、numpy.int32转化为c++格式的数据
  • 使用以下函数遍历实现每个一维、二维数据的读取
*(float *)PyArray_GETPTR1(Py_global_desc, i);
*(int *)PyArray_GETPTR1(Py_global_desc, i, j);

-具体的例子可以参考函数useNet.cc中void GetFeature()

一些奇怪的错误

  1. import cv2放在python文件中,没有缩进就会直接报错,放在函数内部就不会出现错误,后来有没有这个问题了,可能是c++写example的时候使用{}分别引入两次python导致的??{}不是会自己提供作用阈吗?,还是这个接口是全局的?
  2. 之前使用c++多线程开cv::imshow()程序会崩溃,没想到用c++自己用一个cv::imshow(),然后python也开一个cv.imshow()也会崩溃。啊这当时还以为cv::Mat转为PyObject的格式出了问题呢,结果不是的。

你可能感兴趣的:(python,c++,开发语言)