C++pcl学习

点云数据

pcl::PointCloud中包含points , width , height 。points是一个vector容器包含点云中所有的点,可以通过下下标来访问所有的点。

一、点的数据类型

1、PointXYZ

pcl::PointCloudpcl::PointXYZ
成员变量: float x, y, z;
最常见的一个点数据类型,因为它只包含三维xyz坐标信息, 值得注意的是它多加了一个浮点数来满足内存对齐

//访问xyz方式一
float x = cloud.points[i].data[0];
float y = cloud.points[i].data[1];
float z = cloud.points[i].data[2];

//访问xyz方式二
float x = cloud.points[i].x;
float y = cloud.points[i].y;
float z = cloud.points[i].z;

2、PointXYZI

pcl::PointCloudpcl::PointXYZI
成员变量: float x, y, z, intensity; 表示XYZ信息加上强度信息的类型。

	pcl::PointCloud<pcl::PointXYZI>::Ptr boundingbox(new pcl::PointCloud<pcl::PointXYZI>)
   for (size_t i = 0; i < boundingbox->points.size(); ++i)
    {
        std::cout << "polygons:" + std::to_string(i) + ": "
                  << surface_hull->points[i].x << ","
                  << surface_hull->points[i].y << ","
                  << surface_hull->points[i].z << ","
                  << std::endl;
    }

PCL:点云滤波

https://blog.csdn.net/weixin_46098577/article/details/114385690

一、获取固定区域点云

(一)通过凸包

凸包和凹包的概念: https://blog.csdn.net/com1098247427/article/details/120664905
凸包分割点云:https://blog.csdn.net/qq_45006390/article/details/119107777

使用方法:通过设置一系列点(boundingbox)来划定一个区域,获取输入点云在这个区域中的点。boundingbox这些点围成的可以是一个三维图形也可以是一个平面图形。
1.如果boundingbox设置为一个水平平面,setDimension和setDim函数设置为2 (两个函数维度必须设置相同),那么保留的点云为水平平面竖直向下扫过的立体点云。
2.如果boundingbox设置为一个三维立体图形,setDimension和setDim函数设置为3 (两个函数维度必须设置相同),那么保留的点云为设置的三维立体图形包含的点云。

void cropHull(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, pcl::PointCloud<pcl::PointXYZI>::Ptr boundingbox, pcl::PointCloud<pcl::PointXYZI>::Ptr objects)
{
    pcl::PointCloud<pcl::PointXYZI>::Ptr surface_hull (new pcl::PointCloud<pcl::PointXYZI>);

    //构造凸包
    pcl::ConvexHull<pcl::PointXYZI> hull;//创建凸包对象
    hull.setInputCloud(boundingbox);	//设置输入点云:封闭区域顶点点云
    hull.setDimension(3);				//设置凸包维度
    std::vector<pcl::Vertices> polygons;//设置Vertices类型的向量,用于保存凸包顶点
    hull.reconstruct(*surface_hull, polygons);//计算凸包结果

    std::cout << "polygons size: " << polygons.size() << std::endl;
    //polygons中每个向量中保存的是每个顶点的索引,索引和surface_hull中的点对应
//    for (size_t i = 0; i < boundingbox->points.size(); ++i)
//    {
//        std::cout << "polygons:" + std::to_string(i) + ": "
//                  << surface_hull->points[i].x << ","
//                  << surface_hull->points[i].y << ","
//                  << surface_hull->points[i].z << ","
//                  << std::endl;
//    }


    //CropHull滤波
    pcl::CropHull<pcl::PointXYZI> CH;//创建CropHull滤波对象
    CH.setDim(3);					//设置维度,与凸包维度一致
    CH.setInputCloud(cloud);		//设置需要滤波的点云
    CH.setHullIndices(polygons);	//输入封闭区域的顶点
    CH.setHullCloud(surface_hull);	//输入封闭区域的形状
    CH.filter(*objects);			//执行CropHull滤波,存储结果于objects
}

