当前位置:   article > 正文

传统方法分割三维点云——几何分割_o3d.geometry.trianglemesh.create_cylinder设置

o3d.geometry.trianglemesh.create_cylinder设置

基于几何特征的分割方法


        基于几何特征的分割方法是最早被提出和广泛应用的方法之一。它通过计算点云中的几何属性,如法向量、曲率等,来进行分割。其中,最常用的方法包括:


1.1 基干法向量的分割方法

        该方法假设同一区域的法向量相似,通过计算点云中每个点的法向量,将相似法向量的点划分为同一区域。优点是简单易实现,但对于复杂场景或存在噪声的点云效果较差。


1.2 基于曲率的分割方法

        该方法通过计算点云中每个点的曲率,将曲率变化较大的点划分为不同区域。优点是对于不同形状的物体有较好的分割效果,但对于噪声点较敏感。

        传统方法对于噪声比较多的点云数据处理起来都较为复杂的,而在降噪过程中由于点云的无序性和无规则性,对于去噪后的结果无法保证,因此,传统方法分割点云的困难重重。

        本文从几何分割的角度出发,充分利用物体几何特征,对于矩形物体进行了几何分割,以下是算法过程:

最后得到的数据分割情况如下,可以分割出三个面的点云数据,正上方的点云数据粉色块部分,是该程序也适用于四个面的矩形点云数据。

结果如下:

