MrNeRF/gaussian-splatting-cuda

ply file formatting?

Closed this issue · 3 comments

Hi, and thank you very much for making this code available!
I am looking to embed a renderer for these outputs in a realtime engine (c++/ DX12)

To start, i am reading the ply file (stored as ASCI), and spawning ellipsoids based on the data variables. Does this make sense? Or should I be using points?

Would you be able to outline how the data is stored in the ply file please?

These are obviously position and normal:

property float x
property float y
property float z
property float nx
property float ny
property float nz

what are these values?

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

Are these quaternion values?

property float rot_0
property float rot_1
property float rot_2
property float rot_3

If so, what is the order? WXYZ?

The scale values:

property float scale_0
property float scale_1
property float scale_2

Are all negative, is this expected?

Thanks!

MrNeRF commented

Hi,
the normals are all zero. Compare GaussianModel::Save_ply(...)
The rotation is a quaternion. Should be order (x,y,z,w). That's what I write.
Shouldn't the scale be all positive? Otherwise change the sign.
How the features are handled in the SIBR viewer? Didn't look it up yet. Please check it yourself. Would be nice if you could point me to the exact code.
Unfortunately, I didn't have the time to trace everything in the SIBR viewer how it exactly works.

See below for a little Python script I use to dump a .ply file. Some of the values are written as the log of the actual value, specifically the scale_X ones. Also, the opacity value needs to go through a sigmoid to get the actual value. This is based on how the SIBR viewer reads them (https://gitlab.inria.fr/sibr/sibr_core/-/blob/gaussian_code_release_linux/src/projects/gaussianviewer/renderer/GaussianView.cpp#L124).

#!/usr/bin/env python
import sys
from struct import unpack
from math import exp

def sigmoid(m1):
    return 1.0 / (1.0 + exp(-m1))

with open(sys.argv[1], 'rb') as f:
    
    nv = 0
    props = []
    
    line = f.readline().strip()
    while line != b'end_header':
        if line.startswith(b'element vertex'):
            nv = int(line[14:])
        elif line.startswith(b'property float'):
            props.append(line[15:].decode('utf8'))
        else:
            print('Skipping line: %s' % line)
        line = f.readline().strip()
        
    print(nv)
    print(props)
    
    for i in range(nv):
        sys.stdout.write('[%d] ' % i)
        for j, prop in enumerate(props):
            value = unpack('<f', f.read(4))[0]
            if prop == 'opacity':
                sys.stdout.write('%s %.6f (%.6f) | ' % (prop, sigmoid(value), value))
            elif prop in ['scale_0', 'scale_1', 'scale_2']:
                sys.stdout.write('%s %.6f (%.6f) | ' % (prop, exp(value), value))
            else:
                sys.stdout.write('%s %.6f | ' % (prop, value))
        sys.stdout.write('\n')

Should we include it in a new scripts folder? This is light on its deps so can be run with a vanilla python installation.
If you like, you can open a pr!