//设置封闭范围顶点
    pcl::PointCloud<pcl::PointXYZI>::Ptr boundingbox(new pcl::PointCloud<pcl::PointXYZI>);
    pcl::PointXYZI point[8];
    point[0].x = 1.8f;
    point[0].y = 0.3f;
    point[0].z = -1.0f;
    point[0].intensity = 1.0f;

    point[1].x = 1.8f;
    point[1].y = 0.0f;
    point[1].z = -1.0f;
    point[1].intensity = 1.0f;

    point[2].x = 2.0f;
    point[2].y = 0.3f;
    point[2].z = -1.0f;
    point[2].intensity = 1.0f;

    point[3].x = 2.0f;
    point[3].y = 0.0f;
    point[3].z = -1.0f;
    point[3].intensity = 1.0f;

    point[4].x = 2.0f;
    point[4].y = 0.3f;
    point[4].z = -0.5f;
    point[4].intensity = 1.0f;

    point[5].x = 2.0f;
    point[5].y = 0.0f;
    point[5].z = -0.5f;
    point[5].intensity = 1.0f;

    point[6].x = 1.8f;
    point[6].y = 0.3f;
    point[6].z = -0.5f;
    point[6].intensity = 1.0f;

    point[7].x = 1.8f;
    point[7].y = 0.0f;
    point[7].z = -0.5f;
    point[7].intensity = 1.0f;

    for(int i = 0; i < 8; i++)
    {
        std::cout << "i: " << std::to_string(i) << std::endl;
        boundingbox->push_back(point[i]);
    }

    std::cout << "voxel_ptr size: " << voxel_ptr->size() << std::endl;
    cropHull(voxel_ptr, boundingbox, crophull_ptr);

(二)通过pcl::CropBoxpcl::PointXYZI

//获取立方体内点
void CropBoxFilter(const PointCloudTypePtr& cloud_in, Eigen::Vector4f& min_point, Eigen::Vector4f& max_point,bool negative,PointCloudType& cloud_out)
{
    cloud_out.clear();
    if(cloud_in->empty())
        return ;

    pcl::CropBox<pcl::PointXYZI> clipper;

    // min_point << bounds[0], bounds[2], bounds[4], 1.0;//cube的两个对角点
    // max_point << bounds[1], bounds[3], bounds[5], 1.0;

     clipper.setMin(min_point);
     clipper.setMax(max_point);
     clipper.setInputCloud(cloud_in);
     clipper.setNegative(negative);//默认为false
     clipper.filter(cloud_out);

     pcl::PointIndices indexs;
     clipper.getRemovedIndices (indexs);

}
min_point,max_point为cube的两个对角点

DBSCAN算法:

一、DBSCAN算法原理:

(1)DBSCAN通过检查数据集中每点的Eps邻域来搜索簇,如果点p的Eps邻域包含的点多于MinPts个,则创建一个以p为核心对象的簇;
(2)然后,DBSCAN迭代地聚集从这些核心对象直接密度可达的对象,这个过程可能涉及一些密度可达簇的合并;
(3)当没有新的点添加到任何簇时,该过程结束。

简单理解:在每个点的Eps邻域内拥有点的个数多于MinPts,那么认为p为核心对象簇的一员。即dbscan认为密度可达的点即为一个簇,这也是dbscan聚类的核心思想。根据密度可达的定义,在聚类过程中,除了第一个和最后一个点之外(可以为核心对象或者边界对象),其余点必须是核心对象才可以。

二、优缺点

优点:
(1)聚类速度快且能够有效处理噪声点和发现任意形状的空间聚类;
(2)与K-MEANS比较起来,不需要输入要划分的聚类个数;
(3)聚类簇的形状没有偏倚;
(4)可以在需要时输入过滤噪声的参数。

