-
2021-07-31 22:48:38
pyntcloud是一个python3库,用于利用python科学堆栈的强大功能处理3d点云
与open3d相互转换:
import open3d as o3d from pyntcloud import PyntCloud # FROM Open3D original_triangle_mesh = o3d.io.read_triangle_mesh("diamond.ply") cloud = PyntCloud.from_instance("open3d", original_triangle_mesh) # TO Open3D cloud = PyntCloud.from_file("diamond.ply") converted_triangle_mesh = cloud.to_instance("open3d", mesh=True) # mesh=True by default
pyvista相互转换:
import pyvista as pv from pyntcloud import PyntCloud # FROM PyVista original_point_cloud = pv.read("diamond.ply") cloud = PyntCloud.from_instance("pyvista", original_point_cloud) # TO PyVista cloud = PyntCloud.from_file("diamond.ply") converted_triangle_mesh = cloud.to_instance("pyvista", mesh=True)
保存ply或者npz:
predicted_labels = user.infer(scan) cloud = pd.DataFrame(scan[:, :3], columns=['x', 'y', 'z']) cloud['seg_id'] = predicted_labels pcloud = pyntcloud.PyntCloud(cloud) pcloud.plot(use_as_color='seg_id', cmap="cool", backend="pythreejs") pcloud.to_file("out1.ply")
更多相关内容 -
PDAL_点云处理库_cloud_
2021-10-03 09:50:17Point cloud Data Abstraction -
PDF-SLAM-点云处理-pcl点云库入门到精通
2022-04-20 14:34:31PDF-SLAM-点云处理-pcl点云库入门到精通 -
利用OpenGL VC++编写的C++ 三维点云处理程序
2018-09-13 13:48:13利用OpenGL、VC++编写的C++,三维点云处理程序,对于学习图形学、C++、OpenGL、文件读写很有帮助,是一个三维软件公司编写代码一部分,尤其是OpenGL库文件相当管用。 有两个数据文件 鼠标默认操作:具体还在头文件中... -
pcl点云处理
2022-05-24 11:23:21pcl点云处理,拼接、分割、bin、pcd读取、格式转换PCL点云数据库的使用
主要使用CMake工具组织实现
PCL 1.9.1
LAS 1.8.1
Eigen 3.1.0
使用pcl与las库实现了,
- bin点云读取
- las点云读取
- 点云拼接
- 点云分块保存
其中还有其他的一些小功能,比如:获取指定路径下的指定后缀名称的文件名称,并按照名称排序,自定义STL模板的排序。
include文件夹下
#ifndef BUILDPRIORMAP_H #define OPTIMIZER_H #include <string> #include <iostream> #include <set> #include <vector> #include <fstream> #include <sstream> #include <liblas/liblas.hpp> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/filters/voxel_grid.h> #include <pcl/common/common.h> #include <pcl/kdtree/kdtree_flann.h> #include <pcl/features/normal_3d_omp.h> //使用OMP需要添加的头文件 #include <pcl/filters/passthrough.h> //分块使用 #include <pcl/common/transforms.h> namespace PCL_TOOLS { class BuildPriorMap { public: BuildPriorMap(std::string rootfile, std::string PriorMapDir); std::string rootfile; //原始的las地图路径 std::string PriorMapDir; //处理后的地图存储路径 std::string GroundTruth; // ground truth txt文件路径 std::string MapbinDir; //分块bin地图存储路径 std::string MappcdDir; //分块的pcd地图保存路径 pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloudPtr; //所有的点云地图 pcl::PointCloud<pcl::Normal>::Ptr pointCloudNormalsPtr; //所有的点云地图的法向量 public: void LoadFiles(std::string DirPath, std::string Suffix, std::vector<std::string> &files); //获取指定路径下的指定后缀的所有文件的文件名并排序 void MapPointXYZFilter(pcl::PointCloud<pcl::PointXYZ>::Ptr filterptr, float x = 0.2, float y = 0.2, float z = 0.2); //降采样 void readlas2pcl(bool bSaveAllPCD = false, bool pose2Identity = true); // las格式转换为pcl格式 void bin2pcd(std::string _MapbinDir, std::string _MappcdDir); //读取bin格式转化为pcd格式 void readpcd2pcl(std::string GroundTruth, bool bSaveAllPCD = false); // 使用ground truth位姿实现 pcd 点云拼接 void BuildNormalsMap(pcl::PointCloud<pcl::PointXYZ>::Ptr PointXYZPtr, pcl::PointCloud<pcl::Normal>::Ptr NormalPtr); //根据PointXYZ点云生成pcl::Normal点云地图 void SavePriorMap(unsigned int mapCubeSize = 50); //按照方格坐标切分保存点云地图 }; } #endif
src文件夹下
#include <BuildPriorMap.h> //模板函数:将string类型变量转换为常用的数值类型(此方法具有普遍适用性) template <class Type> Type stringToNum(const std::string &str) { std::istringstream iss(str); Type num; iss >> num; return num; } bool computePairNum(std::string pair1, std::string pair2) { return pair1 < pair2; } namespace PCL_TOOLS { /** * @brief Construct a new Build Prior Map:: Build Prior Map object * * @param rootfile urban格式数据集根目录 * @param PriorMapDir PCD格式先验地图输出保存路径 */ BuildPriorMap::BuildPriorMap(std::string rootfile, std::string PriorMapDir) : rootfile(rootfile), PriorMapDir(PriorMapDir), pointCloudPtr(new pcl::PointCloud<pcl::PointXYZ>), pointCloudNormalsPtr(new pcl::PointCloud<pcl::Normal>) { std::cout << "rootfile:" << rootfile << std::endl; std::cout << "PriorMapDir:" << PriorMapDir << std::endl; GroundTruth = rootfile + "07.txt"; MapbinDir = rootfile + "velodyne/"; MappcdDir = rootfile + "pcd/"; } /** * @brief * * @param dir 文件夹路径 * @param Suffix 要获取的文件后缀名 * @param files 获取文件路径名称存储向量 */ void BuildPriorMap::LoadFiles(std::string DirPath, std::string Suffix, std::vector<std::string> &files) { std::cout << "DirPath : " << DirPath << " get[*." << Suffix << "]files"; std::vector<std::string> file_lists; // step 1 获取指定后缀的所有文件 struct dirent *ptr; DIR *dir; dir = opendir(DirPath.c_str()); file_lists.clear(); while ((ptr = readdir(dir)) != NULL) { std::string tmp_file = ptr->d_name; if (tmp_file[0] == '.') { //取读取文件的第一个字符,如果为.则说明是隐藏文件不读取 continue; } if (Suffix.size() <= 0) { //如果无传入的类型,则该路径下所有文件都读取 file_lists.push_back(ptr->d_name); } else { if (tmp_file.size() < Suffix.size()) { //说明不可能,也就是文件名的长度小于后缀名这3长度 continue; } std::string tmp_cut_type = tmp_file.substr(tmp_file.size() - Suffix.size(), Suffix.size()); //截取后三位 if (tmp_cut_type == Suffix) { //如果截取文件名的后三位和传入的类型一致的话,添加这个文件的全称包括后缀 file_lists.push_back(ptr->d_name); } } } // step 2 对指定文件进行排序 if (file_lists.empty()) return; std::sort(file_lists.begin(), file_lists.end(), computePairNum); //按照computePairNum函数的方法进行排序 // step 3 保存排序结果 for (int i = 0; i < file_lists.size(); ++i) { std::string file = file_lists[i]; files.push_back(file); //将文件名字传入到pcd_filename里 } std::cout << "num:" << files.size() << std::endl; } /** * @brief PointXYZ地图进行下采样滤波 * * @param filterptr 输入地图 * @param x 滤波网格大小 * @param y 滤波网格大小 * @param z 滤波网格大小 */ void BuildPriorMap::MapPointXYZFilter(pcl::PointCloud<pcl::PointXYZ>::Ptr filterptr, float x, float y, float z) { pcl::VoxelGrid<pcl::PointXYZ> filter; //创建滤波器 filter.setInputCloud(filterptr); //输入要滤波的点云 filter.setLeafSize(x, y, z); //用0.2 x 0.2 x 0.2的立方体对点云进行稀疏化 filter.filter(*filterptr); //获得滤波结果 } /** * @brief 读取las数据格式转换为pcd格式 * * @param bSaveAllPCD 是否保存完整的pcd格式地图 * @param pose2Identity 是否对初始位姿进行坐标变换 */ void BuildPriorMap::readlas2pcl(bool bSaveAllPCD, bool pose2Identity) { std::string lasfile = rootfile + "sick_pointcloud.las"; std::ifstream inFile; inFile.open(lasfile, std::ios::in | std::ios::binary); liblas::ReaderFactory readLas; liblas::Reader reader = readLas.CreateWithStream(inFile); liblas::Header const &header = reader.GetHeader(); std::cout << "Compressed: " << (header.Compressed() == true ? "True" : "False") << std::endl; std::cout << "Signature: " << header.GetFileSignature() << std::endl; int count = header.GetPointRecordsCount(); while (reader.ReadNextPoint()) { liblas::Point const &point = reader.GetPoint(); double x, y, z; x = point.GetX(); y = point.GetY(); z = point.GetZ(); pcl::PointXYZ p(x, y, z); pointCloudPtr->push_back(p); } std::cout << "las2pcd :successed! Point num:" << count << std::endl; // MapPointXYZFilter(pointCloudPtr, 1, 1, 1); //滤波 //初始位姿转换到单位矩阵 Eigen::Matrix4f T_init = Eigen::Matrix4f::Identity(); if (pose2Identity) { std::ifstream inFilecsv(rootfile + "global_pose.csv", std::ios::in); std::string initPosestring; //读取csv以,为分割 std::getline(inFilecsv, initPosestring, '\n'); //取出一行 std::stringstream ss(initPosestring); std::string Pose_i; std::vector<float> Pose; while (std::getline(ss, Pose_i, ',')) { float temp; temp = stringToNum<double>(Pose_i); Pose.push_back(temp); } T_init(0, 0) = Pose[1]; T_init(0, 1) = Pose[2]; T_init(0, 2) = Pose[3]; T_init(0, 3) = Pose[4]; T_init(1, 0) = Pose[5]; T_init(1, 1) = Pose[6]; T_init(1, 2) = Pose[7]; T_init(1, 3) = Pose[8]; T_init(2, 0) = Pose[9]; T_init(2, 1) = Pose[10]; T_init(2, 2) = Pose[11]; T_init(2, 3) = Pose[12]; Eigen::Matrix4f trans = Eigen::Matrix4f::Identity(); // z轴旋转90° trans << 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1; // Urban39 Eigen::Matrix4f Vehicle2IMU; Vehicle2IMU << 1, 0, 0, -0.07, 0, 1, 0, 0, 0, 0, 1, 1.7, 0, 0, 0, 1; std::cout << "point[0] pose before:" << (*pointCloudPtr)[0] << std::endl; std::cout << "T_init:" << T_init << std::endl; pcl::transformPointCloud(*pointCloudPtr, *pointCloudPtr, Vehicle2IMU.inverse() * T_init.inverse()); //将点云进行旋转平移变换到IMU坐标系 std::cout << "point[0] pose after:" << (*pointCloudPtr)[0] << std::endl; std::cout << "pose2Identity succeed" << std::endl; inFilecsv.close(); } BuildNormalsMap(pointCloudPtr, pointCloudNormalsPtr); //构建法向量地图 // 调试 z 轴的方向, 使用pcl_viewer 查看pcd文件 /* { for (int index_i = -2; index_i < 2; ++index_i) { for (int index_j = -2; index_j < 2; ++index_j) { for (int j = 0; j < 200; j++) { pcl::PointXYZ point_z; point_z.x = index_i * 50; point_z.y = index_j * 50; point_z.z = 10 * j; pointCloudPtr->push_back(point_z); } } } } */ if (bSaveAllPCD) { std::string pcdfile = PriorMapDir + "allpointCloud.pcd"; pcl::io::savePCDFileASCII(pcdfile, *pointCloudPtr); std::cout << "allpointCloud.pcd save:" << pcdfile << std::endl; pcdfile = PriorMapDir + "allnoramls.pcd"; pcl::io::savePCDFileASCII(pcdfile, *pointCloudNormalsPtr); std::cout << "allnoramls.pcd save:" << pcdfile << std::endl; } inFile.close(); std::cout << "las2pcl finished!" << std::endl; } /** * @brief * * @param _MapbinDir bin格式地图路径 * @param _MappcdDir 保存pcd格式地图路径 */ void BuildPriorMap::bin2pcd(std::string _MapbinDir, std::string _MappcdDir) { std::cout << "load bin map from: " << _MapbinDir << " and save pcd map" << std::endl; //设置路径 MapbinDir = _MapbinDir; MappcdDir = _MappcdDir; //获取文件名称 std::vector<std::string> binfiles; std::string suffix = "bin"; LoadFiles(MapbinDir, suffix, binfiles); //读取bin int32_t num = 1000000; int32_t file_size; float *data; pcl::PointCloud<pcl::PointXYZI> point_cloud; for (int i = 0; i < binfiles.size(); ++i) { data = (float *)malloc(num * sizeof(float)); // pointers float *px = data + 0; float *py = data + 1; float *pz = data + 2; float *pr = data + 3; std::string binfile = MapbinDir + binfiles[i]; //存储路径 + 文件名 std::cout << "binfile:[" << binfile << "] "; std::fstream a_file(binfile.c_str(), std::ios::binary | std::ios::in | std::ios::ate); file_size = a_file.tellg(); std::cout << "file size: " << file_size << " "; a_file.seekg(0, std::ios::beg); if (!a_file.read(reinterpret_cast<char *>(data), file_size)) { std::cout << "Error reading from file" << std::endl; return; } point_cloud.clear(); point_cloud.width = (file_size / sizeof(float)) / 4; point_cloud.height = 1; point_cloud.is_dense = false; point_cloud.points.resize(point_cloud.width * point_cloud.height); std::cout << "resized to " << point_cloud.points.size() << std::endl; // fill in the point cloud for (int j = 0; j < point_cloud.points.size(); ++j) { point_cloud.points[j].x = *px; point_cloud.points[j].y = *py; point_cloud.points[j].z = *pz; point_cloud.points[j].intensity = *pr; px += 4; py += 4; pz += 4; pr += 4; } //保存pcd格式 std::string name = binfiles[i].substr(0, binfiles[i].size() - suffix.size() - 1); //去掉后缀名三位 std::string pcdfile = MappcdDir + name + ".pcd"; std::cout << "save:[" << pcdfile << "]" << std::endl; pcl::io::savePCDFileASCII(pcdfile, point_cloud); } std::cout << "bin2pcd finished!" << std::endl; } /** * @brief * * @param GroundTruth 位姿真值的 txt * @param bSaveAllPCD 是否保存拼接后的地图 */ void BuildPriorMap::readpcd2pcl(std::string _GroundTruth, bool bSaveAllPCD) { GroundTruth = _GroundTruth; //获取文件名称 std::vector<std::string> pcdfiles; std::string suffix = "pcd"; LoadFiles(MappcdDir, suffix, pcdfiles); //读取ground进行地图拼接 std::ifstream FileIn(GroundTruth); std::cout << "GroundTruth:[" << GroundTruth << "]" << std::endl; Eigen::Matrix4f pose_GT; Eigen::Matrix4f velo2cam, cam2velo, x_270; //给两个变换矩阵赋初值 cam2velo << 0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0.08, 0, 0, 0, 1; velo2cam << 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, -0.08, 0, 0, 0, 1; // !kitti的z轴是前进方向所以是不是应该按照x,z轴分块 // ! 要不就整体绕x旋转-90° //实现这一种方式吧 x_270 << 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1; int i = 0; pcl::PointCloud<pcl::PointXYZ>::Ptr source(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr target(new pcl::PointCloud<pcl::PointXYZ>); while (FileIn >> pose_GT(0, 0) >> pose_GT(0, 1) >> pose_GT(0, 2) >> pose_GT(0, 3) >> pose_GT(1, 0) >> pose_GT(1, 1) >> pose_GT(1, 2) >> pose_GT(1, 3) >> pose_GT(2, 0) >> pose_GT(2, 1) >> pose_GT(2, 2) >> pose_GT(2, 3)) { pose_GT(3, 0) = 0; pose_GT(3, 1) = 0; pose_GT(3, 2) = 0; pose_GT(3, 3) = 1; // pose_GT = cam2velo * pose_data2 * velo2cam; //激光坐标系下的 pose_GT = pose_GT * velo2cam; //相机坐标系下的 std::string pcdfile = MappcdDir + pcdfiles[i]; std::cout << "load:[" << pcdfile << "]"; pcl::io::loadPCDFile(pcdfile, *source); //读入pcd格式的文件 std::cout << " !" << std::endl; //将点云按照transform[i]的变换矩阵进行旋转平移变换,最终存入target中 MapPointXYZFilter(source, 0.2, 0.2, 0.2); //滤波 pcl::transformPointCloud(*source, *target, x_270 * pose_GT); //绕x轴再旋转90° //拼接 *pointCloudPtr = *pointCloudPtr + *target; i++; } std::vector<int> indices_src; //保存去除的点的索引 //移除 NaNs,从传感器获得的点云可能包含几种测量误差和/或不准确。其中之一是在一些点的坐标中存在NaN(不是数)值, pcl::removeNaNFromPointCloud(*pointCloudPtr, *pointCloudPtr, indices_src); BuildNormalsMap(pointCloudPtr, pointCloudNormalsPtr); //构建法向量地图 // 调试 z 轴的方向, 使用pcl_viewer 查看pcd文件 /* { for (int index_i = -2; index_i < 2; ++index_i) { for (int index_j = -2; index_j < 2; ++index_j) { for (int j = 0; j < 50; j++) { pcl::PointXYZ point_z; point_z.x = index_i * 50; point_z.y = index_j * 50; point_z.z = 10 * j; pointCloudPtr->push_back(point_z); } } } } */ if (bSaveAllPCD) { std::string pcdfile = PriorMapDir + "allpointCloud.pcd"; pcl::io::savePCDFileASCII(pcdfile, *pointCloudPtr); std::cout << "allpointCloud.pcd save:" << pcdfile << std::endl; pcdfile = PriorMapDir + "allnoramls.pcd"; pcl::io::savePCDFileASCII(pcdfile, *pointCloudNormalsPtr); std::cout << "allnoramls.pcd save:" << pcdfile << std::endl; } FileIn.close(); std::cout << "pcl2pcl finished!" << std::endl; } void BuildPriorMap::BuildNormalsMap(pcl::PointCloud<pcl::PointXYZ>::Ptr PointXYZPtr, pcl::PointCloud<pcl::Normal>::Ptr NormalPtr) { //计算法线 pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> n; // OMP加速 //建立kdtree来进行近邻点集搜索 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>()); n.setNumberOfThreads(10); //设置openMP的线程数 // n.setViewPoint(0,0,0);//设置视点,默认为(0,0,0) n.setInputCloud(PointXYZPtr); n.setSearchMethod(tree); n.setKSearch(20); //点云法向计算时,需要所搜的近邻点大小 // n.setRadiusSearch(0.03);//半径搜素 n.compute(*NormalPtr); //开始进行法向计 } /** * @brief 保存分块地图 * * @param mapCubeSize 分块地图大小 */ void BuildPriorMap::SavePriorMap(unsigned int mapCubeSize) { // step 1 写构造config 保存分块大小,以及初始化地图是加载哪几块 std::ofstream outFile; outFile.open(PriorMapDir + "config.txt", std::ios::out); outFile << mapCubeSize << std::endl; // step 2 分块保存地图 std::set<std::pair<int, int>> keys; //分块地图的索引 pcl::PointXYZ min, max; pcl::getMinMax3D(*pointCloudPtr, min, max); std::cout << "x:[" << min.x << "," << max.x << "]" << std::endl; std::cout << "y:[" << min.y << "," << max.y << "]" << std::endl; std::cout << "z:[" << min.z << "," << max.z << "]" << std::endl; //计算keys范围 std::pair<int, int> key_x, key_y; key_x.first = std::floor(min.x / mapCubeSize); key_x.second = std::floor(max.x / mapCubeSize); key_y.first = std::floor(min.y / mapCubeSize); key_y.second = std::floor(max.y / mapCubeSize); std::cout << "key_x:[" << key_x.first << "," << key_x.second << "]" << std::endl; std::cout << "key_y:[" << key_y.first << "," << key_y.second << "]" << std::endl; //保存索引范围与分块地图 for (int index_i = key_x.first; index_i < key_x.second + 1; ++index_i) { pcl::PointCloud<pcl::PointXYZ>::Ptr subPointCloudPtr(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr subTempPtr(new pcl::PointCloud<pcl::PointXYZ>); pcl::PassThrough<pcl::PointXYZ> pass_x, pass_y; // 声明直通滤波 pass_x.setInputCloud(pointCloudPtr); // 传入点云数据 pass_x.setFilterFieldName("x"); // 设置操作的坐标轴 pass_x.setFilterLimits(static_cast<int>(index_i * mapCubeSize), static_cast<int>((index_i + 1) * mapCubeSize)); // 设置坐标范围 // pass_x.setFilterLimitsNegative(true);//保存范围内or范围外 pass_x.filter(*subTempPtr); // 进行滤波输出 for (int index_j = key_y.first; index_j < key_y.second + 1; ++index_j) { pass_y.setInputCloud(subTempPtr); // 传入点云数据 pass_y.setFilterFieldName("y"); // 设置操作的坐标轴 pass_y.setFilterLimits(static_cast<int>(index_j * mapCubeSize), static_cast<int>((index_j + 1) * mapCubeSize)); // 设置坐标范围 // pass_y.setFilterLimitsNegative(true);//保存范围内or范围外 pass_y.filter(*subPointCloudPtr); // 进行滤波输出 if (!(*subPointCloudPtr).empty()) { //保存索引 std::pair<int, int> key; key.first = index_i; key.second = index_j; outFile << index_i << " " << index_j << std::endl; keys.insert(key); std::string subPointCloudFile = PriorMapDir + std::to_string(mapCubeSize) + "_" + std::to_string(index_i) + "_" + std::to_string(index_j) + "_points.pcd"; //分块的3D点云pcd<cubesize_indexi_indexj_point.pcd> pcl::io::savePCDFileASCII(subPointCloudFile, *subPointCloudPtr); std::cout << "sub PointCloud save:" << subPointCloudFile << std::endl; pcl::PointCloud<pcl::Normal>::Ptr subnormalsPtr(new pcl::PointCloud<pcl::Normal>); BuildNormalsMap(subPointCloudPtr, subnormalsPtr); std::string subNormalsFile = PriorMapDir + std::to_string(mapCubeSize) + "_" + std::to_string(index_i) + "_" + std::to_string(index_j) + "_normals.pcd"; //分块的法向量 pcl::io::savePCDFileASCII(subNormalsFile, *subnormalsPtr); std::cout << "sub NormalsFile save:" << subNormalsFile << std::endl; } } } outFile.close(); } }
Examples文件夹
KaistUrban.cc
#include <iostream> #include <BuildPriorMap.h> int main(int agrc, char **argv) { // step 1 读取lasmap std::string rootfile = "/home/xxx/urban39/"; std::string PriorMapDir = "/home/xxx/urban39/prior_map/"; PCL_TOOLS::BuildPriorMap kaisturban(rootfile, PriorMapDir); kaisturban.readlas2pcl(false, true); // kaisturban.readlas2pcl(true, false); // step 2 分块保存 kaisturban.SavePriorMap(50); std::cout << "save successed!" << std::endl; return 0; }
Kitti.cc
#include <iostream> #include <BuildPriorMap.h> int main(int agrc, char **argv) { // step 1 读取bin2pcd std::string rootfile = "/home/xxx/datas/KITTI/07/"; std::string PriorMapDir = "/home/xxx/datas/KITTI/07/prior_map/"; std::string GroundTruth = "/home/xxx/datas/KITTI/07/07.txt"; std::string MapbinDir = "/home/xxx/datas/KITTI/07/velodyne/"; std::string MappcdDir = "/home/xxx/datas/KITTI/07/pcd/"; PCL_TOOLS::BuildPriorMap kitti(rootfile, PriorMapDir); // step 1.1 bin转化pcd kitti.bin2pcd(MapbinDir, MappcdDir); // step 2 根据GT拼接pcd kitti.readpcd2pcl(GroundTruth, true); // step 3 分块保存 kitti.SavePriorMap(50); std::cout << "save successed!" << std::endl; return 0; }
CMakeLists.txt
cmake_minimum_required(VERSION 2.8) project(pcl_tools) IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE = Release) ENDIF() MESSAGE("Build type: " ${CMAKE_BUILD_TYPE}) set(CMAKE_CXX_STANDARD 17) LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules) find_package(Eigen3 REQUIRED) find_package(PCL REQUIRED) find_package(libLAS REQUIRED) include_directories( ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${EIGEN3_INCLUDE_DIR} ${PCL_INCLUDE_DIRS} ${LIBLAS_INCLUDE_DIR} ) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) add_library(${PROJECT_NAME} SHARED src/BuildPriorMap.cc include/BuildPriorMap.h ) target_link_libraries(${PROJECT_NAME} ${EIGEN3_LIBS} ${PCL_LIBRARIES} ${libLAS_LIBRARIES} ) # examples set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) # kaist数据集的先验地图处理 add_executable(kaisturban Examples/KaistUrban.cc) target_link_libraries(kaisturban ${PROJECT_NAME}) # kitti数据集的先验地图处理 add_executable(kitti Examples/Kitti.cc) target_link_libraries(kitti ${PROJECT_NAME}) #求解坐标变换 add_executable(matrix2 Examples/matrix2.cc) target_link_libraries(matrix2 ${EIGEN3_LIBS})
-
PDAL点云处理库介绍
2021-02-20 08:01:41点击“蓝字”关注点云PCL,选择“星标”获取最新文章PDAL是点云数据处理的库。这是一个C/C++开源库,用于点云数据的转换和处理。尽管该库中许多工具的重点和发展都起源于激光雷达点云数据的...点击“蓝字”关注点云PCL,选择“星标”获取最新文章
PDAL是点云数据处理的库。这是一个C/C++开源库,用于点云数据的转换和处理。尽管该库中许多工具的重点和发展都起源于激光雷达点云数据的处理,但它也不限于激光雷达数据。
什么是PDAL?
PDAL是点云数据处理的库。这是一个C/C++开源库,用于点云数据的转换和处理。尽管该库中许多工具的重点和发展都起源于激光雷达点云数据的处理,但它也不限于激光雷达数据。
一个简单的PDAL点云处理流程,由读文件、滤波模块和写点云模块组成
组成此操作以将数据重新投影并加载到PostgreSQL的PDAL JSON流程如下所示:
{ "pipeline":[ { "type":"readers.las", "filename":"input.las" }, { "type":"filters.reprojection", "out_srs":"EPSG:3857" }, { "type":"writers.pgpointcloud", "connection":"host='localhost' dbname='lidar' user='hobu'", "table":"output", "srid":"3857" } ]}
PDAL可以为点云的滤波、剪裁、平铺、转换为处理流程以及必要时重用等操作组成中间模块。它允许您将这些流程定义为JSON文件,并提供一个pipeline来执行它们。
它与其他工具有何不同?
LAStools
Martin Isenburg(https://www.cs.unc.edu/~isenburg/)
的LAStools是可用于激光雷达处理的最常见的开源处理工具套件之一。PDAL在许多重要方面的理念不同:
1,PDAL的所有模块都是在OSI许可下作为开源软件发布的。
2,PDAL允许开发人员在处理流程作为专有扩展模块。这些可能是自定义格式读取器、专门的算法或整个方案。
3,PDAL可以对任何格式的点云数据进行操作,而不仅仅是ASPRS LAS。LAStools可以读取和写入除LAS以外的其他格式,但会将所有数据与其对LAS数据的内部处理相关联,从而将其限制为LAS格式提供的维度类型。
4,PDAL由用户使用其声明性JSON语法进行协调。LAStools是通过将许多小型的、专门化的命令行实用程序与复杂的参数连接在一起。
5,PDAL是一个开源项目,它的所有开发活动都可以在线获得https://github.com/PDAL/PDAL
与PCL的区别
PCL是点云数据的一个补充而不是替代的开源软件处理的套件。PCL库的开发专注于算法开发、机器人和计算机视觉以及实时激光扫描仪处理。PDAL可以读写PCL的PCD格式。
与Potree的区别
Potree是一个WebGL HTML5点云渲染器,使用ASPRS LAS和LASzip压缩LAS。你可以在https://github.com/potree/potree/进行访问
其他开源点云库
其他开源点云软件倾向于桌面GUI,而不是以库为中心。它们包括一些处理操作,有时甚至嵌入PDAL之类的工具。这些其他工具包括:
libLAS
CloudCompare
Fusion
OrfeoToolbox
libLAS项目是一个早于PDAL的开源项目,它提供了PDAL提供的一些处理功能。它目前处于维护模式,因为它依赖于LAS,相关的LAStools功能作为开源库发布,以及Python LAS软件的完成。
PDAL是从何而来?
PDAL借鉴了另一个非常流行的开源项目GDAL。GDAL是地理空间数据抽象库,它在整个地理空间软件行业中用于为各种光栅和矢量格式提供处理支持。PDAL为点云数据类型提供了相同的功能。PDAL是在为美国陆军工程兵团CRREL网格项目开发数据库存储和访问功能的基础上发展起来的。正在蔓延到libLAS中的功能被引入了一个新的库中,它的设计初衷是模仿地理空间软件领域中成功的提取、转换和加载库。随着其他软件开发人员使用PDAL为他们的软件提供点云数据转换和处理能力,PDAL已经吸引了更多的贡献者。
点云数据与栅格或矢量地理数据有何不同?
点云数据确实非常像许多地理空间从业者所熟悉的典型矢量点数据类型,但它们的庞大的数量会带来一些重大挑战。除了它们的X、Y和Z位置之外,每个点通常都有其他事物的完整属性信息,如强度、时间、RGB等。点云数据的典型矢量可能会达到一百万个左右的特征。所以这样的点云很快就会进入数十亿甚至万亿的规模,因此必须使用专门的处理和管理技术来有效地处理如此多的数据。用于提取和利用点云数据的算法也明显不同于典型的矢量GIS工作流程,数据组织对于有效利用可用计算非常重要。这些特性需要一个面向这些方法的库,PDAL实现了这一点。
PDAL擅长哪些任务?
PDAL在点云数据转换工作流程中非常有用。它允许用户通过为内容提供抽象API将算法应用于数据,从而让用户不用担心许多数据格式问题。PDAL的格式问题确实带来了一些间接成本。但是在大多数情况下,这并不重要,对于具有特定数据的特定处理工作流,专用工具肯定会优于它。PDAL还提供了一个简单的命令行,它通过Numpy扩展了简单的通用Python处理。这些特性使它对软件开发人员、数据管理人员和科学研究人员具有吸引力。
PDAL的弱点是什么?
PDAL没有提供友好的GUI界面,需要对点云的滤波、读写器有一定的了解。
PDAL首先是一个软件库。一个成功的软件库必须满足软件开发人员的需求,他们使用它为自己的软件提供软件功能。除了用作软件库之外,PDAL还提供了一些命令行应用程序,用户可以利用这些应用程序方便地用PDAL点云转换、过滤和处理数据。最后,PDAL以嵌入式操作和Python扩展的形式提供Python支持。
核心C++软件库
PDAL提供了一个C++ API开发软件,可以在自己的软件中提供点云处理能力。PDAL是跨平台C++,可以在Linux、OS X和Windows上编译运行。
开源库 https://github.com/PDAL/PDAL.git
资源
三维点云论文及相关应用分享
【点云论文速读】基于激光雷达的里程计及3D点云地图中的定位方法
3D-MiniNet: 从点云中学习2D表示以实现快速有效的3D LIDAR语义分割(2020)
PCL中outofcore模块---基于核外八叉树的大规模点云的显示
更多文章可查看:点云学习历史文章大汇总
SLAM及AR相关分享
往期线上分享录播汇总
如果你对本文感兴趣,请点击“原文阅读”获取知识星球二维码,务必按照“姓名+学校/公司+研究方向”备注加入免费知识星球,免费下载pdf文档,和更多热爱分享的小伙伴一起交流吧!
以上内容如有错误请留言评论,欢迎指正交流。如有侵权,请联系删除
扫描二维码
关注我们
让我们一起分享一起学习吧!期待有想法,乐于分享的小伙伴加入免费星球注入爱分享的新鲜活力。分享的主题包含但不限于三维视觉,点云,高精地图,自动驾驶,以及机器人等相关的领域。
分享及合作方式:微信“920177957”(需要按要求备注) 联系邮箱:dianyunpcl@163.com,欢迎企业来联系公众号展开合作。
点一下“在看”你会更好看耶
-
点云处理库中常见的几种滤波方法
2022-06-04 16:21:04本文介绍了点云处理库常见的几种滤波方法,包括直通滤波器,体素滤波器,统计滤波器,半径滤波器。本文介绍了点云处理库中常见的集中滤波方法,包括直通滤波器,体素滤波器,统计滤波器,半径滤波器。
1、直通滤波器
使用直通滤波器,确定点云在x或y方向上的范围,可较快去除一定范围为之外的点云,达到第一步粗处理的目的。
pcl::PassThrough<pcl::PointXYZ> passthrough; passthrough.setInputCloud(cloud);//输入点云 passthrough.setFilterFieldName("z");//对z轴进行操作 passthrough.setFilterLimits(0.0, 1.0);//设置直通滤波器操作范围 passthrough.setFilterLimitsNegative(true);//true表示保留范围内,false表示保留范围外 passthrough.filter(*cloud_after_PassThrough);//执行滤波,
坐标轴表示为红色(x)、绿色(y) 和蓝色(z)。这五个点用绿色表示过滤后剩余的点,红色表示已被过滤器删除的点。
2、体素滤波器
体素的概念类似于像素,使用AABB包围盒将点云数据体素化,一般体素越密集的地方信息越多,噪音点及离群点可通过体素网格去除。另一方面如果使用高分辨率相机等设备对点云进行采集,往往点云会较为密集。过多的点云数量会对后续分割工作带来困难。体素滤波器可以达到向下采样同时不破坏点云本身几何结构的功能。
pcl::VoxelGrid<pcl::PCLPointCloud2> sor; sor.setInputCloud (cloud); sor.setLeafSize (0.01f, 0.01f, 0.01f); sor.filter (*cloud_filtered);
体素滤波前
体素滤波后
3、统计滤波器
统计滤波器主要用于去除明显离群点。 离群点特征在空间中分布稀疏。定义某处点云小于某个密度,既点云无效。计算每个点到其最近的k个点平均距离。
其大致的原理思路是:
- 对每个点,我们计算它到它的K邻域内所有点的平均距离d。对输入点云中的每个点都进行这样的计算,即每个点都可以求得其对应的d,因此可以得到一个包含各点d值的数组,记为distance。
- 对于输入点云中的所有点,假设得到distance数组中的各元素构成一个高斯分布,该数组即为一个样本,样本容量为点云包含的点数目。高斯分布曲线的形状由样本的均值和标准差决定,d值在标准范围(由样本的均值和方差定义)之外的对应点,可被定义为离群点并可从数据集中去除掉。
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; sor.setInputCloud (cloud); sor.setMeanK (50); sor.setStddevMulThresh (1.0); sor.filter (*cloud_filtered);
4、半径滤波器
点云半径滤波也叫基于连通分析的点云滤波,该方法的基本思想是假定原始点云中每个激光点在指定的半径邻域中至少包含一定数量的近邻点。原始点云中符合假设条件的激光点被视为正常点进行保留,反之,则视为噪声点并进行去除。该方法对原始激光点云中存在的一些悬空的孤立点或无效点具有很好的去除效果。
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filter(new pcl::PointCloud<pcl::PointXYZ>); pcl::RadiusOutlierRemoval<pcl::PointXYZ> sor; sor.setInputCloud(cloud); sor.setRadiusSearch(0.02); sor.setMinNeighborsInRadius(15); sor.setNegative(false); sor.filter(*cloud_filter);
-
点云:python版本的点云数据处理库
2020-10-27 20:20:28libLAS是一个C/C++/Python库(接触的第一个点云处理库),用于读写LAS格式的点云。libLAS支持ASPRS LAS格式规范版本:1.0、1.1、1.2和1.3(基本支持)。虽然libLAS已经被PDAL / Laspy取代,但不可否认,它是一个很... -
点云库PCL学习教程+源码
2020-08-17 22:10:09这是学习点云处理的入门教程,其中附带源码,有利于大家快速查找,学习。主要针对激光雷达场景使用。使用方便,上手简单,容易掌握。 -
点云库PCL学习教程.pdf
2021-11-15 20:34:08点云库PCL学习教程.pdf 点云库PCL学习教程.pdf 点云库PCL学习教程.pdf 点云库PCL学习教程.pdf -
C#界面中实现点云数据的动态显示
2022-03-23 16:02:441、该资源采用C++和PCL编写用于点云显示的DLL动态库,实现了点云显示与软件窗体的链接,属于开发点云软件中显示的必备功能。 2、本资源提供了C# 端、C++端dll开发的全部代码,可以在该框架上添加其他的软件功能... -
开源项目推荐:3D点云处理开源库PCL/Open3d/Easy3D/PDAL/LAStools
2020-09-25 11:07:01(Point Cloud Library)是在吸收了前人点云相关研究基础上建立起来的大型跨平台开源C++编程库,它实现了大量点云相关的通用算法和高效数据结构,涉及到点云获取、滤波、分割、配准、检索、特征提取、识别、追踪、... -
《点云库pcl点云学习教程》1-16章清晰完整版
2019-03-12 16:26:39《点云库pcl点云学习教程》1-16章完整版PDF清晰带有书签方便阅读 -
VCG库读取点云文件并绘制
2016-04-01 11:59:06读取点云文件,用VCG库对其进行处理,返回三角形网格数据,然后用OpenTK进行绘制。能处理海量的点云文件,300W点数据处理只需10S左右! -
PCL点云库配置
2021-08-17 15:50:12PCL1.8.1+VScode2017+win10 一、下载PCL包 注意首先需要下载两个PCL的包 ... 在此界面一直下拉,找到下图中的两个文件,都下载下来 ...关于win32,win64是指VScode编译器环境的...二、安装PCL点云库 点击下一步 ... -
点云处理软件/库
2021-01-21 16:05:04PDAL库 C/C++ 好像多用于处理激光雷达点云数据,但是不限于激光雷达数据 https://mp.weixin.qq.com/s/Lysn4VcU-WXy6m0XyfrYDQ -
【点云处理】CUDA-PCL 1.0在robosense激光雷达上的地面点分割实验
2021-12-04 16:15:02CUDA PCL 1.0是基于CUDA开发的点云处理库,目前为止提供了5个库:cluster、octree、ICP、segmentation和filter。本文只实验segmentation库在Robosense激光雷达下的地面分效果。关于雷达的基本信息如下图所示。 我... -
点云 相关的一些开源代码库
2022-05-13 10:44:41开源点云处理库总结,对比 -
Python-pyntcloud是一个用于处理3D点云的Python库
2019-08-10 22:38:19pyntcloud is a Python library for working with 3D point clouds. -
点云库PCL学习教程 2
2018-06-01 00:07:12点云处理技术广泛应用在逆向工程、CAD/CAM、机器人学、激光遥感测量、机器视觉、虚拟现实、人机交互、立体3D影像等诸多领域。由于其涉及计算机学、图形学、人工智能、模式识别、几何计算、传感器等诸多学科,但一直... -
pclpy:点云库(PCL)的Python绑定
2021-05-06 21:40:35点云库(PCL)的Python绑定。 使用CppHeaderParser和pybind11从标头生成。 使用conda install -c conda-forge -c davidcaron pclpy : conda install -c conda-forge -c davidcaron pclpy (请参见下面的安装) ... -
点云库PCL的使用(与SLAM相关,一般在ROS系统下)
2022-05-04 16:01:31点云库PCL的使用 -
点云处理相关实用软件
2019-03-09 16:17:40@[TOC](点云处理相关实用软件) ##cloudcompare 软件及源码下载http://www.cloudcompare.org/ 简易版使用教程https://blog.csdn.net/huihut/article/details/69399050 ##Geometry++ 教程:... -
cpp-PCL点云库是一个独立的大规模的开放项目用于2D3D图像和点云处理
2019-08-16 03:00:02PCL :点云库是一个独立的,大规模的开放项目,用于2D/3D图像和点云处理 -
点云3d检测模型pointpillar
2021-03-01 09:32:03PointPillars一个来自工业界的模型.https://arxiv.org/abs/1812.057843D目标检测通常做法3d卷积投影到前平面在bird-view上操作处理思路依然是3d转2d,先把3维的点云转成2d的伪图像.Feature Net把点云数据处理成类似... -
PCL点云处理_安装及环境配置(1)
2021-12-21 14:27:22PCL点云 -
pyntcloud是用于处理3D点云的Python库。-Python开发
2021-05-25 16:15:57pyntcloud是用于处理3D点云的Python库。 再次使点云变得有趣pyntcloud是一个Python 3库,可利用Python科学堆栈的强大功能来处理3D点云。 示例(我们建议您通过启动Binder尝试这些示例。)文档安装conda install ...