文章目录
一个简单实例的创建
1、Pybind文件部署目录
├── build
├── CMakeLists.txt
├── fmain.cpp:自己实现的功能
└── pybind11:(文件夹)注意这个文件夹名字不能变
注:(1)要保证编译环境和运行环境一致,否则会出现找不到模块的情况。
2、环境配置和准备
$ git clone https://github.com/pybind/pybind11
$ pip install cython
$ pip install pytest
$ mkdir pybind11/build
$ cd pybind11/build
$ cmake ..
$ cmake --build . --config Release --target check
3、cpp文件的编写
fmain.cpp
#include <pybind11/pybind11.h>
#include <pcl/point_types.h>
#include <pcl/io/ply_io.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/gp3.h>
#include <pcl/octree/octree.h>
#include <pcl/point_cloud.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/segmentation/conditional_euclidean_clustering.h>
#include <vector>
#include <fstream>
using namespace std;
namespace py = pybind11;
#ifndef PointType
#define PointType pcl::PointXYZRGBNormal
#endif
void PatchExtract(string path,string output_path)
{
pcl::PointCloud<PointType>::Ptr cloud(new pcl::PointCloud<PointType>);
pcl::io::loadPLYFile<PointType>(path, *cloud);
int K=10;
pcl::search::KdTree<PointType>::Ptr kdtree(new pcl::search::KdTree<PointType>());
kdtree->setInputCloud(cloud);
ofstream fout(output_path);
for(int i=0;i<cloud->points.size();i++){
vector<int> idx(K);
vector<float> dist(K);
kdtree->nearestKSearch(i, K, idx, dist);
for(int j=0;j<K-1;j++)
fout<<idx[j]<<" ";
fout<<idx[K-1]<<endl;
}
fout.close();
cout<<"End!"<<endl;
}
class MyClass{
public:
void Hello(){
cout<<"Hello!"<<endl;
}
};
PYBIND11_MODULE(fmdl, m)
{
/* optional module docstring */
m.doc() = "pybind11 example plugin";
// expose add function, and add keyword arguments and default arguments
m.def("PatchExtract", &PatchExtract, "Extract the patches from a point cloud");
/*My Class*/
py::class_<MyClass>(m,"MyClass", py::dynamic_attr())
.def(py::init<>())
.def(py::init<std::string>())
.def("Hello",&MyClass::Hello)
;
/* exporting variables */
m.attr("the_answer") = 42;
py::object world = py::cast("World");
m.attr("what") = world;
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
project(fmdl)
# PCL
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
# Pybind 11
add_subdirectory(pybind11)
pybind11_add_module(fmdl fmain.cpp)
# PCL
target_link_libraries (fmdl PUBLIC ${PCL_LIBRARIES})
4、Python文件的编写
example.py
import PCN_proprocessing_cpp.build.example as exp
exp.PatchExtract("/home/i9/dataset_new/4_Mimosa.ply","/home/i9/dataset_new/1.txt")
类的使用(继承、重载、枚举)
常用数据的交互
1、传递 vector → \to → numpy
cpp代码
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
py::array_t<double> add_arrays(py::array_t<double> input1, py::array_t<double> input2) {
/* read input arrays buffer_info */
py::buffer_info buf1 = input1.request();
py::buffer_info buf2 = input2.request();
if (buf1.size != buf2.size)
throw std::runtime_error("Input shapes must match");
/* allocate the output buffer */
py::array_t<double> result = py::array_t<double>(buf1.size);
py::buffer_info buf3 = result.request(); // acquire buffer info
double *ptr1 = (double *)buf1.ptr;
double *ptr2 = (double *)buf2.ptr;
double *ptr3 = (double *)buf3.ptr;
size_t high = buf1.shape[0];
size_t width = buf1.shape[1];
/* Add both arrays */
for (size_t idy = 0; idy < high; idy++){
for (size_t idx = 0; idx < width; idx++){
int curIdx = idy*width + idx;
ptr3[curIdx] = ptr1[curIdx] + ptr2[curIdx];
}
}
/* Reshape result to have same shape as input */
result.resize({ high, width });
return result;
}
PYBIND11_MODULE(example, m) {
m.doc() = "Add two vectors using pybind11"; // optional module docstring
m.def("add_arrays", &add_arrays, "Add two NumPy arrays");
}
python调用代码
import numpy as np
import example
a = np.ones((10,3))
b = np.ones((10,3)) * 3
c = example.add_arrays(a, b)
print(c)
注:buffer_info 数据结构如下所示
struct buffer_info {
void *ptr;
ssize_t itemsize;
std::string format;
ssize_t ndim;
std::vector<ssize_t> shape;
std::vector<ssize_t> strides;
};
vector转为numpy流程为py::array_t<double>
→
\to
→py::buffer
→
\to
→ double*
auto result = py::array_t<double>(3);
py::buffer_info buf3 = result.request();
double * ptr3 = (double *) buf3.ptr;
ptr3[0]=1;
ptr3[1]=2;
ptr3[2]=3;
return result;
2、传递 Eigen::MatrixXd → \to → numpy
方式一:通过Eigen::MatrixXd进行传递
cpp文件代码:注意Eigen头文件由pybind11提供
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
class MyClass
{
// 注意将要返回的变量定义为成员变量,要不然会报错
Eigen::MatrixXd big_mat = Eigen::MatrixXd::Zero(3, 3);
public:
MyClass(){
big_mat(0,0)=1;
}
Eigen::MatrixXd &getMatrix() { return big_mat; }
const Eigen::MatrixXd &viewMatrix() { return big_mat; }
};
PYBIND11_MODULE(fmdl, m){
// Later, in binding code:
py::class_<MyClass>(m, "MyClass")
.def(py::init<>())
.def("copy_matrix", &MyClass::getMatrix) // Makes a copy!
.def("get_matrix", &MyClass::getMatrix, py::return_value_policy::reference_internal)
.def("view_matrix", &MyClass::viewMatrix, py::return_value_policy::reference_internal)
;
}
python调用代码
import cppmdl.build.fmdl as fmdl
a = fmdl.MyClass()
m = a.get_matrix() # flags.writeable = True, flags.owndata = False
v = a.view_matrix() # flags.writeable = False, flags.owndata = False
c = a.copy_matrix() # flags.writeable = True, flags.owndata = True
print(m)
print(v)
print(c)
3、传递 vector → \to → list
cpp代码
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>
using std::vector;
namespace py = pybind11;
vector<float> ListMul(vector<float>& in_list, float coef) {
vector<float> ret_vec;
ret_vec.clear();
// Requests that the vector capacity be at least enough to contain n elements.
ret_vec.reserve(in_list.size());
for (size_t i = 0; i < in_list.size(); ++i) {
int v = in_list[i];
ret_vec.push_back(coef * v);
}
return ret_vec;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pass and return a list";
m.def("ListMul", &ListMul, "example function");
}
python端调用
import example
print(example.ListMul([1.0, 2, 3, 4, 5, 6], 5))
4、传递 tuple (C++) → \to → tuple (python)
/* pybind11 */
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
using namespace std;
namespace py = pybind11;
std::tuple<int,int> get_tuple(){
std::tuple<int,int> a;
a=std::make_tuple(1,2);
return a;
}
PYBIND11_MODULE(fmdl, m){
// optional module docstring
m.doc() = "pybind11 example plugin";
m.def("get_tuple", &get_tuple, "get tuple");
}
在python端使用库中的变量
需要使用attr函数在模块中注册变量,内置类型在分配为属性时会自动转换,也可以使用py::cast函数显式转换。
PYBIND11_MODULE(example, m) {
m.attr("the_answer") = 42;
py::object world = py::cast("World");
m.attr("what") = world;
}
调用
>>> import example
>>> example.the_answer
42
>>> example.what
'World'
导出成员变量
py::class_<mycalss>(m, "mycalss")
.def_readwrite("var1", &mycalss::var1)
关于函数参数传递和返回值
pybind11的函数参数和返回值不需要指明,如下所示
#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j){
return i + j;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function which adds two numbers");
}
如果想在python端为函数的指定参数赋值:
m.def("add", &add, "A function which adds two numbers", py::arg("i"), py::arg("j"));
则python端可以这样调用
>>> import example
>>> example.add(i=1, j=2)