保存点云数据_3D点云配准(二多幅点云配准)

f6956d94143e538ce12edd33d3d80b81.png

本文首发于微信公众号「3D视觉工坊」:3D点云配准(二多幅点云配准)

在上一篇文章 点云配准(一 两两配准)中我们介绍了两两点云之间的配准原理。本篇文章,我们主要介绍一下PCL中对于多幅点云连续配准的实现过程,重点请关注代码行的注释。

对于多幅点云的配准,它的主要思想是对所有点云进行变换,使得都与第一个点云在统一坐标系中。在每个连贯的、有重叠的点云之间找到最佳的变换,并累积这些变换到全部的点云。能够进行ICP算法的点云需要进行粗略的预匹配,并且一个点云与另一个点云需要有重叠部分

bc91e5bd153a1004a417cd5bb849a65c.png

此处我们以郭浩主编的《点云库PCL从入门到精通》提供的示例demo来介绍一下多幅点云进行配准的过程。

/* ---[ */
int main (int argc, char** argv)
{// 加载数据
std::vector<PCD, Eigen::aligned_allocator<PCD> > data; //存储管理所有打开的点云
loadData (argc, argv, data); //加载所有点云文件到data//检查用户输入
if (data.empty ()){PCL_ERROR ("Syntax is: %s <source.pcd> <target.pcd> [*]", argv[0]);PCL_ERROR ("[*] - multiple files can be added. The registration results of (i, i+1) will be registered against (i+2), etc");PCL_INFO ("Example: %s `rospack find pcl`/test/bun0.pcd `rospack find pcl`/test/bun4.pcd", argv[0]);return (-1);}PCL_INFO ("Loaded %d datasets.", (int)data.size ());//创建一个PCL可视化对象
p = new pcl::visualization::PCLVisualizer (argc, argv, "Pairwise Incremental Registration example"); //创建一个PCL可视化对象
p->createViewPort (0.0, 0, 0.5, 1.0, vp_1); //用左半窗口创建视口vp_1
p->createViewPort (0.5, 0, 1.0, 1.0, vp_2); //用右半窗口创建视口vp_2
PointCloud::Ptr result (new PointCloud), source, target;Eigen::Matrix4f GlobalTransform = Eigen::Matrix4f::Identity (), pairTransform;for (size_t i = 1; i < data.size (); ++i)  //循环处理所有点云
{source = data[i-1].cloud; //连续配准
target = data[i].cloud;   //相邻两组点云//添加可视化数据
showCloudsLeft(source, target); //可视化为配准的源和目标点云
PointCloud::Ptr temp (new PointCloud);PCL_INFO ("Aligning %s (%d) with %s (%d).n", data[i-1].f_name.c_str (), source->points.size (), data[i].f_name.c_str (), target->points.size ());//调用子函数完成一组点云的配准,temp返回配准后两组点云在第一组点云坐标下的点云,pairTransform返回从目标点云target到源点云source的变换矩阵。//现在我们开始进行实际的匹配,由子函数pairAlign具体实现,//其中参数有输入一组需要配准的点云,以及是否进行下采样的设置项,其他参数输出配准后的点云及变换矩阵。
pairAlign (source, target, temp, pairTransform, true);//把当前的两两配对转换到全局变换//把当前的两两配对后的点云temp转换到全局坐标系下(第一个输入点云的坐标系)返回result
pcl::transformPointCloud (*temp, *result, GlobalTransform);//update the global transform更新全局变换//用当前的两组点云之间的变换更新全局变换
GlobalTransform = pairTransform * GlobalTransform;//保存转换到第一个点云坐标下的当前配准后的两组点云result到文件i.pcd
std::stringstream ss;ss << i << ".pcd";pcl::io::savePCDFile (ss.str (), *result, true);}
}

对于上述过程中的核心函数pairAlign(),我们重点介绍如下:

/**匹配一对点云数据集并且返还结果*参数 cloud_src 是源点云*参数 cloud_src 是目标点云*参数output输出的配准结果的源点云*参数final_transform是在来源和目标之间的转换*/
void pairAlign (const PointCloud::Ptr cloud_src, const PointCloud::Ptr cloud_tgt, PointCloud::Ptr output, Eigen::Matrix4f &final_transform, bool downsample = false)
{//下采样//为了一致性和高速的下采样//注意:为了大数据集需要允许这项
PointCloud::Ptr src (new PointCloud); //存储滤波后的源点云
PointCloud::Ptr tgt (new PointCloud); //存储滤波后的目标点云
pcl::VoxelGrid<PointT> grid; //滤波处理对象
if (downsample){grid.setLeafSize (0.05, 0.05, 0.05); //设置滤波处理时采用的体素大小
grid.setInputCloud (cloud_src);grid.filter (*src);grid.setInputCloud (cloud_tgt);grid.filter (*tgt);}else
{src = cloud_src;tgt = cloud_tgt;}//计算曲面法线和曲率
PointCloudWithNormals::Ptr points_with_normals_src (new PointCloudWithNormals);PointCloudWithNormals::Ptr points_with_normals_tgt (new PointCloudWithNormals);pcl::NormalEstimation<PointT, PointNormalT> norm_est; //点云法线估计对象
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());norm_est.setSearchMethod (tree); //设置估计对象采用的搜索对象
norm_est.setKSearch (30); //设置估计时进行搜索用的k数
norm_est.setInputCloud (src);norm_est.compute (*points_with_normals_src); //下面分别估计源和目标点云法线
pcl::copyPointCloud (*src, *points_with_normals_src);norm_est.setInputCloud (tgt);norm_est.compute (*points_with_normals_tgt);pcl::copyPointCloud (*tgt, *points_with_normals_tgt);//一切准备好之后,可以开始配准了,创建ICP对象,设置它的参数//以需要匹配的两个点云作为输入,使用时,参数的设置需要根据自己的数据集进行调整。////举例说明我们自定义点的表示(以上定义)
MyPointRepresentation point_representation;//调整'curvature'尺寸权重以便使它和x, y, z平衡
float alpha[4] = {1.0, 1.0, 1.0, 1.0};point_representation.setRescaleValues (alpha);//// 配准
pcl::IterativeClosestPointNonLinear<PointNormalT, PointNormalT> reg;  //配准对象
reg.setTransformationEpsilon (1e-6); //设置收敛判断条件,越小精度越大,收敛也越慢//将两个对应关系之间的(src<->tgt)最大距离设置为10厘米//注意:根据你的数据集大小来调整
reg.setMaxCorrespondenceDistance (0.1);//设置点表示
reg.setPointRepresentation (boost::make_shared<const MyPointRepresentation> (point_representation));reg.setInputCloud (points_with_normals_src); //设置源点云
reg.setInputTarget (points_with_normals_tgt); //设置目标点云////在一个循环中运行相同的最优化并且使结果可视化
Eigen::Matrix4f Ti = Eigen::Matrix4f::Identity (), prev, targetToSource;PointCloudWithNormals::Ptr reg_result = points_with_normals_src;//由于这是一个demo,因而希望显示匹配过程的迭代过程,为达到该目的,ICP在内部进行计算时,限制其最大的迭代次数为2,即每迭代两次就认为收敛,停止内部迭代
reg.setMaximumIterations (2); //设置最大迭代次数//手动迭代,此处设置为30次,每手动迭代一次,在配准结果视口对迭代的最新的结果进行刷新显示
for (int i = 0; i < 30; ++i){PCL_INFO ("Iteration Nr. %d.n", i);//为了可视化的目的保存点云
points_with_normals_src = reg_result;reg.setInputCloud (points_with_normals_src);reg.align (*reg_result);//在每一个迭代之间累积转换
Ti = reg.getFinalTransformation () * Ti;//如果这次转换和之前转换之间的差异小于阈值//则通过减小最大对应距离来改善程序//也就是说,如果迭代N次找到的变换和迭代N-1次中找到的变换之间的差异小于传给ICP的变换收敛阈值,我们选择源和目标之间更靠近的对应点距离阈值来改善配准过程if (fabs ((reg.getLastIncrementalTransformation () - prev).sum ()) < reg.getTransformationEpsilon ())reg.setMaxCorrespondenceDistance (reg.getMaxCorrespondenceDistance () - 0.001);prev = reg.getLastIncrementalTransformation ();//可视化当前状态
showCloudsRight(points_with_normals_tgt, points_with_normals_src);}////一旦找到最优的变换,ICP返回的变换是从源点云到目标点云的变换矩阵,我们求逆变换得到从目标点云到源点云的变换矩阵,并应用到目标点云,变换后的目标点云然后添加到源点云中。// 得到目标点云到源点云的变换
targetToSource = Ti.inverse();////把目标点云转换回源框架
pcl::transformPointCloud (*cloud_tgt, *output, targetToSource);p->removePointCloud ("source");p->removePointCloud ("target");PointCloudColorHandlerCustom<PointT> cloud_tgt_h (output, 0, 255, 0);PointCloudColorHandlerCustom<PointT> cloud_src_h (cloud_src, 255, 0, 0);p->addPointCloud (output, cloud_tgt_h, "target", vp_2);p->addPointCloud (cloud_src, cloud_src_h, "source", vp_2);PCL_INFO ("Press q to continue the registration.n");p->spin ();p->removePointCloud ("source");p->removePointCloud ("target");//添加源点云到转换目标
*output += *cloud_src;final_transform = targetToSource;}
效果图如下,此为动态图,请耐心等待几秒钟,注意迭代过程。