代码附上:

  1. import open3d as o3d
  2. import numpy as np
  3. import pyransac3d as pyrsc
  4. def line_fit(pcd,classified_points):
  5. """
  6. 直线拟合
  7. """
  8. for i, points in classified_points.items():
  9. # 创建点云对象
  10. point_cloud = o3d.geometry.PointCloud()
  11. # 将numpy点云数据赋值给点云对象
  12. point_cloud.points = o3d.utility.Vector3dVector(points)
  13. print("->正在直线拟合...")
  14. mesh_cylinders = []
  15. line = pyrsc.Line()
  16. # A:直线的斜率,B:直线的截距,inliers:内点索引,
  17. # thresh:内点的距离阈值
  18. # maxIteration:RANSAC算法的拟合次数
  19. A, B, inliers = line.fit(points, thresh=500, maxIteration=500)
  20. print('直线的三维斜率为:', A)
  21. print('直线的截距为:', B)
  22. R = pyrsc.get_rotationMatrix_from_vectors([0, 0, 1], A)
  23. ransac_line = point_cloud.select_by_index(inliers)
  24. mesh_cylinder = o3d.geometry.TriangleMesh.create_cylinder(radius=20, height=10000)
  25. mesh_cylinder.compute_vertex_normals()
  26. mesh_cylinder.paint_uniform_color([1, 0, 0])
  27. mesh_cylinder = mesh_cylinder.rotate(R, center=[0, 0, 0])
  28. mesh_cylinder = mesh_cylinder.translate((B[0], B[1], B[2]))
  29. o3d.visualization.draw_geometries([point_cloud, ransac_line, mesh_cylinder])
  30. def classifiction_points(pcd,wall_corners):
  31. '''
  32. 根据四面墙的四个角点,将四面墙的点云数据分为四块。
  33. '''
  34. # 计算中心点
  35. center_point = np.mean(wall_corners, axis=0)
  36. # 将每个角点向中心点移动1cm
  37. moved_corners = []
  38. for corner in wall_corners:
  39. direction_to_center = center_point - corner
  40. # 单位化方向向量
  41. unit_direction = direction_to_center / np.linalg.norm(direction_to_center)
  42. # 移动1cm
  43. moved_corner = corner + unit_direction * 1
  44. moved_corners.append(moved_corner)
  45. moved_corners = np.array(moved_corners)
  46. # points_cloud是一个Numpy数组,包含点云数据
  47. points_cloud = np.array(pcd.points)
  48. # 分类点云数据
  49. classified_points = {i: [] for i in range(4)}
  50. for point in points_cloud:
  51. # 对每个点,找到最近的延长线
  52. min_distance = float('inf')
  53. min_index = -1
  54. for i in range(4):
  55. corner1 = moved_corners[i]
  56. corner2 = moved_corners[(i + 1) % 4]
  57. # 计算点到延长线的距离
  58. distance = np.linalg.norm(np.cross(corner2 - corner1, corner1 - point)) / np.linalg.norm(corner2 - corner1)
  59. if distance < min_distance:
  60. min_distance = distance
  61. min_index = i
  62. # 添加到对应分类中
  63. classified_points[min_index].append(point)
  64. # 将分类转换为Numpy数组
  65. for i in range(4):
  66. classified_points[i] = np.array(classified_points[i])
  67. # 定义每个分类的颜色 (r, g, b)
  68. colors = [
  69. [1, 0, 0], # 红色
  70. [0, 1, 0], # 绿色
  71. [0, 0, 1], # 蓝色
  72. [1, 1, 0] # 黄色
  73. ]
  74. # 创建一个Open3D的点云对象列表
  75. point_cloud_objects = []
  76. for i, points in classified_points.items():
  77. # 创建点云对象
  78. point_cloud = o3d.geometry.PointCloud()
  79. # 将numpy点云数据赋值给点云对象
  80. point_cloud.points = o3d.utility.Vector3dVector(points)
  81. # 为点云赋予颜色
  82. point_cloud.paint_uniform_color(colors[i])
  83. # 将点云对象添加到列表中
  84. point_cloud_objects.append(point_cloud)
  85. # 可视化所有分类的点云
  86. o3d.visualization.draw_geometries(point_cloud_objects)
  87. return classified_points
  88. def find_farthest_points_in_quadrants(points, center):
  89. # 初始化四个象限的最远点信息
  90. quadrants = {
  91. 'upper_left': (-np.inf, None),
  92. 'upper_right': (-np.inf, None),
  93. 'lower_right': (-np.inf, None),
  94. 'lower_left': (-np.inf, None)
  95. }
  96. for point in points:
  97. # 计算点到中心点的角度和距离
  98. angle = np.arctan2(point[1] - center[1], point[0] - center[0])
  99. distance = np.linalg.norm(point - center)
  100. # 确定点所在的象限,并更新该象限的最远点
  101. if -np.pi <= angle < -np.pi / 2:
  102. if distance > quadrants['lower_left'][0]:
  103. quadrants['lower_left'] = (distance, point)
  104. elif -np.pi / 2 <= angle < 0:
  105. if distance > quadrants['lower_right'][0]:
  106. quadrants['lower_right'] = (distance, point)
  107. elif 0 <= angle < np.pi / 2:
  108. if distance > quadrants['upper_right'][0]:
  109. quadrants['upper_right'] = (distance, point)
  110. elif np.pi / 2 <= angle <= np.pi:
  111. if distance > quadrants['upper_left'][0]:
  112. quadrants['upper_left'] = (distance, point)
  113. # 从四个象限中提取最远的点
  114. farthest_points = [quadrants[q][1] for q in quadrants]
  115. return np.array(farthest_points)
  116. def visualize_points_and_lines(pcd_clap,points, lines):
  117. # 从点集创建点云对象
  118. pcd = o3d.geometry.PointCloud()
  119. pcd.points = o3d.utility.Vector3dVector(points)
  120. # 从点集和线集创建线集对象
  121. line_set = o3d.geometry.LineSet(
  122. points=o3d.utility.Vector3dVector(points),
  123. lines=o3d.utility.Vector2iVector(lines)
  124. )
  125. # 可视化点云和线集
  126. o3d.visualization.draw_geometries([pcd_clap,pcd, line_set])
  127. class Ransac_fit():
  128. def __init__(self,pointcloud):
  129. """
  130. 初始化参数
  131. """
  132. self.pcd = pointcloud # 输入点云
  133. self.NPt = 0 # 输入点云
  134. self.NpPoints = np.asarray(self.pcd.points) # 点云numpy数组
  135. self.min_height = 0 # 点云最小高度
  136. self.max_height = 0 # 点云最大高度
  137. def get_min_max_horizontal_height(self):
  138. """
  139. 求得点云的最低和最高的Z值
  140. """
  141. obb = self.pcd.get_axis_aligned_bounding_box()
  142. # 从包围盒坐标中获取最高和最低水平高度
  143. obb_points = np.asarray(obb.get_box_points())
  144. self.min_height = np.min(obb_points[:, 2]) # 最低水平高度
  145. self.max_height = np.max(obb_points[:, 2]) # 最高水平高度
  146. def clap_top_bottom(self):
  147. """
  148. 沿着z轴拍平点云数据
  149. """
  150. pointsID_Window = []
  151. # 遍历所有点,并记录符合条件的点的索引
  152. for idx, point in enumerate(self.NpPoints):
  153. if self.min_height <= point[2] <= self.max_height:
  154. point[2] = 0
  155. pointsID_Window.append(idx)
  156. pcd_clap = self.pcd.select_by_index(pointsID_Window).voxel_down_sample(3)
  157. return pcd_clap
  158. def plane_fit(self,distance_threshold,ransac_n,num_iterations):
  159. """
  160. 拍平平面到原来的平面
  161. """
  162. distance_threshold = 5 if distance_threshold == 0 else distance_threshold
  163. ransac_n = 5 if ransac_n == 0 else ransac_n
  164. num_iterations = 1000 if num_iterations == 0 else num_iterations
  165. segment_models, inliers = self.pcd.segment_plane(distance_threshold, ransac_n, num_iterations)
  166. # 获取平面的法向量并归一化
  167. normal_vector = np.array(segment_models[:3])
  168. normal_vector /= np.linalg.norm(normal_vector)
  169. # 计算每个点在法向量方向上的分量
  170. dot_products = np.dot(self.NpPoints, normal_vector)
  171. # 计算投影点
  172. projected_points = self.NpPoints - 0.8 * dot_products[:, np.newaxis] * normal_vector
  173. # 创建新的PointCloud对象
  174. projected_pcd = o3d.geometry.PointCloud()
  175. projected_pcd.points = o3d.utility.Vector3dVector(projected_points)
  176. return projected_pcd
  177. def main():
  178. # 确保文件路径是正确的
  179. file_path = r"C:\Users\zhaojunzhe\Desktop\点云数据存储\钢梁.pts"
  180. # 读取点云数据
  181. pcd = o3d.io.read_point_cloud(file_path)
  182. o3d.visualization.draw_geometries([pcd])
  183. # 点云滤波
  184. cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
  185. pcd = pcd.select_by_index(ind)
  186. # 创建拍平点云数据去拟合
  187. RANSAC = Ransac_fit(pcd)
  188. RANSAC.get_min_max_horizontal_height()
  189. clap_pointcloud = RANSAC.plane_fit(distance_threshold = 0,ransac_n = 0,num_iterations = 0)
  190. # 给点云上色
  191. colored_pcd = clap_pointcloud.paint_uniform_color([0, 0, 1])
  192. # 给定点云数据
  193. points = np.array(colored_pcd.points)
  194. # 计算点云数据的中心
  195. point_cloud_center = np.mean(points,axis = 0)
  196. # 找到相对于中心点在四个象限中距离最远的四个点
  197. farthest_points = find_farthest_points_in_quadrants(points[:, :3], point_cloud_center[:3])
  198. # 点云数据分类
  199. classified_points = classifiction_points(colored_pcd,farthest_points)
  200. # 最后三部分未来得及根据分割后的物体修改完善,请读者自行完善修改!
  201. # RANSAC线拟合
  202. line_fit(colored_pcd,classified_points)
  203. # 定义连接最远点的线集
  204. lines = [[i, (i + 1) % 4] for i in range(4)]
  205. # 可视化中心点、最远点和连接线
  206. visualize_points_and_lines(colored_pcd,farthest_points, lines)
  207. if __name__ == '__main__':
  208. main()

数据已上传到百度网盘,链接地址如下,自取。

链接:https://pan.baidu.com/s/1qqX-J3WTecIbWPE6vvvCfw?pwd=142t 
提取码:142t 
--来自百度网盘超级会员V2的分享

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/228957
推荐阅读
相关标签
  

闽ICP备14008679号