前言
考虑到点云数据最后可以转化成一个三维矩阵[x,y,z],而python在机器学习领域里面有着莫大的优势,python作为一门胶水语言,已经集成并调用其他科学领域的库。如scripy库,底层采用用fortran语言编写的,所以执行起来比C++快。因此本文将采用学习如何从C++调用相关python的模块以及库。
1. 准备
安装python3.9,在mac下可以直接通过brew进行安装:
brew install python@3.9
找到相应的安装目录:/usr/local/Cellar/python@3.9,
并且按照相应的科学计算包通过:
pip3 install scipy
修改CMakeList.txt 引入相关python的头,以及numpy相关的头以及库函数。如下:
#包含头文件以及链接路径
include_directories(/usr/local/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Headers)
link_directories(/usr/local/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/Current/lib)
include_directories(/usr/local/Cellar/numpy/1.22.3_1/lib/python3.9/site-packages/numpy/core/include)
link_directories(/usr/local/Cellar/numpy/1.22.3_1/lib/python3.9/site-packages/numpy/core/lib)
# 链接numpy库,以及python的库
target_link_libraries(${PROJECT_NAME} ${PCL_LIBRARIES} libgtest.a glog::glog libnpymath.a python3.9)
修改相关的CMakeList.txt文件,引入相关的头文件以及进行链接
2.使用
新建一个C++类,如下
template <typename PointT>
class PythonGridModule{
public:
using Ptr = std::shared_ptr<PythonGridModule<PointT>>;
PythonGridModule();
~PythonGridModule();
void exec_mesh_grid_py(typename pcl::PointCloud<PointT>::Ptr pointPtr, typename pcl::PointCloud<PointT>::Ptr out,
const GripStep &step);
private:
PyObject *pModule;
PyObject *hGridDataFunc;
};
定义一个模板类,用于调用python相关的库,其中class里面有两个属性pModule和hGridDataFunc,两个属性,指向python的两个模块以及对象。在构造函数的时候对持有对象进行初始化,并且在析构函数的时候,释放python的对象,如下所示:
long init_numpy() {
import_array();
}
template<typename Point>
PythonGridModule<Point>::PythonGridModule(){
setenv("PYTHONPATH", ".", 1);
Py_Initialize();
init_numpy();
this->pModule = PyImport_ImportModule("grid");
this->hGridDataFunc = PyObject_GetAttrString(this->pModule, "griddata");
}
template<typename Point>
PythonGridModule<Point>::~PythonGridModule() {
Py_Finalize();
}
上述代码,在构造函数里面初始化python相关的运行环境,然后在初始类里面的属性,其中pModule是python的模块,hGridDataFunc是模块里面的function,其中python文件为:
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
import matplotlib
def griddata(cloud, x_step, x_start, x_stop, y_step, y_start, y_stop):
points = cloud[:, 0:2]
values = cloud[:, 2]
x = np.arange(x_start, x_stop + x_step, x_step,dtype =np.float64)
y = np.arange(y_start, y_stop + y_step, y_step,dtype =np.float64)
grid_x, grid_y = np.meshgrid(x, y)
grid = griddata(points, values, (grid_x, grid_y), method='cubic')
return grid
这里实现了用scripy实现了一个曲面重建的算法,第一个参数为点云numpy数组,后面的是相关的步以及开始结束节点。
实现调用相关的python库函数如下:
template<typename PointT>
void PythonGridModule<PointT>::exec_mesh_grid_py(typename pcl::PointCloud<PointT>::Ptr pointPtr, typename pcl::PointCloud<PointT>::Ptr out,
const GripStep &step){
auto *data = new float[pointPtr->size() * 3];
auto j = 0;
for (auto i = 0; i < pointPtr->size(); i++) {
data[j] = pointPtr->points[i].x;
j++;
data[j] = pointPtr->points[i].y;
j++;
data[j] = pointPtr->points[i].z;
j++;
}
auto *pArgs = PyTuple_New(7);
npy_intp dim[2] = {(long) pointPtr->size(), 3};
PyTuple_SetItem(pArgs, 0, PyArray_SimpleNewFromData(2, dim, NPY_FLOAT, data));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("f", step.xStep));
PyTuple_SetItem(pArgs, 2, Py_BuildValue("f", step.xStart));
PyTuple_SetItem(pArgs, 3, Py_BuildValue("f", step.xStop));
PyTuple_SetItem(pArgs, 4, Py_BuildValue("f", step.yStep));
PyTuple_SetItem(pArgs, 5, Py_BuildValue("f", step.yStart));
PyTuple_SetItem(pArgs, 6, Py_BuildValue("f", step.yStop));
auto *func = PyObject_CallObject(this->hGridDataFunc, pArgs);
if (func) {
auto *pyArray = (PyArrayObject *) func;
npy_intp d1 = pyArray->dimensions[0];
npy_intp d2 = pyArray->dimensions[1];
out->width = d2;
out->height = d1;
out->resize(d1 * d2);
float x_index ;
float y_index = step.yStop;
int index = 0;
for (int i = 0; i < d1; i++) {
x_index = step.xStart;
for (int j = 0; j < d2; j++) {
out->points[index].x = x_index;
out->points[index].y = y_index;
out->points[index].z = ((double *) pyArray->data)[index];
if (out->is_dense && isnan(out->points[index].z))
out->is_dense = false;
x_index += step.xStep;
index++;
}
y_index += step.yStep;
}
} else {
PyErr_Print();
}
delete[] data;
}
上述代码也简单,通过先将点云数据转化成Numpy数组,然后执行python的griddata函数,再将执行完成后的结果写入到点云对象out里面去,完成一个完整的调用逻辑。
3. 总结
本文完成相关C++调用python相关的内容,也就是说点云数据可以转化为3维矩阵,可以采用python里面相关的机器学习算法进行后续的建模 ,优化,寻找最小值等等优化内容。