缺点:
(1)当数据量增大时,要求较大的内存支持I/O消耗也很大;
(2)当空间聚类的密度不均匀、聚类间距差相差很大时,聚类质量较差,因为这种情况下参数MinPts和Eps选取困难。
(3)算法聚类效果依赖与距离公式选取,实际应用中常用欧式距离,对于高维数据,存在“维数灾难”。

三、代码实现

// 依赖项:pcl1.9.1
//时间:2021.06.25
//功能:实现点云dbscan聚类
//
#pragma once

#include <iostream>
#include<string>
#include<vector>

#include<pcl/io/pcd_io.h>
#include<pcl/point_cloud.h>
#include<pcl/point_types.h>
#include<pcl/kdtree/kdtree_flann.h>
#include<pcl/visualization/cloud_viewer.h>

typedef pcl::PointXYZ pointT;
typedef pcl::PointCloud<pointT> cloud;

//const cloud::Ptr& cloud_in,输入点云
//std::vector<std::vector<int>> &clusters_index, 索引
//const double& r, Eps邻域半径,单位:m
//const int& size,Eps邻域包含的点的最小个数MinPts
bool dbscan(const cloud::Ptr& cloud_in, std::vector<std::vector<int>> &clusters_index, const double& r, const int& size)
{
    if (!cloud_in->size())
        return false;
    //LOG()
    pcl::KdTreeFLANN<pointT> tree;
    tree.setInputCloud(cloud_in);
    std::vector<bool> cloud_processed(cloud_in->size(), false);

    for (size_t i = 0; i < cloud_in->points.size(); ++i)
    {
        if (cloud_processed[i])
        {
            continue;
        }

        std::vector<int>seed_queue;
        //检查近邻数是否大于给定的size(判断是否是核心对象)
        std::vector<int> indices_cloud;
        std::vector<float> dists_cloud;
        if (tree.radiusSearch(cloud_in->points[i], r, indices_cloud, dists_cloud) >= size)
        {
            seed_queue.push_back(i);
            cloud_processed[i] = true;
        }
        else
        {
            //cloud_processed[i] = true;
            continue;
        }

        int seed_index = 0;
        while (seed_index < seed_queue.size())
        {
            std::vector<int> indices;
            std::vector<float> dists;
            if (tree.radiusSearch(cloud_in->points[seed_queue[seed_index]], r, indices, dists) < size)//函数返回值为近邻数量
            {
                //cloud_processed[i] = true;//不满足<size可能是边界点,也可能是簇的一部分,不能标记为已处理
                ++seed_index;
                continue;
            }
            for (size_t j = 0; j < indices.size(); ++j)
            {
                if (cloud_processed[indices[j]])
                {
                    continue;
                }
                seed_queue.push_back(indices[j]);
                cloud_processed[indices[j]] = true;
            }
            ++seed_index;                
        }
        clusters_index.push_back(seed_queue);               
      
    }
   // std::cout << clusters_index.size() << std::endl;

    if (clusters_index.size())
        return true;
    else
        return false;
}

int main()
{
    cloud::Ptr cloud_in(new cloud);
    std::vector<std::vector<int>> clusters_index;
    pcl::io::loadPCDFile<pcl::PointXYZ>("2.pcd", *cloud_in);
    dbscan(cloud_in, clusters_index, 0.1, 10);
    std::cout << clusters_index.size() << std::endl;
    pcl::PointCloud<pcl::PointXYZI> visual_cloud;
    visual_cloud.width = cloud_in->size();
    visual_cloud.height = 1;
    visual_cloud.resize(cloud_in->size());
    for (size_t i = 0; i < clusters_index.size(); ++i)
    {
        for (size_t j = 0; j < clusters_index[i].size(); ++j)
        {
            visual_cloud.points[clusters_index[i][j]].x = cloud_in->points[clusters_index[i][j]].x;
            visual_cloud.points[clusters_index[i][j]].y = cloud_in->points[clusters_index[i][j]].y;
            visual_cloud.points[clusters_index[i][j]].z = cloud_in->points[clusters_index[i][j]].z;
            visual_cloud.points[clusters_index[i][j]].intensity = 20*(i+100);
            //std::cout << clusters_index[i][j] << std::endl;

        }
       // std::cout << clusters_index[i].size() << std::endl;

    }
    
    pcl::visualization::CloudViewer viewer("DBSCAN cloud viewer.");
    viewer.showCloud(visual_cloud.makeShared());
    while (!viewer.wasStopped())
    {
    }
    pcl::io::savePCDFile("dbscan.pcd", visual_cloud,true);
    std::cout << "Hello World!\n";
}


