点云滤波方法记录
本片博客的濾波方式引用https://blog.csdn.net/qq_34719188/article/details/79179430該網址的博主內容。
1.本人主要用SICK掃描儀做管道對接課題,前期針對于測量采集的txt數據轉換為pcd文件格式:代碼如下
點云數據txt轉pcd格式
#include "pch.h"
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<pcl\io\pcd_io.h>
#include<pcl\point_types.h>
using namespace std;
int main()
{ //定義一種類型表示TXT中xyz typedef struct TXT_Point_XYZ { double x; double y; double z; }TOPOINT_XYZ; //讀取txt文件 int num_txt; FILE *fp_txt; TXT_Point_XYZ txt_points; vector<TXT_Point_XYZ> my_vTxtPoints; fp_txt = fopen("C://Users//HEHE//Desktop//Chair.txt","r"); if (fp_txt) { while (fscanf(fp_txt, "%lf %lf %lf", &txt_points.x, &txt_points.y, &txt_points.z) != EOF) {//將點存入容器尾部 my_vTxtPoints.push_back(txt_points); } } else cout << "讀取txt文件失敗"<<endl; num_txt = my_vTxtPoints.size(); //寫入點云數據 pcl::PointCloud<pcl::PointXYZ> ::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); cloud->width = num_txt; cloud->height = 1; cloud->is_dense = false; cloud->points.resize(cloud->width*cloud->height); for (int i = 0; i < cloud->points.size(); ++i) { cloud->points[i].x = my_vTxtPoints[i].x; cloud->points[i].y = my_vTxtPoints[i].y; cloud->points[i].z = my_vTxtPoints[i].z; } pcl::io::savePCDFileASCII("C://Users//HEHE//Desktop//Chair.pcd", *cloud); cout<< "從 Chair.txt讀取" << cloud->points.size() << "點寫入Chair.pcd" << endl; //打印出寫入的點 cout << "_________________________________" << endl; for (size_t i = 0; i < cloud->points.size(); ++i) cout << " " << cloud->points[i].x << " " << cloud->points[i].y << " " << cloud->points[i].z << endl; return 0;
}
如下左邊為txt格式數據,右邊為轉化的pcd文件格式。
2.針對于轉化的pcd格式文件再進行濾波處理,在濾波的算法選擇上,個人覺得針對于自己的數據采集區域不同,要達到的目的不一樣,所選擇的濾波算法也應該不一樣,但是在濾波算法選擇上還是挺迷茫的,希望有大神能指導一下(QQ:1753939345)
? ? ?? 第一種濾波算法:VoxelGrid濾波器對點云進行下采樣,(使用體素化網格方法實現下采樣,即減少點的數量 減少點云數據,并同時保存點云的形狀特征,在提高配準,曲面重建,形狀識別等算法速度中非常實用,PCL是實現的VoxelGrid類通過輸入的點云數據創建一個三維體素柵格,容納后每個體素內用體素中所有點的重心來近似顯示體素中其他點,這樣該體素內所有點都用一個重心點最終表示,對于所有體素處理后得到的過濾后的點云,這種方法比用體素中心(注意中心和重心)逼近的方法更慢,但是對于采樣點對應曲面的表示更為準確。)
#include "pch.h"
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>int
main(int argc, char** argv)
{pcl::PCLPointCloud2::Ptr cloud(new pcl::PCLPointCloud2());pcl::PCLPointCloud2::Ptr cloud_filtered(new pcl::PCLPointCloud2());//點云對象的讀取pcl::PCDReader reader;reader.read("C://Users//HEHE//Desktop//Chair.pcd", *cloud); //讀取點云到cloud中std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height<< " data points (" << pcl::getFieldsList(*cloud) << ").";/******************************************************************************創建一個葉大小為1cm的pcl::VoxelGrid濾波器,**********************************************************************************/pcl::VoxelGrid<pcl::PCLPointCloud2> sor; //創建濾波對象sor.setInputCloud(cloud); //設置需要過濾的點云給濾波對象//sor.setLeafSize(0.01f, 0.01f, 0.01f); //設置濾波時創建的體素體積為1cm的立方體sor.setLeafSize(10.0f, 10.0f, 10.0f);//該部分如果選擇的體素體積太小,就會造成索引溢
//出,所以本人改大點。sor.filter(*cloud_filtered); //執行濾波處理,存儲輸出std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height<< " data points (" << pcl::getFieldsList(*cloud_filtered) << ").";pcl::PCDWriter writer;writer.write("C://Users//HEHE//Desktop//Chair_downsampled.pcd", *cloud_filtered,Eigen::Vector4f::Zero(), Eigen::Quaternionf::Identity(), false);return (0);
}
?
左邊圖形是Chair的原采集數據,右邊是經過濾波降采樣之后的數據,本人是將保存的數據分別導入進CouldCompare進行查看的:
? ? ? ? 第2中濾波算法:statisticalOutlierRemoval濾波器移除離群點(**問題描述:**激光掃描通常會產生密度不均勻的點云數據集,另外測量中的誤差也會產生稀疏的離群點,此時,估計局部點云特征(例如采樣點處法向量或曲率變化率)時運算復雜,這會導致錯誤的數值,反過來就會導致點云配準等后期的處理失敗。)(**解決辦法:**對每個點的鄰域進行一個統計分析,并修剪掉一些不符合標準的點。具體方法為在輸入數據中對點到臨近點的距離分布的計算,對每一個點,計算它到所有臨近點的平均距離(假設得到的結果是一個高斯分布,其形狀是由均值和標準差決定),那么平均距離在標準范圍之外的點,可以被定義為離群點并從數據中去除。)代碼如下:
//statisticalOutlierRemoval濾波器移除離群點
#include "pch.h"
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>int
main(int argc, char** argv)
{pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);// 定義讀取對象pcl::PCDReader reader;// 讀取點云文件reader.read<pcl::PointXYZ>("C://Users//HEHE//Desktop//Chair.pcd", *cloud);std::cerr << "Cloud before filtering: " << std::endl;std::cerr << *cloud << std::endl;// 創建濾波器,對每個點分析的臨近點的個數設置為50 ,并將標準差的倍數設置為1 這意味著如果一//個點的距離超出了平均距離一個標準差以上,則該點被標記為離群點,并將它移除,存儲起來pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; //創建濾波器對象sor.setInputCloud(cloud); //設置待濾波的點云sor.setMeanK(50); //設置在進行統計時考慮查詢點臨近點數sor.setStddevMulThresh(1.0); //設置判斷是否為離群點的閥值sor.filter(*cloud_filtered); //存儲std::cerr << "Cloud after filtering: " << std::endl;std::cerr << *cloud_filtered << std::endl;pcl::PCDWriter writer;writer.write<pcl::PointXYZ>("C://Users//HEHE//Desktop//Chair_inliers.pcd", *cloud_filtered, false);sor.setNegative(true);sor.filter(*cloud_filtered);writer.write<pcl::PointXYZ>("C://Users//HEHE//Desktop//Chair_outliers.pcd", *cloud_filtered, false);return (0);
}
在運行該代碼時可能會出現一個錯誤:
解決方案如下:https://github.com/mariusmuja/flann/issues/386
找到你自己的dist.h文件,修改方案為:將該條語句放置在#if之前即可。
In order to fix the issues and get my program and the PCL libraries working correctly I had to move the "typedef unsigned long long pop_t;" outside the #if and #else so that either one will use the parameter to do the math. I don't know if this was the intent of this code but in order to progress in my software application I needed this error fixed. Please fix this as soon as possible and let me know so I can just update the flann and not worry about it.
上述代碼運行結果:
? ? 第3:從一個點云中提取索引,基于某一分割算法提取點云中的一個子集:
//基于某一分割算法提取點云中的一個子集
#include "pch.h"
#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("C://Users//HEHE//Desktop//Chair.pcd", *cloud_blob);//統計濾波前的點云個數std::cerr << "PointCloud before filtering: " << cloud_blob->width * cloud_blob->height << " data points." << std::endl;// 創建體素柵格下采樣: 下采樣的大小為1cmpcl::VoxelGrid<pcl::PCLPointCloud2> sor; //體素柵格下采樣對象sor.setInputCloud(cloud_blob); //原始點云//sor.setLeafSize(0.01f, 0.01f, 0.01f); // 設置采樣體素大小sor.setLeafSize(8.0f, 8.0f, 8.0f); // 設置采樣體素大小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>("C://Users//HEHE//Desktop//CCChair_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 therewhile (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 inliersextract.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 objectextract.setNegative(true);extract.filter(*cloud_f);cloud_filtered.swap(cloud_f);i++;}return (0);
}
?
程序運行截圖:
左邊對應原始圖,右邊對應提取圖。
?
?
總結
- 上一篇: SICK LMS151-10100使用攻
- 下一篇: 点云的NANS