You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:26:01