参考:
1.https://blog.csdn.net/baidu_38172402/article/details/81915112
2.https://blog.csdn.net/xinxiangwangzhi_/article/details/118212814

自己用C++ 实现保存pcd、ply格式的点云文件

https://www.jianshu.com/p/10b96e52a996

pcd、ply格式都分别有ascii形式和二进制形式的保存方式。 ascii形式方便人阅读,但是文件体积稍微大一些。二进制形式文件体积小,但是人不易读

一、pcd

(一)ascii 形式

ascii 形式的PCD文件内容样式如下:
在这里插入图片描述

std::vector<PointXyzRgb<float>> points;  // PointXyzRgb为自定义类,有x/y/z/r/g/b信息

points_len = points.size();
std::string pc_name = "./pointcloud" + std::to_string(cnt) + ".pcd";
std::ofstream fout_pc_name(pc_name);

fout_pc_name << "# .PCD v0.7 - Point Cloud Data file format" << std::endl;
fout_pc_name << "VERSION 0.7" << std::endl;
fout_pc_name << "FIELDS x y z" << std::endl;
fout_pc_name << "SIZE 4 4 4" << std::endl;
fout_pc_name << "TYPE F F F" << std::endl;
fout_pc_name << "COUNT 1 1 1" << std::endl;
fout_pc_name << "WIDTH " << points_len << std::endl;
fout_pc_name << "HEIGHT 1" << std::endl;
fout_pc_name << "VIEWPOINT 0 0 0 1 0 0 0" << std::endl;
fout_pc_name << "POINTS " << points_len << std::endl;
fout_pc_name << "DATA ascii" << std::endl;

for (int n = 0; n < points_len; n++) {
    fout_pc_name << points[n].x << " " << points[n].y << " " << points[n].z << std::endl;
}
fout_pc_name.close();

如果需要颜色,则在文件头多增加几行,for循环里加上rgb的信息

(二)二进制

二进制形式的PCD文件内容样式如下:
在这里插入图片描述
参照这个内容样式可写C++代码如下:

std::vector<PointXyzRgb<float>> points;  // 里面存有点云。 PointXyzRgb为自定义类,有x/y/z/r/g/b信息

points_len = points.size();
std::string pc_name = "./pointcloud" + std::to_string(cnt) + ".pcd";

FILE *fp = fopen(pc_name.c_str(), "wb");
fprintf(fp, "# .PCD v0.7 - Point Cloud Data file format\n");
fprintf(fp, "VERSION 0.7\n");
fprintf(fp, "FIELDS x y z\n");
fprintf(fp, "SIZE 4 4 4\n");
fprintf(fp, "TYPE F F F\n");
fprintf(fp, "COUNT 1 1 1\n");
fprintf(fp, "WIDTH %d\n", points_len);
fprintf(fp, "HEIGHT 1\n");
fprintf(fp, "VIEWPOINT 0 0 0 1 0 0 0\n");
fprintf(fp, "POINTS %d\n", points_len);
fprintf(fp, "DATA binary\n");

for (int n = 0; n < points_len; n++) {
    float tmp2[3] = { points[n].x, points[n].y, points[n].z };
    fwrite(tmp2, sizeof(float), 3, fp);
}
fclose(fp);

如果需要颜色,则在文件头多增加几行,for循环里加上rgb的信息

二、PLY

(一)ascii 形式

