Kinect三维重建2222——地面去除与法向量计算

————————————————
版权声明:本文为CSDN博主「悄然的我-粤Y」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010848251/article/details/71023259

———————————————————————————————————————————

从上一节中我们得到了8个PCD文件,其中第一个文件是地面点云,第二个文件是地面+盒子的点云,后面的文件是6个不同角度的地面+盒子+奶牛的点云。

这一节我们将生成6个不同角度的去除了地面的点云,并计算其法向量,同时获取一些信息,用作下一节来处理。

首先,我们有8个PCD文件:

const int numberOfViews = 8;//点云数量

然后加载这8个文件:

std::vector<PCD, Eigen::aligned_allocator<PCD> > data; //点云数据
std::string prefix("cow");
std::string extension(".pcd"); //声明并初始化string类型变量extension,表示文件后缀名
// 通过遍历文件名,读取pcd文件
for (int i = 0; i < numberOfViews; i++) //遍历所有的文件名
{
	std::string fname = prefix + num2str(i) + extension;
	// 读取点云,并保存到models
	PCD m;
	m.f_name = fname;
	if (pcl::io::loadPCDFile(fname, *m.cloud) == -1) //* 读入PCD格式的文件,如果文件不存在,返回-1
	{
		cout << "Couldn't read file " + fname + "." << endl; //文件不存在时,返回错误,终止程序。
		return (-1);
	}
	data.push_back(m);
}

下一步,先对第一个PCD文件(即地面点云)进行离群点过滤:

for (int i = 0; i <= 1; ++i){
	pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
	outrem.setInputCloud(data[i].cloud);
	outrem.setRadiusSearch(0.01);
	outrem.setMinNeighborsInRadius(10);
	outrem.filter(*data[i].cloud);
}

上面这一步要一定要执行。因为Kinect得到的点云噪声很大,看起来只有地面点云,实际上某些地方(如地面上方)会出现噪点。搜索半径设置为0.01米,最小临域点云个数设置为10个点云。
下一步,单独分别对第一个和第二个文件提取其最大Y值yMax1和yMax2,并以文本的方式进行保存。原因见第一节,这里不再叙述:

float yMax1 = -1000;
for (int i = 0; i < data[0].cloud->size(); ++i)
{
	if (data[0].cloud->points[i].y>yMax1)
		yMax1 = data[0].cloud->points[i].y;
}
cout << "yMax1:" << yMax1 << endl;
 
float yMax2 = -1000;
for (int i = 0; i < data[1].cloud->size(); ++i)
{
	if (data[1].cloud->points[i].y>yMax2)
		yMax2 = data[1].cloud->points[i].y;
}
cout << "yMax2:" << yMax2 << endl;
 
ofstream out("yMax.txt");
if (out.is_open())
{
	out << "yMax1:" << yMax1 << endl;
	out << "yMax2:" << yMax2 << endl;
	out.close();
}

提取出来的yMax1可以用来对剩下的6个文件进行地面点云的去除。yMax2在下一节中有用,先以文本的形式记录下来。
接下来就是对剩下的6个文件进行地面去除、离群点过滤、法向量计算、保存文件的操作。代码比较简单,就不贴出来了,请直接看完整代码。

下面只给出第一个角度的PCD文件进行法向量计算的图:

 最终我们输出得到6个带有法向量的PCD文件(即XYZ+Normal):

 其中yMax.txt保存了yMax1和yMax2的值,下一节我们将要使用yMax2的值。

下面是完整代码:

#define vtkRenderingCore_AUTOINIT 4(vtkInteractionStyle,vtkRenderingFreeType,vtkRenderingFreeType,vtkRenderingOpenGL) 
#define vtkRenderingVolume_AUTOINIT 1(vtkRenderingVolumeOpenGL)
#include <string>
#include <kinect.h>
#include <iostream>
#include <fstream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <pcl/io/pcd_io.h> //PCL的PCD格式文件的输入输出头文件
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h> //PCL对各种格式的点的支持头文件
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/radius_outlier_removal.h>  
#include <pcl/filters/conditional_removal.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/registration/icp.h> //ICP(iterative closest point)配准
#include <pcl/registration/correspondence_rejection_surface_normal.h>
#include <pcl/console/parse.h>  //pcl控制台解析
#include <pcl/ModelCoefficients.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/extract_indices.h>  
//kd树
#include <pcl/kdtree/kdtree_flann.h>
//特征提取
#include <pcl/features/normal_3d_omp.h>
#include <pcl/features/normal_3d.h>
//重构
#include <boost/thread/thread.hpp>
#include <Eigen/Dense>
 
using namespace std;
 
string num2str(int i)
{
	stringstream ss;
	ss << i;
	return ss.str();
}
 
//定义结构体,用于处理点云
struct PCD
{
	std::string f_name; //文件名
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud; //点云指针
	pcl::PointCloud<pcl::PointNormal>::Ptr cloudWithNormal;//存储估计的法线的指针
	//构造函数
	PCD() : cloud(new pcl::PointCloud<pcl::PointXYZ>), cloudWithNormal(new pcl::PointCloud<pcl::PointNormal>){}; //初始化
};
 
int main()
{
	const int numberOfViews = 8;//点云数量
	std::vector<PCD, Eigen::aligned_allocator<PCD> > data; //点云数据
	std::string prefix("cow");
	std::string extension(".pcd"); //声明并初始化string类型变量extension,表示文件后缀名
	// 通过遍历文件名,读取pcd文件
	for (int i = 0; i < numberOfViews; i++) //遍历所有的文件名
	{
		std::string fname = prefix + num2str(i) + extension;
		// 读取点云,并保存到models
		PCD m;
		m.f_name = fname;
		if (pcl::io::loadPCDFile(fname, *m.cloud) == -1) //* 读入PCD格式的文件,如果文件不存在,返回-1
		{
			cout << "Couldn't read file " + fname + "." << endl; //文件不存在时,返回错误,终止程序。
			return (-1);
		}
		data.push_back(m);
	}
	//去除离群点
	for (int i = 0; i <= 1; ++i){
		pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
		outrem.setInputCloud(data[i].cloud);
		outrem.setRadiusSearch(0.01);
		outrem.setMinNeighborsInRadius(10);
		outrem.filter(*data[i].cloud);
	}
	
	float yMax1 = -1000;
	for (int i = 0; i < data[0].cloud->size(); ++i)
	{
		if (data[0].cloud->points[i].y>yMax1)
			yMax1 = data[0].cloud->points[i].y;
	}
	cout << "yMax1:" << yMax1 << endl;
 
	float yMax2 = -1000;
	for (int i = 0; i < data[1].cloud->size(); ++i)
	{
		if (data[1].cloud->points[i].y>yMax2)
			yMax2 = data[1].cloud->points[i].y;
	}
	cout << "yMax2:" << yMax2 << endl;
 
	ofstream out("yMax.txt");
	if (out.is_open())
	{
		out << "yMax1:" << yMax1 << endl;
		out << "yMax2:" << yMax2 << endl;
		out.close();
	}
	
	for (int i = 2; i < numberOfViews; ++i)
	{
		//-----------------------去除地面点云--------------------------
		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>("x", pcl::ComparisonOps::GT, -0.4)));
		range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new	pcl::FieldComparison<pcl::PointXYZ>("x", pcl::ComparisonOps::LT, 0.4)));
		range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new	pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 1.3)));*/
		range_cond->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new	pcl::FieldComparison<pcl::PointXYZ>("y", pcl::ComparisonOps::GT, yMax1)));
		pcl::ConditionalRemoval<pcl::PointXYZ> condrem(range_cond, false);
		condrem.setInputCloud(data[i].cloud);
		condrem.setKeepOrganized(false);
		condrem.filter(*data[i].cloud);
		//--------------------------------------------------------------
 
		//-----------------------去除离群点------------------------
		pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
		outrem.setInputCloud(data[i].cloud);
		outrem.setRadiusSearch(0.01);
		outrem.setMinNeighborsInRadius(10);
		outrem.filter(*data[i].cloud);
 
		//pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
		//sor.setInputCloud(data[i].cloud);
		//sor.setMeanK(10);
		//sor.setStddevMulThresh(1.0);
		//sor.filter(*data[i].cloud);
		//--------------------------------------------------------------
 
		// -------------------计算法向量----------------------
		//pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>); //法向量点云对象指针
		pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;//法线估计对象
		pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);//存储估计的法线的指针
		pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
		tree->setInputCloud(data[i].cloud);
		n.setInputCloud(data[i].cloud);
		n.setSearchMethod(tree);
		n.setRadiusSearch(0.03);
		//n.setKSearch(25);
		n.compute(*normals); //计算法线,结果存储在normals中
		pcl::concatenateFields(*data[i].cloud, *normals, *data[i].cloudWithNormal);//将点云和法线放到一起
		// ---------------------------------------------------
 
		string fileName = "cow" + num2str(i - 2) + "_withNormal.pcd";
		pcl::io::savePCDFile(fileName, *data[i].cloudWithNormal, true);
		cout << fileName << " has been saved." << endl;
 
		// 显示结果图
		boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D viewer"));
		int v1;
		viewer->createViewPort(0.0, 0.0, 1.0, 1.0, v1);
		viewer->setBackgroundColor(0, 0, 0);
		viewer->initCameraParameters();
		pcl::visualization::PointCloudColorHandlerCustom<pcl::PointNormal> cloud_color1(data[i].cloudWithNormal, 255, 255, 255);
		viewer->addPointCloud(data[i].cloudWithNormal, cloud_color1, "cloud_color1", v1);
		viewer->addPointCloudNormals<pcl::PointNormal>(data[i].cloudWithNormal, 50, 0.05, "source_normals", v1); //第2个参数表示多少个点显示一次向量,第3个参数表示向量长度,单位m
		viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "source_normals", v1);
		while (!viewer->wasStopped()){
			viewer->spinOnce();
		}
	}
	std::system("pause");
	return 0;
}

————————————————
版权声明:本文为CSDN博主「悄然的我-粤Y」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010848251/article/details/71023259

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值