基于pybind11的python调用c++动态链接库

基于ubuntu 18.04系统,使用python3调用c++生成的动态链接库

1. pybind11的三种安装方式:

(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

2. 生成动态链接库

这里简单列一下以前使用pybind11写的一个借口函数,并使用CMakeLists.txt生成 .so 动态链接库,如下所示,
map_interface.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>

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 头文件, 因为使用Eigen作为数据传输的接口,因此需要使用#include 文件, 在使用Eigen作为函数参数,用来传输数据时, 注意要指定Eigen矩阵的行列有限排列,即Eigen::RowMajor/ColMajor. 需要特别注意的是,当矩阵是m

×\times

× 1(m行,1列)情况下,必须要使用列优先排列(Eigen::ColMajor),不然会出现错误. 例如:

1
2
    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++的接口函数

1
2
3
4
5
6
7
8
9
10
11
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

1
.def("AddImgFeatures", py::overload_cast< 函数具体的参数 >(&MapInterface::AddImgFeatures))

上面函数的具体实现是在map_interface.cpp文件中, 这里不具体列. 需要特别注意的是,在写接口类,或者接口函数时,接口所在的.h文件尽量少的以来自己写的一些.h文件, 这些依赖可以具体写在.cpp中,这样,在使用python调用(或者是主函数调用)时,使用给出接口的.h文件即可.避免大量.h文件暴露出来

3. 使用CMakeList.txt文件编译生成 .so

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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")

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 SHARED
            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