(1)從一個點雲中提取索引
如何使用一個,基於某一分割算法提取點雲中的一個子集。
代碼解析
#include <iostream> #include <pcl/ModelCoefficients.h> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/sample_consensus/method_types.h> #include <pcl/sample_consensus/model_types.h> #include <pcl/segmentation/sac_segmentation.h> #include <pcl/filters/voxel_grid.h> #include <pcl/filters/extract_indices.h> int main (int argc, char** argv) { /********************************************************************************************************** 從輸入的.PCD 文件載入數據后,創建一個VOxelGrid濾波器對數據進行下采樣,在這里進行下才樣是為了加速處理過程, 越少的點意味着分割循環中處理起來越快 **********************************************************************************************************/ pcl::PCLPointCloud2::Ptr cloud_blob (new pcl::PCLPointCloud2), cloud_filtered_blob (new pcl::PCLPointCloud2);//申明濾波前后的點雲 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>), cloud_p (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>); // 讀取PCD文件 pcl::PCDReader reader; reader.read ("table_scene_lms400.pcd", *cloud_blob); //統計濾波前的點雲個數 std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl; // 創建體素柵格下采樣: 下采樣的大小為1cm pcl::VoxelGrid<pcl::PCLPointCloud2> sor; //體素柵格下采樣對象 sor.setInputCloud (cloud_blob); //原始點雲 sor.setLeafSize (0.01f, 0.01f, 0.01f); // 設置采樣體素大小 sor.filter (*cloud_filtered_blob); //保存 // 轉換為模板點雲 pcl::fromPCLPointCloud2 (*cloud_filtered_blob, *cloud_filtered); std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height << " data points." << std::endl; // 保存下采樣后的點雲 pcl::PCDWriter writer; writer.write<pcl::PointXYZ> ("table_scene_lms400_downsampled.pcd", *cloud_filtered, false); 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); //判斷是否為模型內點的距離閥值 // 設置ExtractIndices的實際參數 pcl::ExtractIndices<pcl::PointXYZ> extract; //創建點雲提取對象 int i = 0, nr_points = (int) cloud_filtered->points.size (); // While 30% of the original cloud is still there 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 the inliers 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); // Create the filtering object extract.setNegative (true); extract.filter (*cloud_f); cloud_filtered.swap (cloud_f); i++; } return (0); }
結果:
顯示出來:
圖1 原始點雲圖像 圖2 下采樣后點雲數據
圖3 分割得到的其一平面模型 圖4 分割得到的其二平面模型
(2)使用ConditionalRemoval 或RadiusOutlinerRemoval移除離群點
如何在濾波模塊使用幾種不同的方法移除離群點,對於ConditionalRemoval濾波器,可以一次刪除滿足對輸入的點雲設定的一個或多個條件指標的所有的數據點,RadiusOutlinerRemoval濾波器,它可以刪除在輸入點雲一定范圍內沒有至少達到足夠多近鄰的所有數據點。
關於RadiusOutlinerRemoval的理解,在點雲數據中,設定每個點一定范圍內周圍至少有足夠多的近鄰,不滿足就會被刪除
關於ConditionalRemoval 這個濾波器刪除點雲中不符合用戶指定的一個或者多個條件的數據點
新建文件remove_outliers.cpp
#include <iostream> #include <pcl/point_types.h> #include <pcl/filters/radius_outlier_removal.h> #include <pcl/filters/conditional_removal.h> int main (int argc, char** argv) { if (argc != 2) //確保輸入的參數 { std::cerr << "please specify command line arg '-r' or '-c'" << std::endl; exit(0); } pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>); //填充點雲 cloud->width = 5; cloud->height = 1; cloud->points.resize (cloud->width * cloud->height); for (size_t i = 0; i < cloud->points.size (); ++i) { cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f); cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f); cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f); } if (strcmp(argv[1], "-r") == 0){ pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem; //創建濾波器 outrem.setInputCloud(cloud); //設置輸入點雲 outrem.setRadiusSearch(0.8); //設置半徑為0.8的范圍內找臨近點 outrem.setMinNeighborsInRadius (2); //設置查詢點的鄰域點集數小於2的刪除 // apply filter outrem.filter (*cloud_filtered); //執行條件濾波 在半徑為0.8 在此半徑內必須要有兩個鄰居點,此點才會保存 } else if (strcmp(argv[1], "-c") == 0){ //創建條件限定的下的濾波器 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))); //添加在Z字段上大於0的比較算子 range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::LT, 0.8))); //添加在Z字段上小於0.8的比較算子 // 創建濾波器並用條件定義對象初始化 pcl::ConditionalRemoval<pcl::PointXYZ> condrem; condrem.setCondition (range_cond); condrem.setInputCloud (cloud); //輸入點雲 condrem.setKeepOrganized(true); //設置保持點雲的結構 // 執行濾波 condrem.filter (*cloud_filtered); //大於0.0小於0.8這兩個條件用於建立濾波器 } else{ std::cerr << "please specify command line arg '-r' or '-c'" << std::endl; exit(0); } std::cerr << "Cloud before filtering: " << std::endl; for (size_t i = 0; i < cloud->points.size (); ++i) std::cerr << " " << cloud->points[i].x << " " << cloud->points[i].y << " " << cloud->points[i].z << std::endl; // display pointcloud after filtering std::cerr << "Cloud after filtering: " << std::endl; for (size_t i = 0; i < cloud_filtered->points.size (); ++i) std::cerr << " " << cloud_filtered->points[i].x << " " << cloud_filtered->points[i].y << " " << cloud_filtered->points[i].z << std::endl; return (0); }
編譯運行的結果為
從中可以看出ConditionalRemoval 或RadiusOutlinerRemoval的區別
RadiusOutlinerRemoval比較適合去除單個的離群點 ConditionalRemoval 比較靈活,可以根據用戶設置的條件靈活過濾
微信公眾號號可掃描二維碼一起共同學習交流