ascii 形式的PLY文件内容样式如下:
在这里插入图片描述
参照这个内容样式可写C++代码如下:

std::vector<PointXyzRgb<float>> points;  // 里面存有点云。 PointXyzRgb为自定义类,有x/y/z/r/g/b信息

points_len = points.size();
std::string pc_name = "./pointcloud" + std::to_string(cnt) + ".ply";
std::ofstream fout_pc_name(pc_name);

fout_pc_name << "ply" << std::endl;
fout_pc_name << "format ascii 1.0" << std::endl;
fout_pc_name << "element vertex " << points_len << std::endl;
fout_pc_name << "property float x" << std::endl;
fout_pc_name << "property float y" << std::endl;
fout_pc_name << "property float z" << std::endl;
fout_pc_name << "element face 0" << std::endl;
fout_pc_name << "property list uchar int vertex_index" << std::endl;
fout_pc_name << "end_header" << std::endl;

for (int n = 0; n < points_len; n++) {
  fout_pc_name << points[n].x << " " << points[n].y << " " << points[n].z << std::endl;
}
fout_pc_name.close();

需要RGB信息,则如下:

std::vector<PointXyzRgb<float>> points;  // 里面存有点云。 PointXyzRgb为自定义类,有x/y/z/r/g/b信息

points_len = points.size();
std::string pc_name = "./pointcloud" + std::to_string(cnt) + ".ply";
std::ofstream fout_pc_name(pc_name);

fout_pc_name << "ply" << std::endl;
fout_pc_name << "format ascii 1.0" << std::endl;
fout_pc_name << "element vertex " << points_len << std::endl;
fout_pc_name << "property float x" << std::endl;
fout_pc_name << "property float y" << std::endl;
fout_pc_name << "property float z" << std::endl;
fout_pc_name << "property uchar red" << std::endl;
fout_pc_name << "property uchar green" << std::endl;
fout_pc_name << "property uchar blue" << std::endl;
fout_pc_name << "element face 0" << std::endl;
fout_pc_name << "property list uchar int vertex_index" << std::endl;
fout_pc_name << "end_header" << std::endl;

for (int n = 0; n < points_len; n++) {
    fout_pc_name << points[n].x << " " << points[n].y << " " << points[n].z << " " <<  
        points[n].r << " " << points[n].g << " " << points[n].b << std::endl;
}

fout_pc_name.close();

(二)二进制

二进制形式的PLY文件内容样式如下:
在这里插入图片描述
参照这个内容样式可写C++代码如下:

void saveBinPts(std::vector<float> &Pts, char *fileName)
{
    int elementNum = Pts.size() / 3;
    FILE *fp = fopen(fileName, "wb");
    fprintf(fp, "ply\n");
    fprintf(fp, "format binary_little_endian 1.0\n");
    fprintf(fp, "comment File generated\n");
    fprintf(fp, "element vertex %d\n", elementNum);
    fprintf(fp, "property float x\n");
    fprintf(fp, "property float y\n");
    fprintf(fp, "property float z\n");
    fprintf(fp, "end_header\n");

    for (int i = 0; i < elementNum; i++) {
        float tmp2[3] = { Pts[3 * i + 0], Pts[3 * i + 1], Pts[3 * i + 2] };
        fwrite(tmp2, sizeof(float), 3, fp);
    }
    fclose(fp);
}

ply各字段含义说明
http://paulbourke.net/dataformats/ply/
http://gamma.cs.unc.edu/POWERPLANT/papers/ply.pdf

