OPEN3D学习笔记(六)——Multiway registration
OPEN3D学习笔记(六)
- Multiway registration
- Input
- Pose graph
- Visualize optimization
- Make a combined point cloud
Multiway registration
多向配准是在全局空间中对齐多个几何图形的过程。通常,输入是一组几何图形(例如,点云或RGBD图像) {Pi}。输出是一组刚性变换{Ti} ,因此变换后的点云{TiPi}在全局空间中对齐。
Input
读取,并下采样
# 返回的是一个list,里面都是降采样的(如果体素大小为0则源点云)
def load_point_clouds(voxel_size=0.0):pcds = []for i in range(3):pcd = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_%d.pcd" % i)pcd_down = pcd.voxel_down_sample(voxel_size=voxel_size)pcds.append(pcd_down)return pcdsvoxel_size = 0.02 # 体素大小
pcds_down = load_point_clouds(voxel_size) # 获得一个经过降采样的点云list
o3d.visualization.draw_geometries(pcds_down) # 可视化这些点云,看看他们的位置,一般是没对准的,很乱
Pose graph
姿势图具有两个关键元素:nodes,edges。
我的理解:nodes就是每个点云,edges连接两个相邻的node(点云),这两个点云有最多的重叠部分
逐对配准容易出错,因此分成两类:
Odometry edges
Loop closure edges
测渗法边缘连接时间上接近的相邻节点。本地注册算法(例如ICP)可以可靠地对齐它们。闭环边缘连接任何不相邻的节点。该对齐方式是通过全局注册找到的,可靠性较低。在Open3D中,这两类边缘由PoseGraphEdge的初始化程序中的不确定参数来区分。
# 两两配准
def pairwise_registration(source, target):print("Apply point-to-plane ICP")icp_coarse = o3d.registration.registration_icp(source, target, max_correspondence_distance_coarse, np.identity(4),o3d.registration.TransformationEstimationPointToPlane()) # 用一个初始4*4转换矩阵,粗配准icp_fine = o3d.registration.registration_icp(source, target, max_correspondence_distance_fine,icp_coarse.transformation,o3d.registration.TransformationEstimationPointToPlane()) # 用粗配准的出的旋转矩阵,进行再次配准transformation_icp = icp_fine.transformation # 配准后的出的旋转矩阵information_icp = o3d.registration.get_information_matrix_from_point_clouds(source, target, max_correspondence_distance_fine,icp_fine.transformation) # 从变换矩阵计算信息矩阵return transformation_icp, information_icp# 全局配准
def full_registration(pcds, max_correspondence_distance_coarse, max_correspondence_distance_fine):pose_graph = o3d.registration.PoseGraph() # 姿势图odometry = np.identity(4) # 对角线是1的方阵,4*4pose_graph.nodes.append(o3d.registration.PoseGraphNode(odometry)) # 不懂这句,odometry是边n_pcds = len(pcds)for source_id in range(n_pcds): # 遍历每个点云for target_id in range(source_id + 1, n_pcds): # 遍历两两点云transformation_icp, information_icp = pairwise_registration(pcds[source_id], pcds[target_id]) # 进行两两配对,获得转换矩阵、信息矩阵print("Build o3d.registration.PoseGraph")if target_id == source_id + 1: # odometry case 在姿势图相邻的两个点云odometry = np.dot(transformation_icp, odometry) # 两个矩阵相乘pose_graph.nodes.append(o3d.registration.PoseGraphNode(np.linalg.inv(odometry))) # 求逆矩阵,往图里添加nodepose_graph.edges.append(o3d.registration.PoseGraphEdge(source_id,target_id,transformation_icp,information_icp,uncertain=False)) # uncertain用于区分是Odometry edgeselse: # loop closure casepose_graph.edges.append(o3d.registration.PoseGraphEdge(source_id,target_id,transformation_icp,information_icp,uncertain=True)) # 添加边Loop closure edges,非邻居的点云的边return pose_graph # 返回图print("Full registration ...")
max_correspondence_distance_coarse = voxel_size * 15 # 粗配准距离阈值,衡量两个对应点的距离
max_correspondence_distance_fine = voxel_size * 1.5 # 精配准距离阈值
with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm: # 上下文管理器pose_graph = full_registration(pcds_down,max_correspondence_distance_coarse,max_correspondence_distance_fine)
Open3D使用函数global_optimization执行姿势图优化。
可以选择两种类型的优化方法:GlobalOptimizationGaussNewton或GlobalOptimizationLevenbergMarquardt。
推荐使用后者,因为它具有更好的收敛性。
GlobalOptimizationConvergenceCriteria类可用于设置最大迭代次数和各种优化参数。
GlobalOptimizationOption类定义了两个选项。max_correspondence_distance确定对应阈值。edge_prune_threshold是修剪异常边缘的阈值。reference_node是被视为全局空间的节点ID。
print("Optimizing PoseGraph ...")
option = o3d.registration.GlobalOptimizationOption(max_correspondence_distance=max_correspondence_distance_fine,edge_prune_threshold=0.25,reference_node=0) # 优化全局配准
with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm:o3d.registration.global_optimization(pose_graph, o3d.registration.GlobalOptimizationLevenbergMarquardt(),o3d.registration.GlobalOptimizationConvergenceCriteria(), option) # 进行全局优化
全局优化在姿势图上执行两次。第一遍将考虑所有边缘的情况优化原始姿态图的姿态,并尽最大努力区分不确定边缘之间的错误对齐。这些错误对齐的行处理权重较小,并且在第一次通过后将其修剪。第二遍没有它们运行,并且产生了紧密的全局对齐。在此示例中,所有边缘均被视为真实对齐,因此第二遍将立即终止。
Visualize optimization
使用draw_geometries列出并可视化已变换的点云
print("Transform points and display")
for point_id in range(len(pcds_down)):print(pose_graph.nodes[point_id].pose)pcds_down[point_id].transform(pose_graph.nodes[point_id].pose) # 每一个点云都使用旋转矩阵进行变换位置,变换到同一坐标系下
o3d.visualization.draw_geometries(pcds_down) # 可视化配准后的点云
'''
输出
Transform points and display
[[ 1.00000000e+00 -2.50509994e-19 0.00000000e+00 0.00000000e+00][-3.35636805e-20 1.00000000e+00 1.08420217e-19 -8.67361738e-19][-1.08420217e-19 -1.08420217e-19 1.00000000e+00 0.00000000e+00][ 0.00000000e+00 0.00000000e+00 0.00000000e+00 1.00000000e+00]]
[[ 0.8401689 -0.14645453 0.52217554 0.34785474][ 0.00617659 0.96536804 0.2608187 -0.39427149][-0.54228965 -0.2159065 0.81197679 1.7300472 ][ 0. 0. 0. 1. ]]
[[ 0.96271237 -0.07178412 0.2608293 0.3765243 ][-0.00196124 0.96227508 0.27207136 -0.48956598][-0.27051994 -0.26243801 0.92625334 1.29770817][ 0. 0. 0. 1. ]]
'''
Make a combined point cloud
合并点云
PointCloud具有便捷的运算符+,可以将两个点云合并为一个。合并后,将使用voxel_down_sample对点进行统一重新采样。建议在合并点云之后进行后处理,因为这可以减轻重复的点或过度密集的点。
pcds = load_point_clouds(voxel_size)
pcd_combined = o3d.geometry.PointCloud()
for point_id in range(len(pcds)):pcds[point_id].transform(pose_graph.nodes[point_id].pose) # 每一个点云都转换位置pcd_combined += pcds[point_id] # 点云不断累加
pcd_combined_down = pcd_combined.voxel_down_sample(voxel_size=voxel_size) # 降采样,因为累加会有很多重复点
o3d.io.write_point_cloud("multiway_registration.pcd", pcd_combined_down) # 保存配准后的点云
o3d.visualization.draw_geometries([pcd_combined_down]) # 渲染配准结果
完美~~
OPEN3D学习笔记(六)——Multiway registration相关推荐
- OPEN3D学习笔记(三)——KDTree ICP Registration
OPEN3D学习笔记(三) KDTree Build KDTree from point cloud Find neighboring points ICP Registration 可视化配准过程 ...
- OPEN3D学习笔记(四)——Global registration
OPEN3D学习笔记(四) Global registration Extract geometric feature Input RANSAC Local refinement Fast globa ...
- Ethernet/IP 学习笔记六
Ethernet/IP 学习笔记六 EtherNet/IP defines two primary types of communications: explicit and implicit (Ta ...
- 吴恩达《机器学习》学习笔记六——过拟合与正则化
吴恩达<机器学习>学习笔记六--过拟合与正则化 一. 过拟合问题 1.线性回归过拟合问题 2.逻辑回归过拟合问题 3.过拟合的解决 二. 正则化后的代价函数 1.正则化思想 2.实际使用的 ...
- ROS学习笔记六:理解ROS服务和参数
ROS学习笔记六:理解ROS服务和参数 主要介绍ROS服务和参数,同时使用命令行工具rosservice和rosparam. ROS service service是节点之间互相通信的另一种方式,se ...
- opencv 手选roi区域_【opencv学习笔记六】图像的ROI区域选择与复制
图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...
- JS学习笔记六:js中的DOM操作
1. JS学习笔记六:js中的DOM操作 文章目录 1. JS学习笔记六:js中的DOM操作 1.1. 获取Dom节点 1.2. 元素属性的操作方式 1.3. DOM节点的创建.插入和删除 1.4. ...
- Python学习笔记六——画小猪佩奇
目录 Python学习笔记六--画小猪佩奇 画布 画笔 属性设置 操纵命令 运动命令 画笔控制命令 全局控制命令 其他命令 Python学习笔记六--画小猪佩奇 使用Python的turtle库可以绘 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
最新文章
- node 压缩模块速成
- 【NLP】听李宏毅点评GPT-3:来自猎人暗黑大陆的模型
- Linux下C程序的可扩展性.
- C++新手之详细介绍MFC
- Integrating ASP.NET AJAX with SharePoint
- jQuery:点击某元素后根据兄弟节点是否显示,控制兄弟节点的显示与否
- 酒店客房管理信息系统
- 阿里云服务器遭ddos攻击防御案例
- Camunda 动态增加会签
- 想入门自学编程,应该怎么开始?
- eclipse osgi_Eclipse通过提议的OSGi容器Kura支持M2M产品组合
- 如何在体育场创造极致观看体验
- 利用开区间覆盖的约简给出有限覆盖定理的一个新证明
- MLY翻译 -- 1.Why Machine Learning Strategy?
- js类数组转化为数组的5种方法【附思路】
- 解决IOS微信浏览器底部会出现向前向后返回按钮问题
- 【Cmake】初识CMake(一)
- v-model 原理及使用
- 计算机网络可用主机地址数量怎么算,子网数目及子网主机数计算
- 模拟银行转账取款存款