Pybind11实现cpp和python混编


一个简单实例的创建

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)

参考

[1] 一个简单的示例
[2]
[3]
[4] pybind11 数据交互

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坚果仙人

谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值