Colmap和Gaussian Splatting保存出来的ply文件格式解析
Colmap
打印Colmap稀疏重建的点云,前13行是这样的:
ply
format binary_little_endian 1.0
element vertex 100267
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
end_header
对应的数据是这样的:
-0.520981 4.31948 -0.425874 0 0 0 127 194 178
其实可以发现稀疏重建的点云,并没有计算每个点的法向,只是保存的ply文件有这些数据。
Gaussian Splatting
对于sh_degree设置为3的Gaussian,每个Gaussian有62个参数(不看法向,实际上只有59个),前66行是这样的:
ply
format binary_little_endian 1.0
element vertex 407216
property float x
property float y
property float z
property float nx
property float ny
property float nz
property float f_dc_0
property float f_dc_1
property float f_dc_2
property float f_rest_0
property float f_rest_1
property float f_rest_2
property float f_rest_3
property float f_rest_4
property float f_rest_5
property float f_rest_6
property float f_rest_7
property float f_rest_8
property float f_rest_9
property float f_rest_10
property float f_rest_11
property float f_rest_12
property float f_rest_13
property float f_rest_14
property float f_rest_15
property float f_rest_16
property float f_rest_17
property float f_rest_18
property float f_rest_19
property float f_rest_20
property float f_rest_21
property float f_rest_22
property float f_rest_23
property float f_rest_24
property float f_rest_25
property float f_rest_26
property float f_rest_27
property float f_rest_28
property float f_rest_29
property float f_rest_30
property float f_rest_31
property float f_rest_32
property float f_rest_33
property float f_rest_34
property float f_rest_35
property float f_rest_36
property float f_rest_37
property float f_rest_38
property float f_rest_39
property float f_rest_40
property float f_rest_41
property float f_rest_42
property float f_rest_43
property float f_rest_44
property float opacity
property float scale_0
property float scale_1
property float scale_2
property float rot_0
property float rot_1
property float rot_2
property float rot_3
end_header
对应的数据是这样的:
2.1896963119506836 0.5654028058052063 1.664149284362793 0.0 0.0 0.0 0.5147678256034851 0.2996779680252075 -0.03710278868675232 0.02432778663933277 0.02444857358932495 0.02761399932205677 -0.009275455959141254 -0.07312484085559845 -0.07785580307245255 0.00238581420853734 0.059049881994724274 -0.00435364106670022 -0.00045146566117182374 0.028354087844491005 -0.08165063709020615 -0.044504206627607346 0.03321271389722824 0.02827625349164009 0.0210369061678648 0.029464859515428543 0.012756595388054848 0.0032518154475837946 -0.06004106625914574 -0.03746717795729637 0.014808553270995617 0.05368928611278534 -0.007978635840117931 -0.04243125021457672 0.002324196044355631 -0.05169673264026642 -0.08004658669233322 0.02979198843240738 0.05964512750506401 -0.022210335358977318 -0.03916030004620552 -0.007248427718877792 0.021033495664596558 0.02982276864349842 -0.004562453832477331 0.029783839359879494 0.003827654290944338 0.01913684606552124 -0.04349766671657562 -0.11221770942211151 -0.0740458145737648 -0.03573375567793846 0.08243989944458008 0.05897160992026329 -1.6227161884307861 -10.401392936706543 -3.377558946609497 -5.4260053634643555 0.9305478930473328 0.17775008082389832 -0.052619289606809616 -0.04175473749637604 2.176823616027832
对于sh_degree设置为0的Gaussian,每个Gaussian有17个参数(不看法向,实际上只有14个),前21行是这样的:
ply
format binary_little_endian 1.0
element vertex 1809737
property float x
property float y
property float z
property float nx
property float ny
property float nz
property float f_dc_0
property float f_dc_1
property float f_dc_2
property float opacity
property float scale_0
property float scale_1
property float scale_2
property float rot_0
property float rot_1
property float rot_2
property float rot_3
end_header
对应的数据是这样的:
-0.4938425123691559 4.382840156555176 -0.4051733613014221 0.0 0.0 0.0 0.18498234450817108 1.119383692741394 0.8595899343490601 -2.0330333709716797 -3.6038339138031006 -3.884824752807617 -3.724702835083008 0.8671903610229492 0.17955081164836884 -0.04104405641555786 -0.1000492125749588
转换代码参考
Colmap二进制点云转ascii点云
import open3d as o3d
import argparse
def convert_ply(input_file, output_file):
# 读取二进制格式的PLY文件
pcd = o3d.io.read_point_cloud(input_file)
# 将PLY数据保存为文本格式的PLY文件
o3d.io.write_point_cloud(output_file, pcd, write_ascii=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Convert binary PLY to ASCII PLY.')
parser.add_argument('input_file', type=str, help='Path to the input binary PLY file')
parser.add_argument('output_file', type=str, help='Path to the output ASCII PLY file')
args = parser.parse_args()
convert_ply(args.input_file, args.output_file)
Gaussian Splatting二进制点云转ascii点云
对于sh_degree=3
的情况:
import argparse
import struct
def convert_ply(input_file, output_file):
with open(input_file, 'rb') as f:
binary_data = f.read()
# 找到头部结束的位置
header_end = binary_data.find(b'end_header\n') + len(b'end_header\n')
header = binary_data[:header_end].decode('utf-8')
body = binary_data[header_end:]
# 生成新的 ASCII 头部
ascii_header = header.replace('binary_little_endian', 'ascii')
# 写入新的 ASCII 文件
with open(output_file, 'w') as f:
f.write(ascii_header)
# 解析每个顶点的数据
offset = 0
# 确定每个顶点的格式和大小
vertex_format = '<3f3f3f1f45f1f3f4f' # x, y, z, nx, ny, nz, f_dc_0, f_dc_1, f_dc_2, f_rest_0 to f_rest_44, opacity, scale_0, scale_1, scale_2, rot_0, rot_1, rot_2, rot_3
vertex_size = struct.calcsize(vertex_format)
vertex_count = int(header.split('element vertex ')[1].split()[0])
for _ in range(vertex_count):
vertex_data = struct.unpack_from(vertex_format, body, offset)
f.write(' '.join(map(str, vertex_data)) + '\n')
offset += vertex_size
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Convert binary PLY to ASCII PLY.')
parser.add_argument('input_file', type=str, help='Path to the input binary PLY file')
parser.add_argument('output_file', type=str, help='Path to the output ASCII PLY file')
args = parser.parse_args()
convert_ply(args.input_file, args.output_file)
对于sh_degree=0
的情况:
import argparse
import struct
def convert_ply(input_file, output_file):
with open(input_file, 'rb') as f:
binary_data = f.read()
# 找到头部结束的位置
header_end = binary_data.find(b'end_header\n') + len(b'end_header\n')
header = binary_data[:header_end].decode('utf-8')
body = binary_data[header_end:]
# 生成新的 ASCII 头部
ascii_header = header.replace('binary_little_endian', 'ascii')
# 写入新的 ASCII 文件
with open(output_file, 'w') as f:
f.write(ascii_header)
# 解析每个顶点的数据
offset = 0
vertex_format = '<3f3f3f1f3f4f' # x, y, z, nx, ny, nz, f_dc_0, f_dc_1, f_dc_2, opacity, scale_0, scale_1, scale_2, rot_0, rot_1, rot_2, rot_3
vertex_size = struct.calcsize(vertex_format)
vertex_count = int(header.split('element vertex ')[1].split()[0])
for _ in range(vertex_count):
vertex_data = struct.unpack_from(vertex_format, body, offset)
f.write(' '.join(map(str, vertex_data)) + '\n')
offset += vertex_size
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Convert binary PLY to ASCII PLY.')
parser.add_argument('input_file', type=str, help='Path to the input binary PLY file')
parser.add_argument('output_file', type=str, help='Path to the output ASCII PLY file')
args = parser.parse_args()
convert_ply(args.input_file, args.output_file)