码力全开 / 点云图可视化

Created Wed, 02 Jul 2025 19:53:38 +0800 Modified Thu, 03 Jul 2025 11:05:29 +0800
876 Words 2 min

最近有家初创企业要求面试前机试,结果发了个ply(Polygen File Format)格式的文件,里面记录着生猪的点云图。其文件内容类似如下:

ply
format binary_little_endian 1.0
element vertex 626250
property float x
property float y
property float z
property uchar red
property uchar green
property uchar blue
end_header

要读取其数据主要有两种方式,一种是使用plyfile,另一种是使用open3d。首先来看第1种方式读取其数据:

>>> from plyfile import PlyData
>>> ply_data = PlyData.read("hog.ply")
>>> ply_data["vertex"]
PlyElement('vertex', (PlyProperty('x', 'float'), PlyProperty('y', 'float'), PlyProperty('z', 'float'), PlyProperty('red', 'uchar'), PlyProperty('green', 'uchar'), PlyProperty('blue', 'uchar')), count=626250, comments=[])
>>> ply_data["vertex"].data
memmap([(186.54663, -111.72368,  778.7497, 219, 230, 234),
        (185.23463, -115.89012,  780.1351, 209, 220, 224),
        (183.42976, -117.20565,  780.244 , 201, 212, 216), ...,
        (161.     ,  371.     , 1050.    ,  82,  84,  71),
        (162.     ,  371.     , 1050.    ,  80,  82,  69),
        (164.     ,  371.     , 1050.    ,  76,  75,  57)],
       dtype=[('x', '<f4'), ('y', '<f4'), ('z', '<f4'), ('red', 'u1'), ('green', 'u1'), ('blue', 'u1')])
>>> ply_data["vertex"].data["x"]
memmap([186.54663, 185.23463, 183.42976, ..., 161.     , 162.     ,
        164.     ], dtype=float32)

要获取其坐标值,需要进行如下的处理:

>>> import numpy as np
>>> vertices = np.vstack([ply['vertex']['x'], ply['vertex']['y'], ply['vertex']['z']]).T

通过使用numpy的vstack将3个向量拼接在一起,从而组成3x626250向量,再经过转置从而得到626250x3向量。

而第2种方式的代码如下:

>>> import open3d as o3d
>>> pcd = o3d.io.read_point_cloud("hog.ply")
>>> o3d.visualization.draw_geometries([pcd])

上述代码将实现点云的可视化。若要获取其对应的点,则可以使用如下的代码:

>>> points=np.asarray(pcd.points)

其结果如下图所示:

image

最后来看下如何对立方体点云的8个顶点进行可视化:

import copy
import numpy as np
import open3d as o3d

def create_cube_point_cloud(center=(0,0,0), side_length=1.0, points_per_edge=10):
    # 生成正方体顶点坐标
    half_len = side_length / 2
    x = np.linspace(center[0]-half_len, center[0]+half_len, points_per_edge)
    y = np.linspace(center[1]-half_len, center[1]+half_len, points_per_edge)
    z = np.linspace(center[2]-half_len, center[2]+half_len, points_per_edge)
    xx, yy, zz = np.meshgrid(x, y, z)
    points = np.vstack([xx.ravel(), yy.ravel(), zz.ravel()]).T
    
    # 创建点云对象
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(points)
    return pcd

def extract_cube_vertices(points):
    """提取立方体点云的8个顶点"""
    min_coords = np.min(points, axis=0)
    max_coords = np.max(points, axis=0)
    # 组合所有极值点坐标
    vertices = np.array([
        [min_coords[0], min_coords[1], min_coords[2]],
        [min_coords[0], min_coords[1], max_coords[2]],
        [min_coords[0], max_coords[1], min_coords[2]],
        [min_coords[0], max_coords[1], max_coords[2]],
        [max_coords[0], min_coords[1], min_coords[2]],
        [max_coords[0], min_coords[1], max_coords[2]],
        [max_coords[0], max_coords[1], min_coords[2]],
        [max_coords[0], max_coords[1], max_coords[2]]
    ])
    # 将Numpy转换为点云数据
    vertex_cloud = o3d.geometry.PointCloud()
    vertex_cloud.points = o3d.utility.Vector3dVector(vertices)
    return vertex_cloud

def visualize_keypoints(pcd, keypoints):
    """可视化点云和关键点"""
    # 创建点云的副本并为关键点设置颜色
    pcd_copy = copy.deepcopy(pcd)
    pcd_copy.paint_uniform_color([0.5, 0.5, 0.5])  # 原始点云设为灰色
    
    # 创建关键点的点云并设置为红色
    keypoints_pcd = o3d.geometry.PointCloud()
    keypoints_pcd.points = keypoints.points
    keypoints_pcd.paint_uniform_color([1.0, 0.0, 0.0])  # 关键点设为红色
    # 可视化
    o3d.visualization.draw_geometries([pcd_copy, keypoints_pcd])

def main():
    # 生成立方体点云
    pcd = create_cube_point_cloud()
    keypoints = extract_cube_vertices(pcd.points)
    
    # 可视化结果
    visualize_keypoints(pcd, keypoints)

这里由于给定的是规则立方体点云,我们通过几何极值的方式对顶点进行提取,通过计算点云在X,Y,Z三个轴上最大值和最小值的组合。其实整个过程还是比较好理解的,我们通过np.min和np.max得到这1000个点中最小点和最大点的坐标。

其结果如下图所示:

cube

如果喜欢这篇文章或对您有帮助,可以:[☕] 请我喝杯咖啡 | [💓] 小额赞助