7c0e322d94b9bf64f2515b9ef464eb89.gif

思考:

对于小型或者中型数量的点云数据(点个数<100,000),我们选择ICP来进行迭代配准,可是利用对应点的特征计算和匹配较为耗时,如果对于两个大型点云(都超过100000)之间的刚体变换的确定,有什么好办法可以解决呢?

主要参考:郭浩主编的<点云库PCL从入门到精通>

上述内容,如有侵犯版权,请联系作者,会自行删文。

星球成员,可免费提问,并邀进讨论群

d13e7d57a127704af263cd389d11f704.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/504928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

url存在宽字节跨站漏洞_5分钟速览丨常见的Web安全漏洞及测试方法

中秋小长假“余额”就剩半天了尽管心里有太多不舍也要调整自己毕竟假期都是短暂的工作才是职场人生的常态为了尽快消除“假日综合症”e小安贴心送上小文一篇小伙伴们赶紧“脉动”回来吧各类web应用充斥在我们的网络生活中&#xff0c;但是因为开发者安全意识不强而导致的安全问…

9切换中文mac_超详细的Mac重装系统教程!让重装系统变得简单起来!

mac电脑该怎么重装系统&#xff1f;苹果电脑在长时间使用后&#xff0c;系统可能会变得比较慢&#xff0c;另外各种缓存垃圾也会越堆越多&#xff0c;影响电脑的反应速度。mac OS系统是苹果电脑独有的操作系统,重装mac系统过程和Win系统完全不同,所以第一次使用苹果电脑的用户都…

delphi datasnap断线后再次连接_电脑连接WiFi后经常出现断线断开连接问题的解决方法...

电脑连接WiFi总是断线怎么办&#xff1f;有用户的笔记本在连接无线网络后&#xff0c;一直出现断线问题&#xff0c;并且网速也很慢&#xff0c;这是怎么回事&#xff1f;电脑连接WiFi后经常断开连接该如何解决&#xff1f;下面给大家分享具体操作步骤。操作步骤&#xff1a;1、…

添加javascript代码:_JavaScript(1)

一、JavaScript组成1.ECMAScript&#xff1a;是ECMA制定的脚本语言的语法标准&#xff0c;基础语法的规范&#xff0c;为了让不同的浏览器都可以运行通过标准运行出来的代码。2.文档对象模型(DOM)&#xff1a;JavaScript操作网页上的元素(标签)的API。3.浏览器对象模型(BOM)&am…

win 8 共享连接数是多少_局域网IP限速怎么配置,限速多少比较合适

由于视频和下载可以轻易的占用大量带宽&#xff0c;为了网络的稳定运行&#xff0c;大部分局域网都会对客户端进行一定的限速。本文中&#xff0c;我将介绍如何根据带宽来做限速&#xff0c;限速设置多少比较合理&#xff1f;1. 限速多少比较合理&#xff1f;正常的办公上网&am…

xp 4g内存补丁_32位操作系统导致电脑可用内存不足4G

现象描述今天给大家分享一个关于电脑使用的小知识&#xff1a;电脑安装32位操作系统&#xff0c;导致电脑可用内存低于4G。详细解释一下&#xff1a;如果你的电脑安装的操作系统是32位操作系统&#xff0c;无论你电脑安装了多大的内存&#xff0c;可用内存都不会超过4G&#xf…

maven 镜像_Maven(一)

1. 掌握M2Elipse插件安装及Maven仓库的配置2. 掌握Maven工程目录结构和创建工程3. 掌握Maven工程的关系4. 掌握Maven常见的插件5. 掌握Maven命令壹、什么是 Maven在Maven中&#xff0c;可以理解为所有的项目都是一个个的对象。贰、Maven 和 ANT 的区别叁、 Maven 的下载与 IDE …

文本编辑器查看 cprintf颜色_实战PyQt5: 028-纯文本编辑控件QPlainTextEdit

QPlainTextEdit简介QPlainTextEdit小部件是一个用于编辑和显示纯文本控件&#xff0c;QPlainTextEdit控件与QTextEdit控件使用了相同的技术和概念&#xff0c;但是它为纯文本处理提供了优化。QPlainTextEdit是一个支持纯文本的高级查看器/编辑器&#xff0c;可以处理大型文档并…

使用通用mapper实现条件查询_【微服务】152:Stream流和通用mapper批量查询的使用...

今天是刘小爱自学Java的第152天。感谢你的观看&#xff0c;谢谢你。学习计划安排如下&#xff1a;补充完昨天商品查询中关于分类和品牌的部分&#xff0c;其中牵扯到了两个非常重要的知识点&#xff1a;Stream流的使用&#xff0c;这个学过后基本就没怎么使用过&#xff0c;这次…

