目录
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; }
我们看下结果: