Open3d学习计划—高级篇 3(点云全局配准)
Open3D是一个开源库,支持快速开发和处理3D数据。Open3D在c++和Python中公开了一组精心选择的数据结构和算法。后端是高度优化的,并且是为并行化而设置的。
本系列学习计划有Blue同学作为发起人,主要以Open3D官方网站的教程为主进行翻译与实践的学习计划。点云PCL公众号作为免费的3D视觉,点云交流社区,期待有使用Open3D或者感兴趣的小伙伴能够加入我们的翻译计划,贡献免费交流社区,为使用Open3D提供中文的使用教程。
ICP配准和彩色点云配准都被称为局部点云配准方法,因为他们都依赖一个粗糙的对齐作为初始化。本篇教程将会展现另一种被称为全局配准的配准方法.这种系列的算法不要求一个初始化的对齐,通常会输出一个没那么精准的对齐结果,并且使用该结果作为局部配准的初始化.
可视化
该辅助函数可以将配准的源点云和目标点云一起可视化。
def draw_registration_result(source, target, transformation): source_temp = copy.deepcopy(source) target_temp = copy.deepcopy(target) source_temp.paint_uniform_color([1, 0.706, 0]) target_temp.paint_uniform_color([0, 0.651, 0.929]) source_temp.transform(transformation) o3d.visualization.draw_geometries([source_temp, target_temp])
注意:这里原来的教程里可视化函数都加了初始视角之类的,但是很多人反映这个会报错,并且官方函数里也没给出可接受的参数,所以在这里把初始视角的参数都去掉了
提取几何特征
我们降采样点云,估计法线,之后对每个点计算FPFH特征.FPFH特征是一个描述点的局部几何属性的33维的向量.在33维空间中进行最近邻查询可以返回具有近似几何结构的点.详细细节请查看 [Rasu2009].
def preprocess_point_cloud(pcd, voxel_size): print(":: Downsample with a voxel size %.3f." % voxel_size) pcd_down = pcd.voxel_down_sample(voxel_size)
radius_normal = voxel_size * 2 print(":: Estimate normal with search radius %.3f." % radius_normal) pcd_down.estimate_normals( o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30))
radius_feature = voxel_size * 5 print(":: Compute FPFH feature with search radius %.3f." % radius_feature) pcd_fpfh = o3d.registration.compute_fpfh_feature( pcd_down, o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature, max_nn=100)) return pcd_down, pcd_fpfh输入
以下代码从两个文件中读取源点云和目标点云.这一对点云使用单位矩阵作为初始矩阵之后是不对齐的.
def prepare_dataset(voxel_size): print(":: Load two point clouds and disturb initial pose.") source = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_0.pcd") target = o3d.io.read_point_cloud("../../TestData/ICP/cloud_bin_1.pcd") trans_init = np.asarray([[0.0, 0.0, 1.0, 0.0], [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]]) source.transform(trans_init) draw_registration_result(source, target, np.identity(4))
source_down, source_fpfh = preprocess_point_cloud(source, voxel_size) target_down, target_fpfh = preprocess_point_cloud(target, voxel_size) return source, target, source_down, target_down, source_fpfh, target_fpfh
voxel_size = 0.05 # means 5cm for this datasetsource, target, source_down, target_down, source_fpfh, target_fpfh = prepare_dataset(voxel_size):: Load two point clouds and disturb initial pose.
:: Downsample with a voxel size 0.050.
:: Estimate normal with search radius 0.100.
:: Compute FPFH feature with search radius 0.250.
:: Downsample with a voxel size 0.050.
:: Estimate normal with search radius 0.100.
:: Compute FPFH feature with search radius 0.250.

RANSAC
我们使用RANSAC进行全局配准.在RANSAC迭代中,我们每次从源点云中选取 ransac_n 个随机点.通过在33维FPFH特征空间中查询最邻近,可以在目标点云中找到他们的对应点.剪枝步骤需要使用快速修剪算法来提早拒绝错误匹配.
Open3d提供以下剪枝算法:
CorrespondenceCheckerBasedOnDistance检查对应的点云是否接近(也就是距离是否小于指定阈值)
CorrespondenceCheckerBasedOnEdgeLength检查从源点云和目标点云对应中分别画上两条任意边(两个顶点连成的线)是否近似.
CorrespondenceCheckerBasedOnNormal考虑的所有的对应的顶点法线的密切关系.他计算了两个法线向量的点积.使用弧度作为阈值.
只有通过剪枝步骤的匹配才用于转换,该转换将在整个点云上进行验证.核心函数是 :
registration_ransac_based_on_feature_matching.
RANSACConvergenceCriteria是里面一个十分重要的超参数.他定义了RANSAC迭代的最大次数和验证的最大次数.这两个值越大,那么结果越准确,但同时也要花费更多的时间.
我们是基于[Choi2015]提供的的经验来设置RANSAC的超参数.
def execute_global_registration(source_down, target_down, source_fpfh, target_fpfh, voxel_size): distance_threshold = voxel_size * 1.5 print(":: RANSAC registration on downsampled point clouds.") print(" Since the downsampling voxel size is %.3f," % voxel_size) print(" we use a liberal distance threshold %.3f." % distance_threshold) result = o3d.registration.registration_ransac_based_on_feature_matching( source_down, target_down, source_fpfh, target_fpfh, distance_threshold, o3d.registration.TransformationEstimationPointToPoint(False), 4, [ o3d.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9), o3d.registration.CorrespondenceCheckerBasedOnDistance( distance_threshold) ], o3d.registration.RANSACConvergenceCriteria(4000000, 500)) return resultresult_ransac = execute_global_registration(source_down, target_down, source_fpfh, target_fpfh, voxel_size)print(result_ransac)draw_registration_result(source_down, target_down, result_ransac.transformation):: RANSAC registration on downsampled point clouds.
Since the downsampling voxel size is 0.050,
we use a liberal distance threshold 0.075.
registration::RegistrationResult with fitness=6.773109e-01, inlier_rmse=3.332039e-02, and correspondence_set size of 3224
Access transformation to get result.