java递归实现多级菜单栏_vue+ java 实现多级菜单递归效果

效果如图&#xff1a;大概思路&#xff1a;树形视图使用的是vue官方事例代码&#xff0c;java负责封装数据&#xff0c;按照vue官方事例的数据结构封装数据即可。有两个需要关注的点&#xff1a;1.官方事例的数据结构是一个对象里面包含着集合&#xff0c;而不是一个集合对象 2…

mfc打开一个.txt文件并进行处理_文件处理方法Python

你好&#xff0c;我是goldsunC让我们一起进步吧&#xff01;1. 文件说明文件是存储在存储器上的数据序列&#xff0c;在计算机中&#xff0c;所有文件都是以二进制的方式进行存储的&#xff0c;而文件的展示形式一般分为两种&#xff1a;文本形式和二进制形式。文本文件由单一特…

qt的如何调整显示不为科学记数法_Excel操作技巧:如何将信息快速准确的录入Excel?...

前面几篇文章中&#xff0c;给大家介绍了一些excel的基本操作技巧&#xff0c;以及如何高效地浏览表格。相信大家多少都会获得一点收获&#xff0c;希望能够为大家提供一些工作上的帮助。工作中&#xff0c;老板经常会安排我们手动输入一些数据&#xff0c;考虑到我们要不停的手…

如何给mysql表添加百万条数据_给mysql一百万条数据的表添加索引

直接alter table add index 添加索引&#xff0c;执行一个小时没反应&#xff0c;并且会导致锁表&#xff1b;故放弃该办法&#xff0c;最终解决办法如下&#xff1a;一.打开mysql 命令行客户端这里我们那可以看到导出的数据文件所存放的默认位置 C:\ProgramData\MySQL\MySQL …

qchart画完以后删除_冬天的夜晚很难画?老师分步骤教你画,简单易学,收藏起来临摹...

今日绘画主题&#xff1a;冬天的夜晚冬天是美好的&#xff0c;也是很多画家喜欢创作的题材之一&#xff0c;但是&#xff0c;冬天的夜晚却是很难画&#xff0c;今天分步骤图解教大家画冬天的夜晚&#xff0c;快收藏起来临摹吧。>>>【第一步】&#xff1a;先来画线稿&a…

lrtemplate如何导入pr_PR模板使用套路讲解 视频剪辑教程

​pr模板在使用的时候需要注意的事项以及如何正确操作https://www.zhihu.com/video/1237765482302226432pr模板在使用的时候需要【注意的事项】1、下载的模板必须解压后使用2、下载模板的时候网站上页面都会有介绍模版所支持的版本3、弹出不同版本的提示窗口&#xff0c;点击确…

linux没有usr目录_了解linux系统目录,sys,tmp,usr,var!

linux小白到大神的成长之路&#xff1a;了解linux系统目录&#xff0c;sys,tmp,usr,var&#xff01;本经验由宗龙龙原创,全文共600多字&#xff0c;阅读需要14分钟&#xff0c;如果文中存在错误&#xff0c;还请大家多多指点&#xff0c;我会积极改进的&#xff01;​这篇文章将…

svd奇异值分解_奇异值分解SVD

点击上方蓝字关注我们奇异值分解(SVD)在计算机视觉中有着广泛的应用&#xff0c;如数据降维、推荐系统、自然语言处理等。本文是介绍SVD的数学计算过程&#xff0c;并从SVD的性质说明其应用的原理。01特征值与特征向量奇异值分解(SVD)与特征分解类似&#xff0c;是将矩阵分解为…

python turtle画房子详细解释_[宜配屋]听图阁

我就废话不多说了&#xff0c;直接上代码吧&#xff01;import turtletturtle.Turtle()turtle.Turtle().screen.delay(0)tleftturtle.Turtle()#第一部分t.penup()t.goto(0,0)t.pendown()t.left(20)t.forward(110)t.left(25)t.forward(40)t.left(100)t.circle(180,20)t.right(12…

简易航空订票系统_四川航空APP全新升级改版,三大亮点提升订票体验

成都2015年10月23日电 /美通社/ -- 四川航空发布手机APP全新的3.0版本&#xff0c;不仅针对界面风格做了全新改变&#xff0c;在iOS9、安卓系统上的展示更加炫丽&#xff1b;而且在机票预订、航班动态、特价机票方面均做了大幅升级&#xff0c;会员服务也更加贴心。对于有机票需…

python可视化分析网易云音乐评论_网易云音乐热门评论api分析

网上有现成的例子我就扒过来了&#xff01;&#xff01;title: 网易云评论api分析date: 2018-12-24 20:54:46tags: [python]网易云音乐是个好地方&#xff0c;里面各个都是人才&#xff0c;特别是评论区……所以我就想把评论爬下来看看&#xff0c;下面记录一下分析api的过程与…