本小节举例说明了如何采用随机采样一致性估计从带有噪声的点云中提取一个圆柱体模型,整个程序处理流程如下: 1、过滤掉远于1.5米的数据点; 2、估计每个点的表面法线; 3、分割出平面模型(在我们的演示数据集中表示桌面)并保存到磁盘中; 4、分割圆出柱体模型(在我们的演示数据集中表示圆杯)并保存到磁盘中。 注意:由于数据中噪音的出现,圆柱体模型并不十分严格。 代码首先,在PCL(Point Cloud Learning)中国协助发行的书[1]提供光盘的第14章例2文件夹中,打开名为cylinder_segmentation.cpp的代码文件,在同一文件夹下有点云文件table_scene_mug_stereo_textured_plane.pcd。 解释说明下面来详细解释打开源代码的关键语句。 pcl::PCDReader reader; //pcd文件读取对象 pcl::PassThrough<PointT> pass; //直通滤波对象 pcl::NormalEstimation<PointT, pcl::Normal> ne; //法线估计对象 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; //分割对象 pcl::PCDWriter writer; //pcd文件写入对象 pcl::ExtractIndices<PointT> extract; //点提取对象 pcl::ExtractIndices<pcl::Normal> extract_normals; //点提取对象 pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());
上面代码定义在程序中用到的对象。 pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>); pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>); pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>); pcl::PointCloud<PointT>::Ptr cloud_filtered2 (new pcl::PointCloud<PointT>); pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2 (new pcl::PointCloud<pcl::Normal>); pcl::ModelCoefficients::Ptr coefficients_plane (new pcl::ModelCoefficients), coefficients_cylinder (new pcl::ModelCoefficients); pcl::PointIndices::Ptr inliers_plane (new pcl::PointIndices), inliers_cylinder (new pcl::PointIndices);
上面代码定义在程序中用到的数据对象,点类型、法线类型的点云对象,以及模型系数等,以及存储平面上内点的点索引集合对象。 reader.read ("table_scene_mug_stereo_textured.pcd", *cloud); std::cerr <<"PointCloud has: "<< cloud->points.size () <<" data points."<< std::endl;
上面代码完成对点云数据从文件的读取。 pass.setInputCloud (cloud); pass.setFilterFieldName ("z"); pass.setFilterLimits (0, 1.5); pass.filter (*cloud_filtered); std::cerr <<"PointCloud after filtering has: "<< cloud_filtered->points.size () <<" data points."<< std::endl;
上面代码进行直通滤波,将z轴不在(0,1.5)范围内的点过滤掉,将剩余点存储在cloud_filtered对象中后续使用。 ne.setSearchMethod (tree); ne.setInputCloud (cloud_filtered); ne.setKSearch (50); ne.compute (*cloud_normals);
上面代码对过滤后的点云进行法线估计,为后续进行基于法线的分割准备数据。 //设置分割所用的模型类型、方法、相关参数 seg.setOptimizeCoefficients (true); seg.setModelType (pcl::SACMODEL_NORMAL_PLANE); seg.setNormalDistanceWeight (0.1); seg.setMethodType (pcl::SAC_RANSAC); seg.setMaxIterations (100); seg.setDistanceThreshold (0.03); seg.setInputCloud (cloud_filtered); seg.setInputNormals (cloud_normals); // 根据上面的输入参数执行分割获取平面模型系数和处在平面上的内点 seg.segment (*inliers_plane, *coefficients_plane); std::cerr <<"Plane coefficients: "<<*coefficients_plane << std::endl; // 从点云中抽取分割的处在平面上的点集 extract.setInputCloud (cloud_filtered); extract.setIndices (inliers_plane); extract.setNegative (false); pcl::PointCloud<PointT>::Ptr cloud_plane (new pcl::PointCloud<PointT> ()); extract.filter (*cloud_plane); //存储分割得到的平面上的点到点云文件 std::cerr <<"PointCloud representing the planar component: "<< cloud_plane->points.size () <<" data points."<< std::endl; writer.write ("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false);
上面代码将过滤后的点云进行分割,并从点云中提取出处在平面上的点,存储到点云文件。 //为圆柱体分割创建分割对象,并设置所有参数seg.setOptimizeCoefficients (true); //设置对估计的模型系数需要进行优化seg.setModelType (pcl::SACMODEL_CYLINDER); //设置分割模型为圆柱型seg.setMethodType (pcl::SAC_RANSAC); //设置采用RANSAC作为算法的参数估计方法seg.setNormalDistanceWeight (0.1); //设置表面法线权重系数seg.setMaxIterations (10000); //设置迭代的最大次数10000seg.setDistanceThreshold (0.05); //设置内点到模型的距离允许最大值seg.setRadiusLimits (0, 0.1); //设置估计出的圆柱模型的半径范围
如上,我们使用RANSAC随机采样一致性鲁棒估计来获取圆柱体模型系数,并且限制了每个局内点到模型的距离阀值DistanceThreshold,即内点与模型的距离不能大于5厘米,设置表面法线的影响权重NormalDistanceWeight为0.1,并且限制了圆柱体模型的半径RadiusLimits要小于10厘米,估计出圆柱模型系数以及对应的内点后,存储内点到点云文件。 |