PLY 文件格式与 PVN3D 中的使用说明
1. 目的
本文说明两件事:
.ply文件通常包含哪些信息- 在当前
PVN3D项目中,代码实际读取并使用了.ply里的哪些信息
需要先明确一点:PLY 是一种通用 3D 数据格式,理论上可以存很多内容;但在当前仓库里,.ply 并没有被当作完整渲染资源来用,而主要被当作"物体 3D 几何点集的来源"。
2. PLY 文件的基本结构
一个典型的 .ply 文件分为两部分:
- 文件头
header - 数据区
body
典型示意:
ply
ply
format ascii 1.0
comment created by ...
element vertex 1000
property float x
property float y
property float z
property float nx
property float ny
property float nz
property uchar red
property uchar green
property uchar blue
element face 2000
property list uchar int vertex_indices
end_header
... vertex data ...
... face data ...
从这个结构可以看出,PLY 至少有以下几个核心概念。
2.1 文件头
文件头用于描述后面数据区的结构,例如:
- 文件是不是
ply - 存储格式是
ascii还是二进制 - 有多少个顶点
vertex - 每个顶点有哪些属性
- 有多少个面片
face - 每个面片如何组织
例如:
format ascii 1.0表示文本格式element vertex 1000表示后面有 1000 个顶点property float x表示每个顶点有一个名为x的浮点字段
2.2 顶点数据
顶点通常至少包含:
xyz
这 3 个值定义了一个 3D 点的位置。
除此之外,顶点还可能包含:
- 法线:
nx ny nz - 颜色:
red green blue - 透明度:
alpha - 纹理坐标:
u v - 置信度、标签等自定义属性
例如某一行顶点数据可能是:
text
0.012 0.031 -0.055 255 0 0
如果头部定义了 x y z red green blue,那么这一行就表示:
- 坐标
(0.012, 0.031, -0.055) - 颜色
(255, 0, 0)
2.3 面片数据
如果 .ply 表示的是网格模型而不只是点云,还会有 face。
典型形式:
text
3 12 18 25
含义通常是:
- 这是一个三角面
- 由编号为
12, 18, 25的 3 个顶点组成
如果是四边形或更一般的多边形,也可能是别的顶点数。
2.4 存储格式
PLY 常见有三种格式:
asciibinary_little_endianbinary_big_endian
其中:
ascii便于查看和手工调试- 二进制格式体积更小、读取更快
3. PLY 文件理论上可以包含哪些信息
从通用定义上看,.ply 可以存储:
- 物体表面点的 3D 坐标
- 点云颜色
- 法线方向
- 网格拓扑关系
- 材质或纹理相关属性
- 注释和元数据
- 任务相关自定义字段
因此,PLY 并不只是"点坐标文件",而是一个可以同时表达:
- 点云
- 带属性点云
- 三角网格
- 带颜色/法线/纹理的网格
的通用容器。
4. 在 PVN3D 中,PLY 实际被使用了哪些信息
在当前仓库中,核心结论很明确:
PVN3D 实际主要只使用了 .ply 中的顶点坐标 x y z。
下面这些字段在当前代码中没有看到被核心训练/评估逻辑依赖:
- 顶点颜色
red green blue - 顶点法线
nx ny nz - 面片
face - 材质和纹理
- 注释字段
也就是说,在本项目里,.ply 更接近"几何点集文件",而不是完整渲染模型资源。
5. PVN3D 中读取 .ply 的具体方式
当前仓库里主要有两种读取 .ply 的方式。
5.1 方式一:手写读取,只取前 3 列
代码位置:
对应函数:
python
def ply_vtx(self, pth):
f = open(pth)
assert f.readline().strip() == "ply"
f.readline()
f.readline()
N = int(f.readline().split()[-1])
while f.readline().strip() != "end_header":
continue
pts = []
for _ in range(N):
pts.append(np.float32(f.readline().split()[:3]))
return np.array(pts)
这段代码做了这些事:
- 检查文件第一行是不是
ply - 从头部读取顶点数
N - 找到
end_header - 逐行读取顶点数据
- 每行只取前 3 列
因此它只关心:
- 文件头中的顶点数量
- 每个顶点的前 3 个字段,即
x y z
如果 .ply 里还有:
nx ny nzrgbface
这些内容,这个函数都不会使用。
5.2 方式二:用 plyfile.PlyData 读取,但仍然只取 x y z
代码位置:
对应逻辑:
python
ply = PlyData.read(model_path)
data = ply.elements[0].data
x = data['x']
y = data['y']
z = data['z']
return np.stack([x, y, z], axis=-1)
以及:
python
ply = PlyData.read(...)
data = ply.elements[0].data
x = data['x']
y = data['y']
z = data['z']
model = np.stack([x, y, z], axis=-1)
虽然这里使用了 PlyData 做标准解析,但真正取出的仍然只有:
xyz
所以这条路径本质上和手写解析的目标一致:把 .ply 转成 N x 3 的 3D 点集。
6. PVN3D 中 .ply 具体用在什么地方
6.1 作为 LINEMOD 物体模型点的来源
代码位置:
核心逻辑:
python
ptxyz_pth = os.path.join(
'datasets/linemod/Linemod_preprocessed/models',
'obj_%02d.ply' % cls
)
pointxyz = self.ply_vtx(ptxyz_pth) / 1000.0
说明:
- LINEMOD 数据集下,物体几何模型来自
models/obj_xx.ply - 代码读取
.ply后只保留顶点坐标 - 然后除以
1000.0
这意味着当前代码假设:
.ply中的单位是毫米- 项目内部使用米
这一点很重要。若模型尺度错误,会直接影响:
- 关键点坐标
- 位姿变换尺度
- ADD / ADDS 评估结果
6.2 作为模型点集并随机下采样
还是在 pvn3d/lib/utils/basic_utils.py,读取完模型点后还会做一次随机删点:
python
dellist = [j for j in range(0, len(pointxyz))]
dellist = random.sample(dellist, len(pointxyz) - 2000)
pointxyz = np.delete(pointxyz, dellist, axis=0)
也就是说,.ply 提供的是原始模型顶点集合,而后续实际参与某些计算的,往往是一个下采样后的点集。
因此在本项目里,.ply 的作用不是"保持完整网格拓扑",而是"提供物体几何参考点"。
6.3 用于 ADD / ADDS 评估
代码位置:
核心逻辑:
python
mesh_pts = bs_utils.get_pointxyz_cuda(...)
add = bs_utils.cal_add_cuda(pred_RT, gt_RT, mesh_pts)
adds = bs_utils.cal_adds_cuda(pred_RT, gt_RT, mesh_pts)
这里 .ply 的作用是:
- 提供物体的 3D 模型点
- 将这些模型点分别变换到预测位姿和真实位姿下
- 计算两者之间的平均距离
所以在 ADD / ADDS 评估中,.ply 提供的是:
- 物体几何形状的参考基准
评估用到的不是纹理、颜色、面片,而是模型点坐标。
6.4 用于姿态可视化投影
代码位置:
核心逻辑:
python
mesh_pts = bs_utils.get_pointxyz(obj_id, ds_type=args.dataset).copy()
mesh_pts = np.dot(mesh_pts, pose[:, :3].T) + pose[:, 3]
mesh_p2ds = bs_utils.project_p3d(mesh_pts, 1.0, K)
np_rgb = bs_utils.draw_p2ds(np_rgb, mesh_p2ds, color=color)
这里 .ply 提供的模型点会被:
- 读成 3D 点集
- 用预测位姿做刚体变换
- 投影到图像平面
- 画到图像上
因此它承担的是:
- 预测结果的几何可视化
注意这里画的是"投影后的模型点",不是利用 .ply 的面片做真实网格渲染。
6.5 用于 Blender 模型或 YCB 模型的几何读取
代码位置:
这里通过 load_ply_model()、get_blender_model()、get_ycb_ply_mdl() 将 .ply 转成 N x 3 的点坐标数组。
其用途仍然是:
- 读取几何模型
- 为后续几何运算提供点集
不是读取颜色、法线、材质等信息。
7. 在当前项目中,没有使用 .ply 的哪些信息
从当前代码路径看,下面这些信息即使存在于 .ply 文件中,也不是当前核心流程依赖的内容:
- 顶点颜色
rgb - 顶点法线
nx ny nz - 三角面片
face - 材质属性
- 纹理坐标
- 注释信息
如果把一个 .ply 文件里的:
- 颜色删掉
- 法线删掉
- 面片删掉
只保留能被当前代码读取的顶点坐标,那么项目的大部分几何相关逻辑仍然是可以运行的。
这也解释了为什么当前仓库中一些函数只关心"每行前 3 个数"。
8. .ply 与 farthest*.txt 的关系
虽然这不是本文重点,但它和 .ply 的角色差异很大,值得顺手说明。
.ply:
- 存储完整物体模型的顶点集合
- 点很多
- 是物体整体几何来源
farthest.txt、farthest4.txt、farthest12.txt 等:
- 只存储少量关键点坐标
- 通常是从模型上挑出的代表性点
- 用于关键点监督和位姿恢复
所以两者关系是:
.ply提供完整几何farthest*.txt提供从完整几何中选出的少量关键点
9. 总结
9.1 从 PLY 格式本身看
PLY 是一个通用 3D 数据容器,可以包含:
- 顶点坐标
- 法线
- 颜色
- 面片拓扑
- 纹理或其他自定义属性
9.2 从当前 PVN3D 项目实现看
项目实际核心使用的主要是:
- 头部里的顶点数量信息
- 顶点坐标
x y z
这些数据被用于:
- 生成物体模型点集
- 构建姿态评估所需的几何参考
- 计算 ADD / ADDS
- 将预测位姿下的模型点投影到图像中做可视化
而以下信息并不是当前主要使用内容:
- 法线
- 颜色
- 面片
- 材质与纹理
因此,在当前 PVN3D 仓库的语境里,.ply 更准确的理解应当是:
"物体几何顶点坐标的来源文件"
而不是:
"被完整消费的网格渲染资源文件"。