局部优化
由于性能原因,全局配准只能在大规模降采样的点云上执行,配准的结果不够精细,我们使用 Point-to-plane ICP 去进一步优化配准结果.
def refine_registration(source, target, source_fpfh, target_fpfh, voxel_size): distance_threshold = voxel_size * 0.4 print(":: Point-to-plane ICP registration is applied on original point") print(" clouds to refine the alignment. This time we use a strict") print(" distance threshold %.3f." % distance_threshold) result = o3d.registration.registration_icp( source, target, distance_threshold, result_ransac.transformation, o3d.registration.TransformationEstimationPointToPlane()) return result
result_icp = refine_registration(source, target, source_fpfh, target_fpfh, voxel_size)print(result_icp)draw_registration_result(source, target, result_icp.transformation):: Point-to-plane ICP registration is applied on original point
clouds to refine the alignment. This time we use a strict
distance threshold 0.020.
registration::RegistrationResult with fitness=6.210275e-01, inlier_rmse=6.565175e-03, and correspondence_set size of 123482
Access transformation to get result.

快速全局配准
由于无数的模型推荐和评估,导致基于RANSAC的全局配准需要很长的时间.
[Zhou2016] 提出了一种加速的方法,该方法可以快速的优化几乎没有对应关系的线处理权重( [Zhou2016] introduced a faster approach that quickly optimizes line process weights of few correspondences).这样在每次迭代的时候没有模型建议和评估,该方法就在计算的时候节约的大量的时间.(建议看看原论文,这个感觉翻译不好,有更好建议的欢迎留言.)
这篇教程比较了基于RANSAC的全局配准和[Zhou2016]方法的运行时间.
输入
我们使用上面全局配准的输入例子.
voxel_size = 0.05 # means 5cm for the datasetsource, target, source_down, target_down, source_fpfh, target_fpfh = \ prepare_dataset(voxel_size):: Load two point clouds and disturb initial pose.
:: Downsample with a voxel size 0.050.
:: Estimate normal with search radius 0.100.
:: Compute FPFH feature with search radius 0.250.
:: Downsample with a voxel size 0.050.
:: Estimate normal with search radius 0.100.
:: Compute FPFH feature with search radius 0.250.

基准
在下面代码中,我们将计时全局配准算法.
start = time.time()result_ransac = execute_global_registration(source_down, target_down, source_fpfh, target_fpfh, voxel_size)print("Global registration took %.3f sec.\n" % (time.time() - start))print(result_ransac)draw_registration_result(source_down, target_down, result_ransac.transformation):: RANSAC registration on downsampled point clouds.
Since the downsampling voxel size is 0.050,
we use a liberal distance threshold 0.075.
Global registration took 0.085 sec.
registration::RegistrationResult with fitness=6.760504e-01, inlier_rmse=2.596653e-02, and correspondence_set size of 3218
Access transformation to get result.

快速全局配准
我们采用和基准相同的输入,下面的代码调用了了[Zhou2016]的实现.
def execute_fast_global_registration(source_down, target_down, source_fpfh, target_fpfh, voxel_size): distance_threshold = voxel_size * 0.5 print(":: Apply fast global registration with distance threshold %.3f" \ % distance_threshold) result = o3d.registration.registration_fast_based_on_feature_matching( source_down, target_down, source_fpfh, target_fpfh, o3d.registration.FastGlobalRegistrationOption( maximum_correspondence_distance=distance_threshold)) return resultstart = time.time()result_fast = execute_fast_global_registration(source_down, target_down, source_fpfh, target_fpfh, voxel_size)print("Fast global registration took %.3f sec.\n" % (time.time() - start))print(result_fast)draw_registration_result(source_down, target_down, result_fast.transformation):: Apply fast global registration with distance threshold 0.025
Fast global registration took 0.128 sec.
registration::RegistrationResult with fitness=5.054622e-01, inlier_rmse=1.743545e-02, and correspondence_set size of 2406
Access transformation to get result.

经过适当的配置,快速全局配准的精度甚至可以和ICP相媲美.更多实验结果请参阅[Zhou2016].
资源
三维点云论文及相关应用分享
【点云论文速读】基于激光雷达的里程计及3D点云地图中的定位方法
3D-MiniNet: 从点云中学习2D表示以实现快速有效的3D LIDAR语义分割(2020)
PCL中outofcore模块---基于核外八叉树的大规模点云的显示
更多文章可查看:点云学习历史文章大汇总
SLAM及AR相关分享
