# 导入open3d和所有其他必要的库。 # 堆代码 duidaima.com import open3d as o3d import os import copy import numpy as np import pandas as pd from PIL import Image np.random.seed(42) #正在检查open3d上安装的版本。 o3d.__version__ # Open3D version used in this exercise: 0.16.0将三维模型加载为网格并将其可视化
#定义三维模型文件的路径。 mesh_path = "data/3d_model.obj" # 使用open3d将三维模型文件读取为三维网格。 mesh = o3d.io.read_triangle_mesh(mesh_path)要可视化网格,请运行以下代码行:
#可视化网格。 draw_geoms_list = [mesh] o3d.visualization.draw_geometries(draw_geoms_list)网格应该在一个新窗口中打开,看起来应该像下面的图像(请注意,网格打开时是静态图像,而不是像这里显示的动画图像)。可以使用鼠标指针根据需要旋转网格图像。
# 计算网格的法线。 mesh.compute_vertex_normals() #可视化网格。 draw_geoms_list = [mesh] o3d.visualization.draw_geometries(draw_geoms_list)一旦可视化,网格应该如下图所示出现。计算法线后,汽车将正确渲染,看起来像一个3D模型。
# 创建XYZ轴笛卡尔坐标系的网格。 # 该网格将显示X、Y和Z轴指向的方向,并且可以覆盖在3D网格上,以可视化其在欧几里得空间中的方向。 # X-axis : 红色箭头 # Y-axis : 绿色箭头 # Z-axis : 蓝色箭头 mesh_coord_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=5, origin=[0, 0, 0]) #使用坐标系可视化网格,以了解方向。 draw_geoms_list = [mesh_coord_frame, mesh] o3d.visualization.draw_geometries(draw_geoms_list)
#使用其束框裁剪汽车网格以移除其右半部分(正Z轴)。 bbox = mesh.get_axis_aligned_bounding_box() bbox_points = np.asarray(bbox.get_box_points()) bbox_points[:, 2] = np.clip(bbox_points[:, 2], a_min=None, a_max=0) bbox_cropped = o3d.geometry.AxisAlignedBoundingBox.create_from_points(o3d.utility.Vector3dVector(bbox_points)) mesh_cropped = mesh.crop(bbox_cropped) # 可视化裁剪的网格。 draw_geoms_list = [mesh_coord_frame, mesh_cropped] o3d.visualization.draw_geometries(draw_geoms_list)
#从网格中均匀采样100000个点,将其转换为点云。 n_pts = 100_000 pcd = mesh.sample_points_uniformly(n_pts) #可视化点云。 draw_geoms_list = [mesh_coord_frame, pcd] o3d.visualization.draw_geometries(draw_geoms_list)
#使用边界框裁剪汽车点云以移除其右半部分(正Z轴)。 pcd_cropped = pcd.crop(bbox_cropped) #可视化裁剪的点云。 draw_geoms_list = [mesh_coord_frame, pcd_cropped] o3d.visualization.draw_geometries(draw_geoms_list)
# 定义隐藏点删除操作的摄影机和半径参数。 diameter = np.linalg.norm(np.asarray(pcd.get_min_bound()) - np.asarray(pcd.get_max_bound())) camera = [0, 0, diameter] radius = diameter * 100 # 使用上面定义的摄影机和半径参数对点云执行隐藏点删除操作。 #输出是可见点的索引列表。 _, pt_map = pcd.hidden_point_removal(camera, radius)使用上面的可见点索引输出列表,我们可以在可视化点云之前将可见点和隐藏点涂成不同的颜色。
# 将点云中的所有可见点绘制为蓝色,将所有隐藏点绘制为红色。 pcd_visible = pcd.select_by_index(pt_map) pcd_visible.paint_uniform_color([0, 0, 1]) #蓝色点是可见点(需要保留)。 print("No. of visible points : ", pcd_visible) pcd_hidden = pcd.select_by_index(pt_map, invert=True) pcd_hidden.paint_uniform_color([1, 0, 0]) # 红色点是隐藏点(要删除)。 print("No. of hidden points : ", pcd_hidden) # 可视化点云中的可见(蓝色)和隐藏(红色)点。 draw_geoms_list = [mesh_coord_frame, pcd_visible, pcd_hidden] o3d.visualization.draw_geometries(draw_geoms_list)
#堆代码 duidaima.com #定义将度数转换为弧度的函数。 def deg2rad(deg): return deg * np.pi/180 #将点云绕X轴旋转90度。 x_theta = deg2rad(90) y_theta = deg2rad(0) z_theta = deg2rad(0) tmp_pcd_r = copy.deepcopy(pcd) R = tmp_pcd_r.get_rotation_matrix_from_axis_angle([x_theta, y_theta, z_theta]) tmp_pcd_r.rotate(R, center=(0, 0, 0)) #可视化旋转的点云。 draw_geoms_list = [mesh_coord_frame, tmp_pcd_r] o3d.visualization.draw_geometries(draw_geoms_list)
围绕X轴旋转90度的三维点云。请注意,与以前不同的是,现在Y轴(绿色箭头)沿着汽车的宽度尺寸运行,Z轴(蓝色箭头)沿着车辆的高度尺寸运行。X轴(红色箭头)没有变化,它仍然沿着汽车的长度方向运行
# 使用上面定义的相同摄影机和半径参数对旋转的点云执行隐藏点移除操作。 #输出是可见点的索引列表。 _, pt_map = tmp_pcd_r.hidden_point_removal(camera, radius) # 将旋转的点云中的所有可见点绘制为蓝色,将所有隐藏点绘制为红色。 pcd_visible = tmp_pcd_r.select_by_index(pt_map) pcd_visible.paint_uniform_color([0, 0, 1]) # 蓝色点是可见点(需要保留)。 print("No. of visible points : ", pcd_visible) pcd_hidden = tmp_pcd_r.select_by_index(pt_map, invert=True) pcd_hidden.paint_uniform_color([1, 0, 0]) #红色点是隐藏点(要删除)。 print("No. of hidden points : ", pcd_hidden) #可视化旋转的点云中的可见(蓝色)和隐藏(红色)点。 draw_geoms_list = [mesh_coord_frame, pcd_visible, pcd_hidden] o3d.visualization.draw_geometries(draw_geoms_list)
# 堆代码 duidaima.com # 定义一个函数以在X、Y和Z轴上旋转点云。 def get_rotated_pcd(pcd, x_theta, y_theta, z_theta): pcd_rotated = copy.deepcopy(pcd) R = pcd_rotated.get_rotation_matrix_from_axis_angle([x_theta, y_theta, z_theta]) pcd_rotated.rotate(R, center=(0, 0, 0)) return pcd_rotated # 定义一个函数以获取隐藏点移除操作的点云的相机和半径参数。 def get_hpr_camera_radius(pcd): diameter = np.linalg.norm(np.asarray(pcd.get_min_bound()) - np.asarray(pcd.get_max_bound())) camera = [0, 0, diameter] radius = diameter * 100 return camera, radius # 定义一个函数,使用前面定义的摄影机和半径参数对点云执行隐藏点删除操作。 #输出是未隐藏的点的索引列表。 def get_hpr_pt_map(pcd, camera, radius): _, pt_map = pcd.hidden_point_removal(camera, radius) return pt_map # 通过在三个轴中的每一个轴上将点云从-90度略微旋转到+90度,依次执行隐藏点移除操作,并在每次操作后聚合未隐藏的点的索引列表。 # 定义一个列表来存储每个隐藏点删除操作的聚合输出列表。 pt_map_aggregated = [] # 定义旋转点云的步长和角度值范围。 theta_range = np.linspace(-90, 90, 7) # 对顺序操作的次数进行计数。 view_counter = 1 total_views = theta_range.shape[0] ** 3 # 获取隐藏点移除操作的相机和半径参数。 camera, radius = get_hpr_camera_radius(pcd) # 循环使用上面为每个轴定义的角度值。 for x_theta_deg in theta_range: for y_theta_deg in theta_range: for z_theta_deg in theta_range: print(f"Removing hidden points - processing view {view_counter} of {total_views}.") #按给定的角度值旋转点云。 x_theta = deg2rad(x_theta_deg) y_theta = deg2rad(y_theta_deg) z_theta = deg2rad(z_theta_deg) pcd_rotated = get_rotated_pcd(pcd, x_theta, y_theta, z_theta) # 使用上面定义的摄影机和半径参数对旋转的点云执行隐藏点移除操作。 pt_map = get_hpr_pt_map(pcd_rotated, camera, radius) # 聚合未隐藏的点的索引的输出列表。 pt_map_aggregated += pt_map view_counter += 1 # 通过将聚合列表转换为集合,从聚合列表中删除所有重复的点。 pt_map_aggregated = list(set(pt_map_aggregated)) # 将点云中的所有可见点绘制为蓝色,将所有隐藏点绘制为红色。 pcd_visible = pcd.select_by_index(pt_map_aggregated) pcd_visible.paint_uniform_color([0, 0, 1]) # 蓝色点是可见点(需要保留)。 print("No. of visible points : ", pcd_visible) pcd_hidden = pcd.select_by_index(pt_map_aggregated, invert=True) pcd_hidden.paint_uniform_color([1, 0, 0]) # 红色点是隐藏点(要删除)。 print("No. of hidden points : ", pcd_hidden) # 可视化点云中的可见(蓝色)和隐藏(红色)点。 draw_geoms_list = [mesh_coord_frame, pcd_visible, pcd_hidden] # draw_geoms_list = [mesh_coord_frame, pcd_visible] # draw_geoms_list = [mesh_coord_frame, pcd_hidden] o3d.visualization.draw_geometries(draw_geoms_list)
#使用先前定义的边界框裁剪可见点的点云,以移除其右半部分(正Z轴)。 pcd_visible_cropped = pcd_visible.crop(bbox_cropped) # 使用先前定义的边界框裁剪隐藏点的点云,以移除其右半部分(正Z轴)。 pcd_hidden_cropped = pcd_hidden.crop(bbox_cropped) # 可视化裁剪的点云。 draw_geoms_list = [mesh_coord_frame, pcd_visible_cropped, pcd_hidden_cropped] o3d.visualization.draw_geometries(draw_geoms_list)
# 为点云创建一个数据帧,其中包含所有点的X、Y和Z位置坐标以及X、Y、Z方向上的法向单位向量坐标。 pcd_df = pd.DataFrame(np.concatenate((np.asarray(pcd.points), np.asarray(pcd.normals)), axis=1), columns=["x", "y", "z", "norm-x", "norm-y", "norm-z"] ) # 使用上面隐藏点删除操作中的点索引聚合列表,添加一列以指示点是否可见。 pcd_df["point_visible"] = False pcd_df.loc[pt_map_aggregated, "point_visible"] = True这将返回如下所示的数据帧,其中每个点都是由上面解释的七个属性列表示的行。
# 堆代码 duidaima.com # 将整个点云保存为.pcd文件。 pcd_save_path = "data/3d_model.pcd" o3d.io.write_point_cloud(pcd_save_path, pcd) # 将删除了隐藏点的点云保存为.pcd文件。 pcd_visible_save_path = "data/3d_model_hpr.pcd" o3d.io.write_point_cloud(pcd_visible_save_path, pcd_visible) # 将点云数据帧保存为.csv文件。 pcd_df_save_path = "data/3d_model.csv" pcd_df.to_csv(pcd_df_save_path, index=False)