基于ubuntu 18.04系统,使用python3调用c++生成的动态链接库
(1) 命令安装
sudo apt-get install python-pybind11
(2) pip命令安装
pip3 install pybind11
(3) 源码编译安装
pip3 install pytest
git clone https://github.com/pybind/pybind11.git
cd pybind11
mkdir build
cd build
cmake …
make -j4
sudo make install
这里简单列一下以前使用pybind11写的一个借口函数,并使用CMakeLists.txt生成 .so 动态链接库,如下所示,
map_interface.h
#include
#include
namespace py = pybind11;
constexpr int kLocalDescriptorSize2 = 256;
constexpr int kGlobalDescriptorSize2 = 4096;
class MapInterface
{
public:
int CreateObj(int img_row, int img_col);
void SetCameraParas(int camera_model, Eigen::Ref<Eigen::Matrix<float, 3, 3, Eigen::RowMajor>> camera_intrinsic,
Eigen::Ref<Eigen::Matrix<float, 5, 1, Eigen::ColMajor>> camera_distort);
bool AddImgFeatures(int frame_index, Eigen::Ref<Eigen::Matrix<float, 4, 4, Eigen::RowMajor>> camera_pose,
Eigen::Ref<Eigen::Matrix<float, 1, kGlobalDescriptorSize2, Eigen::RowMajor>> global_descriptor,
Eigen::Ref<Eigen::Matrix<float, 2, Eigen::Dynamic, Eigen::RowMajor>> row_image_keypoints,
Eigen::Ref<Eigen::Matrix<float, kLocalDescriptorSize2, Eigen::Dynamic, Eigen::RowMajor>> row_local_descriptors);
// 为了便于pybind11 调用,这里暂时不使用重载函数功能
bool AddImgFeatures2(int frame_index, Eigen::Ref<Eigen::Matrix<float, 4, 4, Eigen::RowMajor>> camera_pose,
Eigen::Ref<Eigen::Matrix<float, 1, kGlobalDescriptorSize2, Eigen::RowMajor>> global_descriptor,
Eigen::Ref<Eigen::Matrix<float, 2, Eigen::Dynamic, Eigen::RowMajor>> row_image_keypoints,
Eigen::Ref<Eigen::Matrix<float, 2, Eigen::Dynamic, Eigen::RowMajor>> row_norm_keypoints,
Eigen::Ref<Eigen::Matrix<float, kLocalDescriptorSize2, Eigen::Dynamic, Eigen::RowMajor>> row_local_descriptors);
void DestroyObj();
};
PYBIND11_MODULE(map_interface, m) {
m.doc() = "pybind11 Hierarchical Localization cpp backend";
py::class_<MapInterface>(m, "MapInterface")
.def(py::init())
.def("CreateObj", &MapInterface::CreateObj, py::return_value_policy::copy)
.def("SetCameraParas", &MapInterface::SetCameraParas)
.def("AddImgFeatures", &MapInterface::AddImgFeatures, py::return_value_policy::copy)
.def("AddImgFeatures2", &MapInterface::AddImgFeatures2, py::return_value_policy::copy)
.def("DestroyObj", &MapInterface::DestroyObj);
};
注意需要添加#include
void SetCameraParas(int camera_model, Eigen::Ref<Eigen::Matrix<float, 3, 3, Eigen::RowMajor>> camera_intrinsic,
Eigen::Ref<Eigen::Matrix<float, 5, 1, Eigen::ColMajor>> camera_distort);
当然也可以使用numpy做函数参数的数据传输.
这里将c++的函数接口封装到类里面, 在最后添加了python调用c++的接口函数
PYBIND11_MODULE(map_interface, m) {
m.doc() = "pybind11 Hierarchical Localization cpp backend";
py::class_<MapInterface>(m, "MapInterface")
.def(py::init())
.def("CreateObj", &MapInterface::CreateObj, py::return_value_policy::copy)
.def("SetCameraParas", &MapInterface::SetCameraParas)
.def("AddImgFeatures", &MapInterface::AddImgFeatures, py::return_value_policy::copy)
.def("AddImgFeatures2", &MapInterface::AddImgFeatures2, py::return_value_policy::copy)
.def("DestroyObj", &MapInterface::DestroyObj);
};
注意,上面接口为了方(偷)便(懒), AddImgFeatures()没有如果使用重载函数的话. 如果使用pybind11实现累的重载函数接口时,需要使用py::overload_cast, 具体可参考https://blog.csdn.net/weixin_41521681/article/details/106200017
.def("AddImgFeatures", py::overload_cast< 函数具体的参数 >(&MapInterface::AddImgFeatures))
上面函数的具体实现是在map_interface.cpp文件中, 这里不具体列. 需要特别注意的是,在写接口类,或者接口函数时,接口所在的.h文件尽量少的依赖自己写的一些.h文件, 这些依赖可以具体写在.cpp中. 这样, 在使用python调用(或者是主函数调用)时,使用接口的.h文件可以避免大量.h文件暴露出来. 另外为了更加规范,可以将PYBIND11_MODULE这个模块单独写在一个.h文件中.
CMakeList.txt文件的写法
cmake_minimum_required(VERSION 2.8.12)
project(HFnet_map)
add_compile_options(-std=c++11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g")
set(CMAKE_BUILD_TYPE "Release")
set(PYBIND11_CPP_STANDARD -std=c++11)
find_package(pybind11 REQUIRED)
find_package(OpenCV REQUIRED)
find_package(Eigen3 REQUIRED)
INCLUDE_DIRECTORIES(
${OpenCV_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
${pybind11_INCLUDE_DIRS}
./
)
add_library(map_interface MODULE
map_interface.cpp
xxx.cpp
xxx.cpp
xxx.cpp
)
target_link_libraries(map_interface
pybind11::module
${OpenCV_LIBS}
)
CMakeList里面需要注意对pybind11的引用, 也有其他引用的方式,例如使用 pybind11_add_module,可参考pybind11的官方doc
python调用代码如下:
import os
import cv2
...
import sys
sys.path.append("./build/")
import map_interface # c++接口
# 创建c++接口对象
map_obj = map_interface.MapInterface()
# 调用函数
map_obj.CreateObj()
map_obj.SetCameraParas(<传入调用的参数>)
...
在使用python调用c++接口函数时, 如果python代码和.so不在同一个路径, 则需要指定生成的.so路径
import sys
sys.path.append("so的路径")
在使用过程中, 有三个地方的变量命名需要一致,分别是上述代码中的: PYBIND11_MODULE接口函数中的map_interface, CMakeList中的add_library中的map_interface, python代码中的 import map_interface. 如下,
- PYBIND11_MODULE(map_interface, m)
- add_library(map_interface MODULE … )
- import map_interface # c++接口
这里,全部统一定义成map_interface. 否则在python调用时会出现如下等错误:
ModuleNotFoundError: No module named ’ ’
在运行python程序时,还会出现错误:
ImportError: dynamic module does not define module export function (PyInit_libxxxx)
这个问题仍然是这三个地方的命名不一致导致的,需要注意的是, linux 系统在使用 CMake 编译 c++ 动态链接库的时候, 会加一个前缀lib, 生成libmap_interface.so, 因此,这三个地方的命名应该修改如下:
- PYBIND11_MODULE(libmap_interface, m)
- add_library(map_interface MODULE … )
- import libmap_interface # c++接口
因为这个问题,也是调试了很久.