学习C++:C++进阶(六)如何在C++代码中调用python类,实例化python中类的对象,如何将conda中的深度学习环境导入C++项目中

目录

1.应用场景

2.场景:利用maskrcnn深度学习网络实现语义分割

3.CMake配置python解释器

4.C++中实现实例化python中的类的对象并调用类内方法

4.1 初始化python环境

4.2  实例化python类的对象


1.应用场景

        我们在视觉SLAM以及目标检测和识别中,经常要将代码结合深度学习。但深度学习代码大多都是python的,SLAM代码大多都是C++的,如何建立C++和python的链接桥就显得极为重要,本篇文章将介绍如何配置CMake导入深度学习的python模块以及如何在C++代码中python中类的方法。

2.场景:利用maskrcnn深度学习网络实现语义分割

        在视觉SLAM中调用maskrcnn深度学习语义分割网络实现对动态物体的剔除。

        首先,我训练了一个各种树和人的数据集,确保我的深度学习网络maskrcnn可以运行起来,在pycharm中运行效果如下:

        原图片是一张人的图片,这里返回的是这个图像的掩膜。!!到这里我就想告诉大家,我的深度学习网络可以运行起来(也就是说你在进行C++调用python的时候要确定你的python代码可以运行起来)。

        这个代码里面我是定义了一个类ORBSLAM3_with_masknet

        这个类的初始化函数传入两个参数:输入的图片路径、输出的图片路径。

    def __init__(self, input_image_path, out_put_image):
        self.outImage = None
        self.input_image_path = input_image_path
        self.output_image_path = out_put_image

        这个类定义了第一个方法是saveImagetoORBSLAM3,没有传入参数(self),其功能就是对输入图片路径的图片进行检测并进行掩膜,并将掩膜信息保存到类内变量self.outImage中。

        这个类定义了第一个方法是showimage,没有传入参数(self),其功能是展示掩膜图片self.outImage。

        我们为了确保这个类中没有问题,我们在main函数中实例化这个类然后调用方法:

if __name__ == '__main__':
    my_object = ORBSLAM3_with_masknet("/home/xxxxx/Desktop/slam/mask_rcnn/333.JPG",
                                      "/home/xxxxx/Desktop/slam/mask_rcnn/")
    my_object.saveImagetoORBSLAM3()
    my_object.showimage()

        运行如下:可以运行。

3.CMake配置python解释器

        由于我们系统默认的python路径是环境变量下的python,而不是我们conda虚拟环境下的深度学习环境,因此我们需要设置CMake让我们的SLAM项目链接到我们conda虚拟环境下的深度学习环境:

        我的conda中的pytorch环境在torch虚拟环境中,即在目录/home/anaconda3/envs下面的torch中。

        如果我们不在代码中特意声明,我们看看系统引用的python路径是什么?

find_package(PythonLibs REQUIRED)
if (NOT PythonLibs_FOUND)
    message(FATAL_ERROR "PYTHON LIBS not found.")
else()
    message("PYTHON LIBS were found!")
    message("PYTHON LIBS DIRECTORY: " ${PYTHON_LIBRARY})
endif()

        我们配置一下项目:

        我们可以看到,引用的python环境是系统环境下

PYTHON LIBS DIRECTORY: /usr/lib/x86_64-linux-gnu/libpython3.6m.so的python3.6,这样就会让代码运行时出现python类无法初始化、段错误等令人十分头痛的错误,那要如何设置我们的conda虚拟环境作为我们的python环境变量呢,我们加入下面两行内容:

set(PYTHON_LIBRARY /home/liuhongwei/anaconda3/envs/torch/lib/libpython3.8.so)
set(PYTHON_INCLUDE_DIR /home/liuhongwei/anaconda3/envs/torch/include/python3.8)
find_package(PythonLibs REQUIRED)
if (NOT PythonLibs_FOUND)
    message(FATAL_ERROR "PYTHON LIBS not found.")
else()
    message("PYTHON LIBS were found!")
    message("PYTHON LIBS DIRECTORY: " ${PYTHON_LIBRARY})
endif()

        第一行是python的解释器的动态链接库的位置,他就在我们的conda虚拟环境中,每个机器都是一样的,如下图:

        第二行是python的库文件的位置。

        然后我们再去构建:我们成功链接到了我们的深度学习虚拟环境。

        此外,我们现在只是找到了python深度学习环境,但要将python深度环境加载到我们的项目中,这里用include_directories关键字,将项目所需的库都引入:

include_directories(
        ${EIGEN3_INCLUDE_DIR}
        ${Pangolin_INCLUDE_DIRS}
        /home/xxx/anaconda3/envs/torch/include/python3.8/
        /home/xxx/anaconda3/envs/torch/lib/python3.8/site-packages/numpy/core/include/numpy
        ${Boost_INCLUDE_DIRS}
)

        最后,我们将这些库链接到我们的程序中:

add_executable(ExtractDynamic main.cpp)
target_link_libraries(ExtractDynamic ${PYTHON_LIBRARY})

        所有的代码如下:

cmake_minimum_required(VERSION 3.25)
project(ExtractDynamic)

set(CMAKE_CXX_STANDARD 17)

set(PYTHON_LIBRARY /home/xxx/anaconda3/envs/torch/lib/libpython3.8.so)
set(PYTHON_INCLUDE_DIR /home/xxx/anaconda3/envs/torch/include/python3.8)
find_package(PythonLibs REQUIRED)
if (NOT PythonLibs_FOUND)
    message(FATAL_ERROR "PYTHON LIBS not found.")
else()
    message("PYTHON LIBS were found!")
    message("PYTHON LIBS DIRECTORY: " ${PYTHON_LIBRARY})
endif()

find_package(Eigen3 3.1.0 REQUIRED)
find_package(Pangolin REQUIRED)
find_package(Boost REQUIRED COMPONENTS thread)
if(Boost_FOUND)
    message("Boost was found!")
    message("Boost Headers DIRECTORY: " ${Boost_INCLUDE_DIRS})
    message("Boost LIBS DIRECTORY: " ${Boost_LIBRARY_DIRS})
    message("Found Libraries: " ${Boost_LIBRARIES})
endif()

include_directories(
        #${PROJECT_SOURCE_DIR}
        ${EIGEN3_INCLUDE_DIR}
        ${Pangolin_INCLUDE_DIRS}
        /home/xxx/anaconda3/envs/torch/include/python3.8/
        /home/xxx/anaconda3/envs/torch/lib/python3.8/site-packages/numpy/core/include/numpy
        ${Boost_INCLUDE_DIRS}
)

message("PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR})

add_executable(ExtractDynamic main.cpp)
target_link_libraries(ExtractDynamic ${PYTHON_LIBRARY})

        这样,我们CMake环境就配置好啦!

4.C++中实现实例化python中的类的对象并调用类内方法

4.1 初始化python环境

    std::cout << "Initializing the OutDynamicNet..." << std::endl;

    // 转换字符串
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
    std::wstring pythonHome = converter.from_bytes("/home/liuhongwei/anaconda3/envs/torch");

    // 设置 深度学习网络目录
    std::string masknet;
    setenv("PYTHONPATH","/home/liuhongwei/Desktop/slam/mask_rcnn",1);
    masknet = getenv("PYTHONPATH");


    // 设置python的解释器的目录,初始化python环境
    Py_SetPythonHome(pythonHome.c_str());
    Py_Initialize();
    if(Py_IsInitialized())
    {
        std::cout << "Python Inititalize Succeess" << std::endl;
    }

        我们引入头文件完成python环境的初始化:

#include <Python.h>
#include <locale>
#include <codecvt>

        pythonHome保存的是虚拟环境的目录,注意,这个转换字符串的工作是必须做的!!!!否则会报错.....我在这块卡了一个星期,翻越各种博客和参考书才知道的.....

        我们通过if语句判断python环境是否初始化成功!!注意:这个python环境必须和CMake中设置的python环境一样,不然不会初始化成功。

        我们执行:

4.2  实例化python类的对象

    PyObject * pModule = NULL;
    PyObject * pDict = NULL;
    PyObject * pInstance = NULL;
    PyObject * pClass = NULL;
    PyObject * deal = NULL;

    // 引入python文件 /xxxx/predict.py
    pModule = PyImport_ImportModule("predict");
    if(pModule != NULL)
    {
        std::cout << "Module imported successfully" << std::endl;
    }
    else
    {
        std::cout << "Failed to import module" << std::endl;
    }
    assert(pModule != NULL);


    // 获取predict可以引用的类
    pDict = PyModule_GetDict(pModule);
    assert(pDict != NULL);

    // 获取ORBSLAM3_with_masknet类,并判断类是否可引用
    pClass = PyDict_GetItemString(pDict,"ORBSLAM3_with_masknet");
    if(pClass != NULL && PyCallable_Check(pClass))
    {
        std::cout << "Class imported successfully" << std::endl;
    }
    else
    {
        std::cout << "Failed to import class" << std::endl;
    }

        我们引入这个文件夹下的predict.py文件,这个文件里面有saveImagetoORBSLAM3类,类内有方法帮助我们实现语义分割。

        由于我们先前设置了pythonHome变量,因此pModule就获取了python文件,判断一下是否打开成功(失败是环境问题或者代码问题或者路径问题....)。        

        我们再通过PyModule_GetDict方法获取我们在这个python文件中可以引用的类。

        通过PyDict_GetItemString方法获取我们需要使用的类,参数为可以引用的类的对象以及我们要导入类的名称,判断这个类是否可用。

    pInstance = PyObject_CallFunction(pClass,"ss","/home/xxx/Desktop/slam/mask_rcnn/333.JPG","/home/xxx/Desktop/slam/mask_rcnn/");
    assert(pInstance != NULL);

    PyObject * pFunc = PyObject_GetAttrString(pInstance,"saveImagetoORBSLAM3");
    assert(pFunc!=NULL);

    PyObject * pResult1 = PyObject_CallMethod(pInstance,"__init__","ss","/home/xxx/Desktop/slam/mask_rcnn/333.JPG","/home/xxx/Desktop/slam/mask_rcnn/");
    assert(pResult1!=NULL);

        我们通过PyObject_CallFunction方法实例化pClass所指向的类(ORBSLAM3_with_masknet)。这里由于初始化要传入两个路径,因此数据类型是ss,传入两个地址字符串。

    def __init__(self, input_image_path, out_put_image):
        self.outImage = None
        self.input_image_path = input_image_path
        self.output_image_path = out_put_image

        我们再通过PyObject_GetAttrString执行ORBSLAM3_with_masknet类的方法。

        完成!!!完整代码如下:

#include <iostream>
#include <Python.h>
#include <locale>
#include <codecvt>
int main()
{

    std::cout << "Initializing the OutDynamicNet..." << std::endl;

    // 转换字符串
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
    std::wstring pythonHome = converter.from_bytes("/home/xxx/anaconda3/envs/torch");

    // 设置 深度学习网络目录
    std::string masknet;
    setenv("PYTHONPATH","/home/xxx/Desktop/slam/mask_rcnn",1);
    masknet = getenv("PYTHONPATH");


    // 设置python的解释器的目录,初始化python环境
    Py_SetPythonHome(pythonHome.c_str());
    Py_Initialize();
    if(Py_IsInitialized())
    {
        std::cout << "Python Inititalize Succeess" << std::endl;
    }

    PyObject * pModule = NULL;
    PyObject * pDict = NULL;
    PyObject * pInstance = NULL;
    PyObject * pClass = NULL;
    PyObject * deal = NULL;

    // 引入python文件 /home/xxx/Desktop/slam/mask_rcnn/predict.py
    pModule = PyImport_ImportModule("predict");
    if(pModule != NULL)
    {
        std::cout << "Module imported successfully" << std::endl;
    }
    else
    {
        std::cout << "Failed to import module" << std::endl;
    }
    assert(pModule != NULL);


    // 获取predict可以引用的类
    pDict = PyModule_GetDict(pModule);
    assert(pDict != NULL);

    // 获取ORBSLAM3_with_masknet类,并判断类是否可引用
    pClass = PyDict_GetItemString(pDict,"ORBSLAM3_with_masknet");
    if(pClass != NULL && PyCallable_Check(pClass))
    {
        std::cout << "Class imported successfully" << std::endl;
    }
    else
    {
        std::cout << "Failed to import class" << std::endl;
    }

    // 初始化类
//    const char * ImagePath = ;
//    const char * OutPutPath = ;
    pInstance = PyObject_CallFunction(pClass,"ss","/home/xxx/Desktop/slam/mask_rcnn/333.JPG","/home/liuhongwei/Desktop/slam/mask_rcnn/");
    assert(pInstance != NULL);



    PyObject * pFunc = PyObject_GetAttrString(pInstance,"saveImagetoORBSLAM3");
    assert(pFunc!=NULL);

    PyObject * pResult1 = PyObject_CallMethod(pInstance,"__init__","ss","/home/xxx/Desktop/slam/mask_rcnn/333.JPG","/home/xxx/Desktop/slam/mask_rcnn/");
    assert(pResult1!=NULL);
    PyObject * pResult2 = PyObject_CallMethod(pInstance,"saveImagetoORBSLAM3",NULL);
    assert(pResult2!=NULL);


//    deal = PyObject_CallMethod(pInstance, "saveImagetoORBSLAM3",NULL);
//    if(deal != NULL)
//    {
//        std::cout << "Class imported successfully" << std::endl;
//    }
//    else
//    {
//        std::cout << "Failed to import class" << std::endl;
//    }
//    assert(deal!=NULL);

    std::cout << "Creating net instance..." << std::endl;
    return 0;
}

        我们看下结果:

猜你喜欢

转载自blog.csdn.net/qq_41694024/article/details/130220025