如何用Open3D比较不同点数的点云?含FPFH特征后续处理方案
点云相似性/差异性对比方案(基于Open3D)
一、基于FPFH特征描述子的完整对比流程
针对两款相似汽车点云(或LiDAR扫描与CAD模型点云),通过FPFH特征实现相似/相异区域定位的步骤如下:
1. 预处理(统一尺度与密度)
先对两点云做下采样对齐密度,再估计法线(FPFH特征依赖法线信息):
import open3d as o3d # 加载目标点云 pcd_lidar = o3d.io.read_point_cloud("car_lidar.pcd") pcd_cad = o3d.io.read_point_cloud("car_cad.pcd") # 体素下采样,统一点云密度 voxel_size = 0.05 pcd_lidar_down = pcd_lidar.voxel_down_sample(voxel_size) pcd_cad_down = pcd_cad.voxel_down_sample(voxel_size) # 估计点云法线 radius_normal = voxel_size * 2 pcd_lidar_down.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30)) pcd_cad_down.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30))
2. 提取FPFH特征描述子
使用Open3D内置函数计算两点云的FPFH特征:
radius_feature = voxel_size * 5 fpfh_lidar = o3d.pipelines.registration.compute_fpfh_feature( pcd_lidar_down, o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature, max_nn=100) ) fpfh_cad = o3d.pipelines.registration.compute_fpfh_feature( pcd_cad_down, o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature, max_nn=100) )
3. 特征匹配与区域定位
通过KDTree做特征近邻匹配,筛选相似/相异点并可视化:
# 构建KDTree用于特征匹配 kdtree = o3d.geometry.KDTreeFlann(fpfh_cad.data.T) # 匹配阈值(根据点云尺度调整) match_threshold = 0.2 similar_lidar = [] similar_cad = [] dissimilar_lidar = [] for i in range(len(pcd_lidar_down.points)): # 查找当前点特征的最近邻 [k, idx, dist] = kdtree.search_knn_vector_xd(fpfh_lidar.data[:, i], 1) if dist[0] < match_threshold: similar_lidar.append(pcd_lidar_down.points[i]) similar_cad.append(pcd_cad_down.points[idx[0]]) else: dissimilar_lidar.append(pcd_lidar_down.points[i]) # 转换为点云对象并标记颜色 similar_pcd_lidar = o3d.geometry.PointCloud() similar_pcd_lidar.points = o3d.utility.Vector3dVector(similar_lidar) similar_pcd_lidar.paint_uniform_color([0, 1, 0]) # 绿色标记相似区域 dissimilar_pcd_lidar = o3d.geometry.PointCloud() dissimilar_pcd_lidar.points = o3d.utility.Vector3dVector(dissimilar_lidar) dissimilar_pcd_lidar.paint_uniform_color([1, 0, 0]) # 红色标记相异区域 # 可视化结果 o3d.visualization.draw_geometries([similar_pcd_lidar, dissimilar_pcd_lidar])
二、非特征提取类的点云对比方法
1. ICP配准后距离差异分析
先通过ICP将两点云配准到同一坐标系,再计算点到点的距离,超出阈值的点即为相异区域:
# 粗配准(提供ICP初始位姿) result_ransac = o3d.pipelines.registration.registration_ransac_based_on_feature_matching( pcd_lidar_down, pcd_cad_down, fpfh_lidar, fpfh_cad, True, voxel_size * 1.5, o3d.pipelines.registration.TransformationEstimationPointToPoint(False), 3, [ o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9), o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(voxel_size * 1.5) ], o3d.pipelines.registration.RANSACConvergenceCriteria(100000, 0.999) ) # ICP精配准 result_icp = o3d.pipelines.registration.registration_icp( pcd_lidar_down, pcd_cad_down, voxel_size * 0.5, result_ransac.transformation, o3d.pipelines.registration.TransformationEstimationPointToPoint() ) # 应用配准变换 pcd_lidar_registered = pcd_lidar_down.transform(result_icp.transformation) # 计算点云间距离 distances = pcd_lidar_registered.compute_point_cloud_distance(pcd_cad_down) # 筛选相异点 diff_threshold = 0.1 dissimilar_points = [] for i in range(len(distances)): if distances[i] > diff_threshold: dissimilar_points.append(pcd_lidar_registered.points[i]) diff_pcd = o3d.geometry.PointCloud() diff_pcd.points = o3d.utility.Vector3dVector(dissimilar_points) diff_pcd.paint_uniform_color([1, 0, 0]) # 可视化配准结果与差异区域 o3d.visualization.draw_geometries([pcd_lidar_registered, pcd_cad_down, diff_pcd])
2. 体素网格差异检测
将两点云体素化,统计仅在单一云存在的体素,对应相异区域:
# 创建体素网格 voxel_grid_lidar = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd_lidar, voxel_size=0.1) voxel_grid_cad = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd_cad, voxel_size=0.1) # 获取体素坐标集合 voxels_lidar = set(voxel.grid_index.tolist() for voxel in voxel_grid_lidar.get_voxels()) voxels_cad = set(voxel.grid_index.tolist() for voxel in voxel_grid_cad.get_voxels()) # 计算差异体素 unique_voxels_lidar = voxels_lidar - voxels_cad unique_voxels_cad = voxels_cad - voxels_lidar # 转换差异体素为点云 def voxels_to_pcd(voxel_indices, voxel_size): points = [] for idx in voxel_indices: center = (o3d.utility.Vector3d(idx) + o3d.utility.Vector3d([0.5, 0.5, 0.5])) * voxel_size points.append(center) pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(points) return pcd diff_pcd_lidar = voxels_to_pcd(unique_voxels_lidar, 0.1) diff_pcd_lidar.paint_uniform_color([1, 0, 0]) diff_pcd_cad = voxels_to_pcd(unique_voxels_cad, 0.1) diff_pcd_cad.paint_uniform_color([0, 0, 1]) o3d.visualization.draw_geometries([diff_pcd_lidar, diff_pcd_cad])
3. 基于RANSAC的几何基元对比
检测两点云中的共性几何基元(如平面、圆柱),未匹配的基元对应相异区域:
# 从LiDAR点云中提取平面(车身主体) plane_model, inliers_lidar = pcd_lidar.segment_plane(distance_threshold=0.03, ransac_n=3, num_iterations=1000) plane_pcd_lidar = pcd_lidar.select_by_index(inliers_lidar) plane_pcd_lidar.paint_uniform_color([0, 1, 0]) non_plane_pcd_lidar = pcd_lidar.select_by_index(inliers_lidar, invert=True) non_plane_pcd_lidar.paint_uniform_color([1, 0, 0]) # 从CAD点云中提取平面 plane_model, inliers_cad = pcd_cad.segment_plane(distance_threshold=0.03, ransac_n=3, num_iterations=1000) plane_pcd_cad = pcd_cad.select_by_index(inliers_cad) plane_pcd_cad.paint_uniform_color([0, 1, 0]) non_plane_pcd_cad = pcd_cad.select_by_index(inliers_cad, invert=True) non_plane_pcd_cad.paint_uniform_color([1, 0, 0]) # 可视化几何基元与差异区域 o3d.visualization.draw_geometries([plane_pcd_lidar, non_plane_pcd_lidar]) o3d.visualization.draw_geometries([plane_pcd_cad, non_plane_pcd_cad])
内容的提问来源于stack exchange,提问作者Ayush Singh
相关产品推荐
相关产品推荐

