点云重建几何形状是一项常见的任务,通常由建模工程师在具有cad功能的软件中手动建模几何形状来完成。基于全自动几何提取的最先进的工作流程受到点云密度和内存限制,需要用 户进行预处理和后处理。本文提出一种交互式、用户驱动、特征辅助的从任意大小的点云进行交互式提取边缘的框架。
本文目前实现了所有功能,下面看下程序运行结果。
交互式选择种子点:
搜索临近区域:
平面分割:
交点提取:
双向生长:
终止生长:
碰到多面角点停止生长
下面,附上本人的代码,为python实现。
import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import pairwise_distances
class PointCloudProcessing:
def __init__(self, point_cloud_path):
self.point_cloud = o3d.io.read_point_cloud(point_cloud_path)
self.seed_point = None
self.picked_indices = None
# 交互式选择点
def pick_points(self):
print("")
print(
"1) 请至少选择三个对应点,使用 [Shift + 左键单击]"
)
print(" 按 [Shift + 右键单击] 取消点的选择")
print("2) 选择完点后,按 'Q' 键关闭窗口")
vis = o3d.visualization.VisualizerWithEditing()
vis.create_window()
vis.add_geometry(self.point_cloud)
vis.run() # user picks points
vis.destroy_window()
print("")
picked_indices = np.asarray(vis.get_picked_points())
if len(picked_indices) > 0:
# Convert Python list to NumPy array and update the colors
self.picked_indices = np.asarray(picked_indices)
self.point_cloud.colors[self.picked_indices] = [1, 0, 0]
self.seed_point = np.array(self.point_cloud.points)[picked_indices]
print(self.seed_point)
else:
print("No points picked.")
def extract_local_points(self,search_radius):
[k, idx, _] = pcd_tree.search_radius_vector_3d(self.point_cloud.points[self.picked_indices],search_radius)
np.asarray(self.point_cloud.colors)[idx[1:], :] = [0, 1, 0]
print("Visualize the point cloud.")
o3d.visualization.draw_geometries([self.point_cloud])
local_points = np.asarray(self.point_cloud.points)[idx[1:], :]
local_points = np.append(local_points, self.seed_point,axis=0)
local_points_pc = o3d.geometry.PointCloud()
local_points_pc.points = o3d.utility.Vector3dVector(local_points)
# o3d.visualization.draw_geometries([local_points_pc])
return local_points_pc
def fit_planes(self, pcd):
segment_models = {} # 用于存储平面分割模型的字典
segments = {} # 存储分割出的点云片段的字典
max_plane_idx = 2 # 最大平面数量
rest = pcd # 初始点云,用于迭代分割
d_threshold = 0.01 # DBSCAN聚类时的距离阈值
# 遍历每个平面进行分割
for i in range(max_plane_idx):
# 使用RANSAC算法进行平面分割
colors = plt.get_cmap("tab20")(i)
segment_models[i], inliers = rest.segment_plane(distance_threshold=2, ransac_n=5, num_iterations=1000)
# 选择平面内的点云
segments[i] = rest.select_by_index(inliers)
# 使用DBSCAN进行聚类,获取每个聚类的标签
labels = np.array(segments[i].cluster_dbscan(eps=d_threshold * 10, min_points=10))
# 计算每个聚类的点数
candidates = [len(np.where(labels == j)[0]) for j in np.unique(labels)]
# 选择点数最多的聚类作为最好的拟合面
best_candidate = int(np.unique(labels)[np.where(candidates == np.max(candidates))[0]])
print("最好的拟合面是:", best_candidate)
# 更新剩余点云,去除当前平面内的点和非最好拟合面的点
rest = rest.select_by_index(inliers, invert=True) + segments[i].select_by_index(
list(np.where(labels != best_candidate)[0]))
# 更新当前平面为最好拟合面的点
segments[i] = segments[i].select_by_index(list(np.where(labels == best_candidate)[0]))
segments[i].paint_uniform_color(list(colors[:3]))
print("pass", i + 1, "/", max_plane_idx, "done.")
# 可视化平面分割效果
o3d.visualization.draw_geometries([segments[i] for i in range(max_plane_idx)])
# 将 Open3D 的点云数据转换为 NumPy 数组
points_i = np.asarray(segments[0].points)
points_j = np.asarray(segments[1].points)
# 计算欧式距离矩阵
distances_matrix = pairwise_distances(points_i, points_j)
# 找到距离小于1的点的索引
row_indices, col_indices = np.where(distances_matrix < 6)
# 将满足条件的点添加到最近点列表
closest_points = np.concatenate([points_i[row_indices], points_j[col_indices]])
# 将 closest_points 转换为 Open3D 点云对象
point_cloud = o3d.geometry.PointCloud()
point_cloud.points = o3d.utility.Vector3dVector(closest_points)
o3d.io.write_point_cloud("point_cloud.pcd",point_cloud)
return closest_points
if __name__ == "__main__":
point_cloud_path = r'C:\Users\zhaojunzhe\Desktop\pointcloud\bottom_staircase.pcd'
# 声明类
processor = PointCloudProcessing(point_cloud_path)
# 按shift+左键选取感兴趣点
processor.pick_points()
search_radius = 80 #设置搜索半径
pcd_tree = o3d.geometry.KDTreeFlann(processor.point_cloud)
# 提取感兴趣范围内点云数据
local_pcd = processor.extract_local_points(search_radius)
o3d.io.write_point_cloud("local_pcd.pcd",local_pcd)
# 平面分割
plane_params = processor.fit_planes(local_pcd)