File size: 4,302 Bytes
4db20e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""Gaussian Splatting utilities."""

import os
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union

import numpy as np
import torch


def export_gaussian_splatting(
    gaussian_cloud: Union[np.ndarray, torch.Tensor],
    output_path: Union[str, Path],
) -> str:
    """
    Export Gaussian Splatting representation to PLY format.
    
    Args:
        gaussian_cloud: [N, 14] array/tensor with columns:
            [x, y, z, scale_x, scale_y, scale_z,
             rot_qx, rot_qy, rot_qz, rot_qw,
             r, g, b, opacity]
    """
    if isinstance(gaussian_cloud, torch.Tensor):
        gaussian_cloud = gaussian_cloud.cpu().numpy()
    
    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        from plyfile import PlyData, PlyElement
        
        num_points = len(gaussian_cloud)
        dtype = [
            ('x', 'f4'), ('y', 'f4'), ('z', 'f4'),
            ('scale_0', 'f4'), ('scale_1', 'f4'), ('scale_2', 'f4'),
            ('rot_0', 'f4'), ('rot_1', 'f4'), ('rot_2', 'f4'), ('rot_3', 'f4'),
            ('f_dc_0', 'f4'), ('f_dc_1', 'f4'), ('f_dc_2', 'f4'),
            ('opacity', 'f4'),
        ]
        
        vertices = np.zeros(num_points, dtype=dtype)
        vertices['x'] = gaussian_cloud[:, 0]
        vertices['y'] = gaussian_cloud[:, 1]
        vertices['z'] = gaussian_cloud[:, 2]
        vertices['scale_0'] = gaussian_cloud[:, 3]
        vertices['scale_1'] = gaussian_cloud[:, 4]
        vertices['scale_2'] = gaussian_cloud[:, 5]
        vertices['rot_0'] = gaussian_cloud[:, 6]
        vertices['rot_1'] = gaussian_cloud[:, 7]
        vertices['rot_2'] = gaussian_cloud[:, 8]
        vertices['rot_3'] = gaussian_cloud[:, 9]
        vertices['f_dc_0'] = gaussian_cloud[:, 10]
        vertices['f_dc_1'] = gaussian_cloud[:, 11]
        vertices['f_dc_2'] = gaussian_cloud[:, 12]
        vertices['opacity'] = gaussian_cloud[:, 13]
        
        el = PlyElement.describe(vertices, 'vertex')
        PlyData([el], text=True).write(str(output_path))
        
    except ImportError:
        # Fallback: write simple ASCII PLY
        _write_ascii_ply(gaussian_cloud, output_path)
    
    return str(output_path)


def _write_ascii_ply(gaussian_cloud: np.ndarray, output_path: Path):
    """Write simple ASCII PLY fallback."""
    num_points = len(gaussian_cloud)
    
    with open(output_path, 'w') as f:
        f.write("ply\n")
        f.write("format ascii 1.0\n")
        f.write(f"element vertex {num_points}\n")
        f.write("property float x\n")
        f.write("property float y\n")
        f.write("property float z\n")
        f.write("property float scale_0\n")
        f.write("property float scale_1\n")
        f.write("property float scale_2\n")
        f.write("property float rot_0\n")
        f.write("property float rot_1\n")
        f.write("property float rot_2\n")
        f.write("property float rot_3\n")
        f.write("property float f_dc_0\n")
        f.write("property float f_dc_1\n")
        f.write("property float f_dc_2\n")
        f.write("property float opacity\n")
        f.write("end_header\n")
        
        for point in gaussian_cloud:
            f.write(" ".join(f"{v:.6f}" for v in point) + "\n")


def render_gaussian_splatting(
    gaussian_cloud: torch.Tensor,
    camera_pose: torch.Tensor,
    image_size: Tuple[int, int] = (512, 512),
    fov: float = 50.0,
) -> torch.Tensor:
    """
    Render Gaussian Splatting from given camera pose.
    
    Args:
        gaussian_cloud: [N, 14] tensor
        camera_pose: [4, 4] camera-to-world transform
        image_size: (H, W)
        fov: Field of view in degrees
        
    Returns:
        Rendered image [3, H, W]
    """
    # Placeholder: in production, use gsplat or differentiable rasterizer
    # For now, return empty image
    
    H, W = image_size
    return torch.zeros(3, H, W, device=gaussian_cloud.device)


def merge_gaussian_clouds(
    clouds: List[torch.Tensor],
) -> torch.Tensor:
    """Merge multiple Gaussian clouds into one."""
    if not clouds:
        return torch.zeros(0, 14)
    
    valid_clouds = [c for c in clouds if len(c) > 0]
    if not valid_clouds:
        return torch.zeros(0, 14)
    
    return torch.cat(valid_clouds, dim=0)