1. 程式人生 > >基於PCL的三維重建——點雲的濾波處理

基於PCL的三維重建——點雲的濾波處理

在獲取點雲資料時 ,由於裝置精度,操作者經驗環境因素帶來的影響,以及電磁波的衍射特性,被測物體表面性質變化和資料拼接配準操作過程的影響,點雲資料中講不可避免的出現一些噪聲。在點雲處理流程中濾波處理作為預處理的第一步,對後續的影響比較大,只有在濾波預處理中將噪聲點 ,離群點,孔洞,資料壓縮等按照後續處理定製,才能夠更好的進行配準,特徵提取,曲面重建,視覺化等後續應用處理,PCL中點雲濾波模組提供了很多靈活實用的濾波處理演算法,例如:雙邊濾波,高斯濾波,條件濾波,直通濾波,基於隨機取樣一致性濾波。

(一)使用VoxelGrid濾波器對點雲進行下采樣

       使用體素化網格方法實現下采樣,即減少點的數量 減少點雲資料,並同時儲存點雲的形狀特徵,在提高配準,曲面重建,形狀識別等演算法速度中非常實用,PCL是實現的VoxelGrid類通過輸入的點雲資料建立一個三維體素柵格,容納後每個體素內用體素中所有點的重心來近似顯示體素中其他點,這樣該體素內所有點都用一個重心點最終表示,對於所有體素處理後得到的過濾後的點雲,這種方法比用體素中心逼近的方法更慢,但是對於取樣點對應曲面的表示更為準確。

#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("squ4r.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.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("squ4r filter.pcd", *cloud_filtered,Eigen::Vector4f::Zero(), Eigen::Quaternionf::Identity(), false);
	return (0);
}

(二)使用statisticalOutlierRemoval濾波器移除離群點
      使用統計分析技術,從一個點雲資料中集中移除測量噪聲點(也就是離群點)比如:鐳射掃描通常會產生密度不均勻的點雲資料集,另外測量中的誤差也會產生稀疏的離群點,使效果不好,估計區域性點雲特徵(例如取樣點處法向量或曲率變化率)的運算複雜,這會導致錯誤的數值,反過來就會導致點雲配準等後期的處理失敗。
      解決辦法:每個點的鄰域進行一個統計分析,並修剪掉一些不符合一定標準的點,稀疏離群點移除方法基於在輸入資料中對點到臨近點的距離分佈的計算,對每一個點,計算它到它的所有臨近點的平均距離,,假設得到的結果是一個高斯分佈,其形狀是由均值和標準差決定,平均距離在標準範圍之外的點,可以被定義為離群點並可從資料中去除。
#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>("sun2.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>("sun1_inliers.pcd", *cloud_filtered, false);

	sor.setNegative(true);
	sor.filter(*cloud_filtered);
	writer.write<pcl::PointXYZ>("sun1_outliers.pcd", *cloud_filtered, false);

	return (0);
}