zoukankan      html  css  js  c++  java
  • 点云有用的操作 

    前言:最近在做点云的工作,通过资料及其他网页,总结一些比较常用且实用的操作,留给自己查看,同时也希望能给别人带来方便。

    1. 两片点云cloudA、cloudB,若在cloudB中找到cloudA的数据点,则从cloudB中删除该点。

    #include <pcl/point_cloud.h>        //点类型定义头文件
    #include <pcl/kdtree/kdtree_flann.h> //kdtree类定义头文件
    
    #include <iostream>
    #include <vector>
    #include <ctime>
    const double eps = 1.0e-6;
    
    int
    main(int argc, char** argv)
    {
        //srand(time(NULL));   //用系统时间初始化随机种子
        //创建一个PointCloud<pcl::PointXYZ>
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloudA(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloudB(new pcl::PointCloud<pcl::PointXYZ>);
        // 随机点云生成
        cloudA->width = 20;             //此处点云数量
        cloudA->height = 1;                //表示点云为无序点云
        cloudA->points.resize(cloudA->width * cloudA->height);
    
        for (size_t i = 0; i < cloudA->points.size(); ++i)   //循环填充点云数据
        {
            cloudA->points[i].x = i + 1;
            cloudA->points[i].y = i + 1;
            cloudA->points[i].z = i + 1;
        }
    
        cloudB->width = 20;             //此处点云数量
        cloudB->height = 1;                //表示点云为无序点云
        cloudB->points.resize(cloudB->width * cloudB->height);
        for (size_t i = 0; i < cloudB->points.size(); ++i)   //循环填充点云数据
        {
            cloudB->points[i].x = i + 8;
            cloudB->points[i].y = i + 8;
            cloudB->points[i].z = i + 8;
        }
        //cloudBackup->points = cloud->points;
        //创建KdTreeFLANN对象,并把创建的点云设置为输入,创建一个searchPoint变量作为查询点
        pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
    
        pcl::PointXYZ searchPoint;
        int K = 1;
        std::vector<int> pointIdxNKNSearch(K);      //存储查询点近邻索引
        std::vector<float> pointNKNSquaredDistance(K); //存储近邻点对应距离平方
        std::vector<pcl::PointXYZ> DeleteData;
        int num = 0;
        for (auto iterA = cloudA->begin(); iterA != cloudA->end(); iterA++)
        {
            searchPoint.x = iterA->x;
            searchPoint.y = iterA->y;
            searchPoint.z = iterA->z;
            kdtree.setInputCloud(cloudB);    //在cloudB中找到对应点后,在cloudB中直接删除该点
            num = kdtree.nearestKSearch(searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance);
            if (num > 0)
            {
                if (sqrt(pointNKNSquaredDistance[0])<eps)
                {
                    auto iterB = cloudB->begin() + pointIdxNKNSearch[0];
                    cloudB->erase(iterB);
                    DeleteData.push_back(searchPoint);
                    if (cloudB->size()==0)
                    {
                        break;
                    }
                    searchPoint.x = 0;
                    searchPoint.y = 0;
                    searchPoint.z = 0;
                    num = 0;
                    pointIdxNKNSearch.clear();
                    pointNKNSquaredDistance.clear();
                }
            }
            
        }
        
        for (auto iter = DeleteData.begin(); iter != DeleteData.end(); iter++)
        {
            std::cout << iter->x << " " << iter->y << " " << iter->z << std::endl;
        }
    
        system("pause");
        return 0;
    }

    2.几种常用的操作

    (1)保存点云数据

    //普通格式ASSIC(占用内存较大)
    pcl::PCDWriter writer;
    writer.write(s_pcd,laserCloudIn);
    
    //bin格式(占用内存较小)
    pcl::io::savePCDFileBinary(s_pcd, *cloud);

    (2)查找点云在X、Y、Z轴的极值

    pcl::PointXYZ minPt, maxPt;
    pcl::getMinMax3D (*cloud, minPt, maxPt);

    (3)知道要保存点的索引,从原点云中拷贝点到新点云

    pcl::PointCloud::Ptr cloudOut(new pcl::PointCloud);
    std::vector indexs = { 1, 2, 5 };
    pcl::copyPointCloud(*cloud, indexs, *cloudOut);

    (4)从点云里删除和添加点

    pcl::PointCloud::iterator index = cloud->begin();
    cloud->erase(index);//删除第一个
    index = cloud->begin() + 5;
    cloud->erase(index);//删除第5个
    pcl::PointXYZ point = { 1, 1, 1 };
    //在索引号为5的位置上插入一点,之后的所有点后移一位
    cloud->insert(cloud->begin() + 5, point);
    cloud->push_back(point);//从点云最后面插入一点

    (5)对点云进行全局或局部变换

    //全部
    pcl::transformPointCloud(*cloud,*transform_cloud1,transform_1);
            
    //局部
    //第一个参数为输入,第二个参数为输入点云中部分点集索引,第三个为存储对象,第四个是变换矩阵。
    pcl::transformPointCloud(*cloud,pcl::PointIndices indices,*transform_cloud1,matrix); 

    (6)链接两个点云字段(两点云大小必须相同)

    pcl::PointCloud::Ptr cloud_with_nomal (new pcl::PointCloud);
    pcl::concatenateFields(*cloud,*cloud_normals,*cloud_with_nomal);

    (7)从点云中删除无效点

    vector indices;
    pcl::removeNaNFromPointCloud(*cloud,*output,indices);

    (8)计算质心

    Eigen::Vector4f centroid;//质心
    pcl::compute3DCentroid(*cloud_smoothed,centroid); //估计质心的坐标

    (9)计算点云的法向量

    pcl::NormalEstimation ne;
    pcl::PointCloud::Ptr pcNormal(new pcl::PointCloud);  
    pcl::search::KdTree::Ptr tree(new pcl::search::KdTree);
    tree->setInputCloud(inCloud);  
    ne.setInputCloud(inCloud);  
    ne.setSearchMethod(tree);  
    ne.setKSearch(50);  
    //ne->setRadiusSearch (0.03);   
    ne.compute(*pcNormal);

    (10)提取点云的边界

    pcl::PointCloud boundaries; //保存边界估计结果pcl::BoundaryEstimation boundEst; //定义一个进行边界特征估计的对象
    pcl::NormalEstimation normEst; //定义一个法线估计的对象
    pcl::PointCloud::Ptr normals(new pcl::PointCloud); //保存法线估计的结果
    pcl::PointCloud::Ptr cloud_boundary (new pcl::PointCloud); 
    normEst.setInputCloud(pcl::PointCloud::Ptr(cloud)); 
    normEst.setRadiusSearch(reforn); //设置法线估计的半径
    normEst.compute(*normals); //将法线估计结果保存至normals
    
     
    boundEst.setInputCloud(cloud); //设置输入的点云
    boundEst.setInputNormals(normals); //设置边界估计的法线,因为边界估计依赖于法线
    boundEst.setRadiusSearch(re); //设置边界估计所需要的半径
    boundEst.setAngleThreshold(M_PI/4); //边界估计时的角度阈值
    boundEst.setSearchMethod(pcl::search::KdTree::Ptr (new pcl::search::KdTree)); //设置搜索方式KdTree
    boundEst.compute(boundaries); //将边界估计结果保存在boundaries

    (11)k近邻和半径搜索

    pcl::KdTreeFLANNkdtree; //创建kdtree搜索对象
    kdtree.setInputCloud(cloud);           //载入点云
    pcl::PointXYZ searchPoint;              //设置查询点并赋随机值
    //K近邻搜索
    int K = 10;                             //搜索最近邻的点数
    vectorpointIdxNKNSearch(K);        //存放最近邻点的索引
    vectorpointNKNSquaredDistance(K);//对应的距离平方
    kdtree.nearestKSearch(searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance)
    //半径内搜索
    std::vector pointIdxRadiusSearch;
    std::vector pointRadiusSquaredDistance;
    float radius; //定义搜索半径
    kdtree.radiusSearch(searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance);

    (12)添加新的点云类

    //有自定义点云类型时要加入该头文件才能使用PCL库模板函数
    #include   
    //运用相应的函数需加入以下相应头文件
    //如:直通滤波器
    #include 
    
    //定义一个新的点云类
    struct PointXYZIR
    {
      PCL_ADD_POINT4D
      float intensity;
      uint16_t ring;                   ///< laser ring number
      EIGEN_MAKE_ALIGNED_OPERATOR_NEW  //确保new操作符对齐
    } EIGEN_ALIGN16; //强制SSE对齐
    
    POINT_CLOUD_REGISTER_POINT_STRUCT(PointXYZIR, (float, x, x)(float, y, y)(float, z, z)(float, intensity, intensity)(uint16_t, ring, ring))

    3. 几种点云常用的滤波器

    (1)直通滤波器

    // 创建滤波器对象
    pcl::PassThrough<pcl::PointXYZ> pass;
    pass.setInputCloud (cloud);
    pass.setFilterFieldName ("z");
    pass.setFilterLimits (0.0, 1.0);
    //pass.setFilterLimitsNegative (true);
    pass.filter (*cloud_filtered);

    (2)VoxelGrid滤波器

    创建三维体素栅格,将体素栅格内所有点的重心代替体素中其他点,实现下采样。

    // 创建滤波器对象
    pcl::VoxelGrid<pcl::PointXYZ> sor;
    sor.setInputCloud(cloud);
    sor.setLeafSize(0.01f, 0.01f, 0.01f);
    sor.filter(*cloud_filtered);            

    (3)StatisticalOutlireRemoval滤波器

    对每一个点的近邻进行一个统计分析,计算点到近邻点的距离,计算所有近邻点的平均距离,平均距离在标准范围之外的点被视为离群点

    // 创建滤波器对象
    pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
    sor.setInputCloud (cloud);
    sor.setMeanK (50);
    sor.setStddevMulThresh (1.0);
    sor.filter (*cloud_filtered);

    (4)使用参数化模型投影点云

    基于对模型的结构和尺寸等信息,选择特殊模型,如:球等,设置参数进行滤波。

    首先,填充ModelCoefficients的值,该例中使用X-Y平面

    //创建一个系数为(0,0,1,0)的平面
    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());
    coefficients->values.resize(4);
    coefficients->values[0] = coefficients->values[1] = 0;
    coefficients->values[2] = 1.0;
    coefficients->values[3] = 0;

    然后,创建ProjectInliers对象,使用ModelCoefficients作为投影对象的模型参数

    //创建滤波器对象
    pcl::ProjectInliers<pcl::PointXYZ>proj;
    proj.setModelType(pcl::SACMODEL_PLANE);
    proj.setInputCloud(cloud);
    proj.setModelCoefficients(coefficients);
    proj.filter(*cloud_projected);

    (5)ExtractIndices滤波器

    基于某一分割算法提取点云的一个子集

    首先,使用VoxelGrid滤波器对数据下采样,加快处理速度

    // 创建滤波器对象:使用叶大小为1cm的下采样
    pcl::VoxelGrid<pcl::PCLPointCloud2> sor;
    sor.setInputCloud(cloud_blob);
    sor.setLeafSize(0.01f, 0.01f, 0.01f);
    sor.filter(*cloud_filtered_blob);

    接着,参数化分割

    然后,设置extraction filter实际参数

    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices());
    // 创建分割对象
    pcl::SACSegmentation<pcl::PointXYZ> seg;
    // 可选
    seg.setOptimizeCoefficients(true);
    // 必选
    seg.setModelType(pcl::SACMODEL_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);
    seg.setMaxIterations(1000);
    seg.setDistanceThreshold(0.01);
    // 创建滤波器对象
    pcl::ExtractIndices<pcl::PointXYZ> extract;
    int i = 0, nr_points = (int)cloud_filtered->points.size();
    // 当还有30%原始点云数据时
    while (cloud_filtered->points.size() > 0.3 * nr_points)
    {
        // 从余下的点云中分割最大平面组成部分
        seg.setInputCloud(cloud_filtered);
        seg.segment(*inliers, *coefficients);
        if (inliers->indices.size() == 0)
        {
            std::cerr << "Could not estimate a planar model for the given dataset." << std::endl;
            break;
        }
        // 分离内层
        extract.setInputCloud(cloud_filtered);
        extract.setIndices(inliers);
        extract.setNegative(false);
        extract.filter(*cloud_p);
        std::cerr << "PointCloud representing the planar component: " << cloud_p->width * cloud_p->height << " data points." << std::endl;
        std::stringstream ss;
        ss << "table_scene_lms400_plane_" << i << ".pcd";
        writer.write<pcl::PointXYZ>(ss.str(), *cloud_p, false);
        // 创建滤波器对象
        extract.setNegative(true);
        extract.filter(*cloud_f);
        cloud_filtered.swap(cloud_f);
        i++;
    }

     需要将sensor_msgs::PointCloud2::Ptr改为pcl::PCLPointCloud2::Ptr;pcl::fromROSMsg()改为pcl::fromPCLPointCloud2();

     (6)ConditionalRemoval或RadiusOutlierRemoval类

    ConditionalRemoval滤波器可以删除设定的一个或多个条件指标的所有数据点;

    首先,创建条件限定对象,为限定对象添加比较算子,创建滤波器并用条件定义对象初始化,

    // 创建环境
    pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new
                pcl::ConditionAnd<pcl::PointXYZ>());
    range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
                pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0)));
    range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
                pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8)));
    // 创建滤波器
    pcl::ConditionalRemoval<pcl::PointXYZ> condrem(range_cond);
    condrem.setInputCloud(cloud);
    condrem.setKeepOrganized(true);
    // 应用滤波
    condrem.filter(*cloud_filtered);

    然后,RadiusOutlinerRemoval滤波器删除搜索半径内不满足设定的近邻点数,可用于移除离群点。

    pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
    // 创建滤波器
    outrem.setInputCloud(cloud);
    outrem.setRadiusSearch(0.8);  // 设置搜索半径
    outrem.setMinNeighborsInRadius(2);   // 设置查询点的近邻点数
    // 应用滤波器
    outrem.filter(*cloud_filtered);

    (7)CropHull滤波器

    获取2D封闭多边形内部或者外部的点云

    首先,设置2D封闭多边形顶点;

    然后,使用ConvexHull类构造2D凸包;

    最后,创建Crophull对象,滤波获取凸包范围内的点云。

    pcl::PointCloud<pcl::PointXYZ>::Ptr boundingbox_ptr(new pcl::PointCloud<pcl::PointXYZ>);
    boundingbox_ptr->push_back(pcl::PointXYZ(0.1, 0.1, 0));
    boundingbox_ptr->push_back(pcl::PointXYZ(0.1, -0.1, 0));
    boundingbox_ptr->push_back(pcl::PointXYZ(-0.1, 0.1, 0));
    boundingbox_ptr->push_back(pcl::PointXYZ(-0.1, -0.1, 0));
    boundingbox_ptr->push_back(pcl::PointXYZ(0.15, 0.1, 0));
    
    pcl::ConvexHull<pcl::PointXYZ> hull;
    hull.setInputCloud(boundingbox_ptr);
    hull.setDimension(2);
    std::vector<pcl::Vertices> polygons;
    pcl::PointCloud<pcl::PointXYZ>::Ptr surface_hull(new pcl::PointCloud<pcl::PointXYZ>);
    hull.reconstruct(*surface_hull, polygons);
    
    pcl::PointCloud<pcl::PointXYZ>::Ptr objects(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::CropHull<pcl::PointXYZ> bb_filter;
    bb_filter.setDim(2);
    bb_filter.setInputCloud(cloud);
    bb_filter.setHullIndices(polygons);
    bb_filter.setHullCloud(surface_hull);
    bb_filter.filter(*objects);

    参考:

    https://www.jianshu.com/p/57bbad9c39d6

    https://blog.csdn.net/zhan_zhan1/article/details/103991733?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param

    https://www.it610.com/article/1282154616984715264.htm

  • 相关阅读:
    Servlet基本用法(一)基本配置
    python 起航第一步吧
    shell脚本的执行方式
    linux 计划任务执行命令 crontab -e
    一个完整的 curl post登录带验证码的代码
    php curl post登录与带cookie模拟登录随笔
    liunx 配置 php curl 拓展库的方法
    php 魔术方法学习笔记
    php curl选项列表(超详细)
    正则表达式后面接的/isU, /is, /s含义
  • 原文地址:https://www.cnblogs.com/hgl0417/p/13507533.html
Copyright © 2011-2022 走看看