Open3D使用 下采样(Voxel Downsample) 和 欧式聚类分割(Cluster DBSCAN) 优化点云的去噪效果,尤其是在面对大量噪声或孤立簇时非常有效。
- Voxel 下采样:降低点云密度,加速后续处理。
- DBSCAN 聚类分割:识别并移除孤立的小点簇(即噪声簇)。
✅ 方法一:Voxel 下采样(Voxel Downsample)
📌 原理说明:
通过将空间划分为体素(voxel),每个体素内只保留一个代表点(如中心点或平均点),从而达到降采样和初步去噪的目的。
# 设置 voxel_size,根据你的点云密度调整(单位与点云一致,例如米或毫米)
voxel_size = 0.05 # 例如:5厘米
# 执行 Voxel 下采样
downsampled_pcd = filtered_pcd.voxel_down_sample(voxel_size=voxel_size)
print(f"原始点数量: {len(filtered_pcd.points)}")
print(f"下采样后点数量: {len(downsampled_pcd.points)}")
✅ 方法二:欧式聚类分割(Cluster DBSCAN)
📌 原理说明:
DBSCAN 是一种基于密度的聚类算法,能识别出密集区域的点簇,并将稀疏的、孤立的小簇标记为噪声。
# 使用欧式聚类 DBSCAN 分割
with o3d.utility.VerbosityContextManager(o3d.utility.VerbosityLevel.Debug) as cm:
labels = np.array(
downsampled_pcd.cluster_dbscan(eps=0.3, min_points=10, print_progress=True)
)
# eps: 搜索邻域半径(单位与点云一致)
# min_points: 成为一个簇所需的最小点数
# 显示聚类结果
max_label = labels.max()
print(f"找到 {max_label + 1} 个簇")
# 可选:给不同簇上色可视化
colors = plt.get_cmap("tab20")(labels / (max_label if max_label > 0 else 1))
colors[labels < 0] = 0 # 噪声点涂成黑色
downsampled_pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
# 可视化聚类结果
o3d.visualization.draw_geometries([downsampled_pcd])
✅ 方法三:结合使用 —— 移除孤立小簇(更彻底去噪)
如果你的目标是去除所有不属于大簇的点(即噪声),可以按如下方式操作:
# 将簇标签转换为整型数组
unique_labels, counts = np.unique(labels, return_counts=True)
# 设置最小簇大小阈值(例如:50个点)
min_cluster_size = 50
# 获取满足条件的簇标签
valid_clusters = unique_labels[counts >= min_cluster_size]
# 构建 mask:仅保留属于“大簇”的点
mask = np.isin(labels, valid_clusters)
# 应用 mask 到点云
cleaned_points = np.asarray(downsampled_pcd.points)[mask]
cleaned_colors = np.asarray(downsampled_pcd.colors)[mask] if downsampled_pcd.has_colors() else None
# 创建新的干净点云
final_cleaned_pcd = o3d.geometry.PointCloud()
final_cleaned_pcd.points = o3d.utility.Vector3dVector(cleaned_points)
if cleaned_colors is not None:
final_cleaned_pcd.colors = o3d.utility.Vector3dVector(cleaned_colors)
# 可视化最终结果
o3d.visualization.draw_geometries([final_cleaned_pcd])
🧠 总结流程建议:
步骤 | 目的 |
---|---|
voxel_down_sample() | 减少点数量,提高效率,初步去噪 |
cluster_dbscan() | 识别点簇,区分主要结构与孤立噪声 |
mask + filter | 移除小簇噪声,保留主要结构 |
📦 完整函数封装示例(可直接调用)
def remove_noise_with_clustering(pcd, voxel_size=0.05, eps=0.3, min_points=10, min_cluster_size=50):
# Step 1: 下采样
downsampled_pcd = pcd.voxel_down_sample(voxel_size=voxel_size)
# Step 2: DBSCAN 聚类
labels = np.array(downsampled_pcd.cluster_dbscan(eps=eps, min_points=min_points, print_progress=False))
# Step 3: 过滤小簇
unique_labels, counts = np.unique(labels, return_counts=True)
valid_clusters = unique_labels[counts >= min_cluster_size]
mask = np.isin(labels, valid_clusters)
# Step 4: 提取干净点云
cleaned_points = np.asarray(downsampled_pcd.points)[mask]
cleaned_colors = np.asarray(downsampled_pcd.colors)[mask] if downsampled_pcd.has_colors() else None
cleaned_pcd = o3d.geometry.PointCloud()
cleaned_pcd.points = o3d.utility.Vector3dVector(cleaned_points)
if cleaned_colors is not None:
cleaned_pcd.colors = o3d.utility.Vector3dVector(cleaned_colors)
return cleaned_pcd
✅ 使用示例:
import open3d as o3d
import numpy as np
def remove_noise_with_clustering(pcd, voxel_size=0.05, eps=0.3, min_points=10, min_cluster_size=50):
# Step 1: 下采样
downsampled_pcd = pcd.voxel_down_sample(voxel_size=voxel_size)
# Step 2: DBSCAN 聚类
labels = np.array(downsampled_pcd.cluster_dbscan(eps=eps, min_points=min_points, print_progress=False))
# Step 3: 过滤小簇
unique_labels, counts = np.unique(labels, return_counts=True)
valid_clusters = unique_labels[counts >= min_cluster_size]
mask = np.isin(labels, valid_clusters)
# Step 4: 提取干净点云
cleaned_points = np.asarray(downsampled_pcd.points)[mask]
cleaned_colors = np.asarray(downsampled_pcd.colors)[mask] if downsampled_pcd.has_colors() else None
cleaned_pcd = o3d.geometry.PointCloud()
cleaned_pcd.points = o3d.utility.Vector3dVector(cleaned_points)
if cleaned_colors is not None:
cleaned_pcd.colors = o3d.utility.Vector3dVector(cleaned_colors)
return cleaned_pcd
if __name__ == "__main__":
# 加载点云
file_path = "demo.ply"
pcd = o3d.io.read_point_cloud(file_path)
# 将点云数据转化为numpy数组
points = np.asarray(pcd.points)
colors = np.asarray(pcd.colors) # 获取颜色数据
# 筛选Z轴小于设定阈值的点及其对应的颜色
mask = (points[:, 2] > 500) & (points[:, 2] < 3000)
filtered_points = points[mask]
filtered_colors = colors[mask]
# 交换X轴和Y轴的数据
filtered_points[:, [0, 1]] = filtered_points[:, [1, 0]]
# 创建新的点云对象并赋值筛选后的点和颜色
filtered_pcd = o3d.geometry.PointCloud()
filtered_pcd.points = o3d.utility.Vector3dVector(filtered_points)
filtered_pcd.colors = o3d.utility.Vector3dVector(filtered_colors)
cleaned_pcd = remove_noise_with_clustering(filtered_pcd, voxel_size=0.05*1000, eps=0.3*1000, min_points=10, min_cluster_size=50)
o3d.visualization.draw_geometries([filtered_pcd],width=800, height=500)
o3d.visualization.draw_geometries([cleaned_pcd],width=800, height=500)
原点云:
Voxel 下采样+欧式聚类分割(Cluster DBSCAN)