### 回答1: PCL (Point Cloud Library) 是一个非常强大的点云处理库,它提供了丰富的功能来处理、分析和可视化点云数据。下面将从学习教程的角度来介绍PCL C++ 学习教程。 在学习PCL C++学习教程之前,我们先了解一下PCL的基础知识。PCL是一个开源项目,可以在Windows、Linux和Mac OS等操作系统上使用。它提供了常见的点云数据类型、滤波、分割、配准、特征提取和重建等功能。此外,PCL还有一个强大的可视化模块,可以方便地实时显示点云数据。 对于初学者而言,可以从PCL官方网站上的教程入手。官方网站提供了完整的文档和示例代码,可供学习者参考。此外,还有一些博客和视频教程可以帮助学习者更好地掌握PCL的使用。 学习PCL C++,需要一定的编程基础知识,例如掌握C++语言、面向对象编程和基本的算法理论等。在学习过程中,可以按照如下步骤进行: 1. 安装PCL库:根据自己的操作系统选择合适的安装方式,到PCL官方网站下载安装包并按照指南进行安装。 2. 学习PCL的基础知识:首先熟悉PCL的常见数据类型,例如点云表示和常见的PCL数据结构。然后学习如何读取和保存点云数据,以及基本的点云操作和可视化。 3. 学习PCL的模块功能:PCL库包含多个模块,例如滤波、分割、配准、特征提取和三维重建等。可以针对自己的需求选择相应的模块进行学习,并掌握它们的基本原理和使用方法。 4. 练习和实践:通过完成一些PCL的实际项目,例如点云配准、目标检测或三维重建等,来巩固所学的知识。 总之,学习PCL C++需要一定的时间和耐心,通过实践和不断学习,逐渐掌握PCL的使用技巧。希望以上简要的回答能对你理解PCL C++学习教程有所帮助。 ### 回答2: PCL(Point Cloud Library)是一个开源的计算机视觉库,主要用于处理和分析点云数据。它提供了许多功能强大的算法和工具,可以帮助开发人员在点云处理方面进行快速开发。 PCL C学习教程是PCL官方提供的一份入门教程,旨在帮助初学者快速上手PCL C的开发。该教程主要介绍了PCL的基本概念和使用方法,并提供了一些常见的点云处理算法的示例代码。通过学习PCL C学习教程,我们可以了解PCL的基本功能和使用技巧,并且可以开始进行简单的点云处理任务。 在学习PCL C的过程中,我们需要掌握一些基础知识,比如点云的表示和存储方式、点云的滤波和分割方法、点云的特征提取和匹配算法等等。PCL C学习教程提供了详细的解释和示例代码,以帮助我们理解和运用这些基础知识。 此外,PCL C学习教程还介绍了一些常见的点云处理应用场景,比如目标检测、点云配准和重建等。通过学习这些应用场景,我们可以了解到PCL C在不同领域的应用和相关的算法思想。 总结来说,PCL C学习教程是学习和使用PCL的入门指南,通过学习教程中的内容,我们可以掌握PCL C的基础知识和技能,并且开始进行简单的点云处理任务。 ### 回答3: PCL(Point Cloud Library)是一个用于点云数据处理的开源库。学习PCL可以帮助我们更好地理解和处理点云数据,从而在计算机视觉、机器人领域等方面开展相关工作。 学习PCL可以从以下几个方面入手。首先,了解PCL的基本概念和原理是非常重要的。PCL涵盖了点云数据获取、滤波、特征提取、配准等算法,了解这些基本概念是进行更高级别的数据处理的基础。 其次,学习PCL的使用方法。PCL提供了丰富的功能库和示例代码,通过实践使用可以更好地掌握PCL的各项功能。可以通过看官方文档、阅读教程、查阅论坛等方式学习PCL的使用方法。 再次,尝试应用PCL解决实际问题。通过完成一些小项目,如点云数据的滤波、分割、配准等任务,可以锻炼我们对PCL的熟练程度,并将PCL应用于实际工程中。 最后,与其他PCL爱好者交流学习。在互联网上可以找到很多与PCL相关的讨论组、社区和论坛,与其他使用PCL的人交流经验、分享学习心得,可以提高我们的学习效果。 总之,学习PCL需要理解基本概念和原理、掌握使用方法、应用于实践项目以及与他人交流学习。希望以上建议对学习PCL有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值