Python实现点云随机一致性(RANSAC)配准——粗配准
今天我们来介绍精配准前的重要步骤——粗配准。大家使用前面ICP和各种变式时,有一个使用单位矩阵代替的部分。对于姿态一致,距离较近的两点云,使用单位矩阵一点问题都没有,可是姿态不对应,哪怕离的很近也会出现配准问题。那么今天我们就来解决这个问题。
RANSAC(Random Sample Consensus)是一种迭代式鲁棒估计算法,专门用于在强噪声或高离群点比例的数据中估计模型参数。它在点云粗配准、图像拼接、3D 重建等任务中扮演着关键角色。RANSAC算法共可以分为七步(难道是七步诗?),表示如下:
1. 特征提取
分别在源点云和目标点云中检测关键点,并计算每个点的局部特征描述符(常用 FPFH、SHOT 等)。
2. 初始匹配
基于特征距离(如欧氏距离或余弦距离)建立两组特征之间的初始对应关系,结果往往包含大量误匹配。
3. 随机采样
在全部匹配对中随机抽取最小子集,该子集的大小需恰好能唯一确定一个刚体变换(通常 3 对匹配点即可求解 3D 刚体变换)。
4. 模型估计
利用当前子集计算刚体变换矩阵(旋转 R 与平移 t)。
5. 内点检验
将剩余所有匹配对按此变换投影,计算投影误差。误差小于给定阈值的匹配被视为内点。
6. 模型评估
统计当前模型的内点数目,并与历史最优模型比较。若内点更多,则更新最优模型及其参数。
7. 迭代收敛
重复步骤 3–6,直至达到预设迭代次数或找到满足置信度的模型。迭代次数可根据离群点比例与期望成功率自适应确定。最终输出内点最多的刚体变换,即为源点云到目标点云的配准结果。
本次实验为了和前面的ICP对应上,依旧使用的是我们的老朋友——兔砸,显示如下:
一、RANSAC算法程序
import copy
import open3d as o3d
import numpy as np# -------------传入点云数据,计算FPFH------------
def FPFH_Compute(pcd):radius_normal = 0.01 # kdtree参数,用于估计法线的半径,pcd.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30))# 估计法线的1个参数,使用混合型的kdtree,半径内取最多30个邻居radius_feature = 0.02 # kdtree参数,用于估计FPFH特征的半径pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(pcd,o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature,max_nn=100)) # 计算FPFH特征,搜索方法kdtreereturn pcd_fpfh # 返回FPFH特征# ----------------RANSAC配准--------------------
def execute_global_registration(source, target, source_fpfh,target_fpfh): # 传入两个点云和点云的特征distance_threshold = 10 # 设定距离阈值# 2个点云,两个点云的特征,距离阈值,一个函数,4,# 一个list[0.9的两个对应点的线段长度阈值,两个点的距离阈值],# 一个函数设定最大迭代次数和最大验证次数result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(source, target, source_fpfh, target_fpfh, True, distance_threshold,o3d.pipelines.registration.TransformationEstimationPointToPoint(False),4, [o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(distance_threshold)], o3d.pipelines.registration.RANSACConvergenceCriteria(4000000, 500))return resultif __name__ == "__main__":# -------------------读取点云数据--------------------source = o3d.io.read_point_cloud("E:/CSDN/规则点云/bunny.pcd")# 原始点云x轴平移0.1,y轴平移0.15,z轴平移0.2,然后加上噪声,最后新的点云作为目标点云# 平移target = copy.deepcopy(source)points = np.asarray(target.points)t = np.array([0.1, 0.15, 0.2])target = target.translate(t, relative=True) # relative=True 表示“增量”平移# 加噪声mu, sigma = 0, 0.001 # 均值和标准差根据密度调节random_numbers = np.random.normal(mu, sigma, size=(points.shape[0], 3))points += random_numberstarget.points = o3d.utility.Vector3dVector(points)target.paint_uniform_color([0, 1, 0]) # 给目标点云赋予单色# --------------------计算法向量---------------------source.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.01, max_nn=30))target.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.01, max_nn=30))# 复制点云,防止更改source1 = copy.deepcopy(source)target1 = copy.deepcopy(target)# -----------------计算的FPFH---------------source_fpfh = FPFH_Compute(source1)target_fpfh = FPFH_Compute(target1)# ---------------调用RANSAC执行配准------------result_ransac = execute_global_registration(source1, target1,source_fpfh, target_fpfh)print("配准后信息:", result_ransac) # 输出RANSAC配准信息print("两块点云之间的配准矩阵:", result_ransac.transformation)source2 = copy.deepcopy(source1)source2.transform(result_ransac.transformation)o3d.visualization.draw_geometries([source1, target1],window_name="两点云初始位置",width=1200, height=800,left=50, top=50)o3d.visualization.draw_geometries([source2, target1],window_name="配准后两点云初始位置",width=1200, height=800,left=50, top=50)
二、RANSAC算法结果
从上面可以看出,经过RANSAC粗配准以后,点云可以很好的对齐。所以说明什么,有时候精配准和粗配准是相对的,不一定需要精配准!
还有一点,上面的程序运行起来不快,因为是全局寻找特征点并且配准,所以速度就下来了。如果同学们想加快速度,可以使用前面咱们介绍的点云各种滤波,最好使用减少点云个数的。
就酱,下次见^-^