Writer module

Writing functions for Gaussian splatting PLY files.

This module provides ultra-fast writing of Gaussian splatting PLY files in uncompressed format, with compressed format support planned.

API Examples:
>>> from gsply import plywrite
>>> plywrite("output.ply", means, scales, quats, opacities, sh0, shN)
>>> # Or use format-specific writers
>>> from gsply.writer import write_uncompressed
>>> write_uncompressed("output.ply", means, scales, quats, opacities, sh0, shN)
Performance:
  • Write uncompressed: 3-7ms for 50K Gaussians (7-17M Gaussians/sec)

  • Write compressed: 2-11ms for 50K Gaussians (4-25M Gaussians/sec)

gsply.writer.plywrite(file_path, data, scales=None, quats=None, opacities=None, sh0=None, shN=None, compressed=False, validate=True)[source]

Write Gaussian splatting PLY file with automatic optimization.

The helper accepts either a gsply.GSData instance (recommended), a gsply.GSTensor instance (converted to GSData automatically), or the individual Gaussian arrays. When _base is available the writer streams the consolidated buffer directly to disk; otherwise it performs a one-time consolidation before writing. File format selection happens automatically: the compressed path is chosen when compressed=True or when the destination filename already ends with .compressed.ply / .ply_compressed.

Parameters:
  • file_path (str | Path) – Output PLY file path (extension auto-adjusted if compressed=True)

  • data (GSData | GSTensor | np.ndarray) – GSData object, GSTensor object, OR (N, 3) xyz positions array

  • scales (ndarray | None) – (N, 3) scale parameters (required if data is array)

  • quats (ndarray | None) – (N, 4) rotation quaternions (required if data is array)

  • opacities (ndarray | None) – (N,) opacity values (required if data is array)

  • sh0 (ndarray | None) – (N, 3) DC spherical harmonics (required if data is array)

  • shN (ndarray | None) – (N, K, 3) or (N, K*3) - Higher-order SH coefficients (optional)

  • compressed (bool) – If True, write compressed format and auto-adjust extension

  • validate (bool) – If True, validate input shapes (default True)

Return type:

None

Performance:
  • GSData from plyread: ~7ms for 400K Gaussians (zero-copy, 53 M/s)

  • GSData created manually: ~19ms for 400K Gaussians (auto-consolidated, 49 M/s)

  • Individual arrays: ~19ms for 400K Gaussians (converted + consolidated)

  • All methods produce identical output

Example

>>> # RECOMMENDED: Pass GSData from file (automatic zero-copy)
>>> data = plyread("input.ply")
>>> plywrite("output.ply", data)  # ~7ms for 400K, zero-copy!
>>>
>>> # GSData created manually (auto-consolidated)
>>> data = GSData(means=means, scales=scales, ...)
>>> plywrite("output.ply", data)  # ~19ms for 400K, auto-optimized!
>>>
>>> # GSTensor (converted to GSData automatically)
>>> gstensor = plyread_gpu("input.compressed.ply", device="cuda")
>>> plywrite("output.ply", gstensor, compressed=False)  # Uncompressed PLY
>>>
>>> # Individual arrays (converted + auto-consolidated)
>>> plywrite("output.ply", means, scales, quats, opacities, sh0, shN)
>>>
>>> # Write compressed format
>>> plywrite("output.ply", data, compressed=True)
gsply.writer.write_uncompressed(file_path, data, validate=True)[source]

Write uncompressed Gaussian splatting PLY file with zero-copy optimization.

Always operates on GSData objects. Automatically uses zero-copy when data has a _base array (from plyread), achieving 6-8x speedup.

Performance:
  • Zero-copy path (data with _base): Header + I/O only, no memory copying * 400K SH3: ~15-20ms (vs 121ms without optimization) - 6-8x faster!

  • Standard path (data without _base): ~20-120ms depending on size and SH degree

  • Peak: 70M Gaussians/sec for 400K Gaussians, SH0 (zero-copy)

Parameters:
  • file_path (str | Path) – Output PLY file path

  • data (GSData) – GSData object containing Gaussian parameters

  • validate (bool) – If True, validate input shapes (default True)

Return type:

None

Example

>>> # RECOMMENDED: Pass GSData directly (automatic zero-copy)
>>> data = plyread("input.ply")
>>> write_uncompressed("output.ply", data)  # 6-8x faster!
>>>
>>> # Create GSData from scratch
>>> data = GSData(means, scales, quats, opacities, sh0, shN)
>>> write_uncompressed("output.ply", data)
gsply.writer.write_compressed(file_path, means, scales, quats, opacities, sh0, shN=None, validate=True)[source]

Write compressed Gaussian splatting PLY file (PlayCanvas format).

Compresses data using chunk-based quantization (256 Gaussians per chunk). Achieves 3.8-14.5x compression ratio using highly optimized vectorized operations.

Uses Numba JIT compilation for fast parallel compression (3.8x faster than pure NumPy).

Parameters:
  • file_path (str | Path) – Output PLY file path

  • means (ndarray) – (N, 3) - xyz positions

  • scales (ndarray) – (N, 3) - scale parameters

  • quats (ndarray) – (N, 4) - rotation quaternions (must be normalized)

  • opacities (ndarray) – (N,) - opacity values

  • sh0 (ndarray) – (N, 3) - DC spherical harmonics

  • shN (ndarray | None) – (N, K, 3) or (N, K*3) - Higher-order SH coefficients (optional)

  • validate (bool) – If True, validate input shapes (default True)

Return type:

None

Performance:
  • With JIT: ~15ms for 400K Gaussians, SH0 (27M Gaussians/sec)

  • With JIT: ~92ms for 400K Gaussians, SH3 (4.4M Gaussians/sec)

Format:

Compressed PLY with chunk-based quantization: - 256 Gaussians per chunk - Position: 11-10-11 bit quantization - Scale: 11-10-11 bit quantization - Color: 8-8-8-8 bit quantization - Quaternion: smallest-three encoding (2+10+10+10 bits) - SH coefficients: 8-bit quantization (optional)

Example

>>> write_compressed("output.ply", means, scales, quats, opacities, sh0, shN)
>>> # File is 14.5x smaller than uncompressed
gsply.writer.compress_to_bytes(data_or_means, scales=None, quats=None, opacities=None, sh0=None, shN=None, validate=True)[source]

Compress Gaussian splatting data to bytes (PlayCanvas format).

Compresses Gaussian data into PlayCanvas format and returns as bytes, without writing to disk. Useful for network transfer or custom storage.

Parameters:
  • data_or_means (GSData | ndarray) – Either a GSData object or means array (N, 3) float32

  • scales (ndarray | None) – Gaussian scales (N, 3) float32 (required if first arg is means)

  • quats (ndarray | None) – Gaussian quaternions (N, 4) float32 (required if first arg is means)

  • opacities (ndarray | None) – Gaussian opacities (N,) float32 (required if first arg is means)

  • sh0 (ndarray | None) – Degree 0 SH coefficients RGB (N, 3) float32 (required if first arg is means)

  • shN (ndarray | None) – Optional higher degree SH coefficients (N, K, 3) float32

  • validate (bool) – Whether to validate inputs

Return type:

bytes

Returns:

Complete compressed PLY file as bytes

Example

>>> from gsply import plyread, compress_to_bytes
>>> # Method 1: Using GSData (recommended)
>>> data = plyread("model.ply")
>>> compressed_bytes = compress_to_bytes(data)
>>>
>>> # Method 2: Using individual arrays (backward compatible)
>>> compressed_bytes = compress_to_bytes(
...     means, scales, quats, opacities, sh0, shN
... )
>>>
>>> # Save or transmit
>>> with open("output.compressed.ply", "wb") as f:
...     f.write(compressed_bytes)
gsply.writer.compress_to_arrays(data_or_means, scales=None, quats=None, opacities=None, sh0=None, shN=None, validate=True)[source]

Compress Gaussian splatting data to component arrays (PlayCanvas format).

Compresses Gaussian data into PlayCanvas format and returns as separate components (header, chunks, data, SH), without writing to disk. Useful for custom processing or partial updates.

Parameters:
  • data_or_means (GSData | ndarray) – Either a GSData object or means array (N, 3) float32

  • scales (ndarray | None) – Gaussian scales (N, 3) float32 (required if first arg is means)

  • quats (ndarray | None) – Gaussian quaternions (N, 4) float32 (required if first arg is means)

  • opacities (ndarray | None) – Gaussian opacities (N,) float32 (required if first arg is means)

  • sh0 (ndarray | None) – Degree 0 SH coefficients RGB (N, 3) float32 (required if first arg is means)

  • shN (ndarray | None) – Optional higher degree SH coefficients (N, K, 3) float32

  • validate (bool) – Whether to validate inputs

Return type:

tuple[bytes, ndarray, ndarray, ndarray | None]

Returns:

Tuple containing header_bytes (PLY header as bytes), chunk_bounds (Chunk boundary array (num_chunks, 18) float32), packed_data (Main compressed data array (N, 4) uint32), packed_sh (Optional compressed SH data array uint8)

Example

>>> from gsply import plyread, compress_to_arrays
>>> # Method 1: Using GSData (recommended)
>>> data = plyread("model.ply")
>>> header, chunks, packed, sh = compress_to_arrays(data)
>>>
>>> # Method 2: Using individual arrays (backward compatible)
>>> header, chunks, packed, sh = compress_to_arrays(
...     means, scales, quats, opacities, sh0, shN
... )
>>>
>>> # Process components individually
>>> print(f"Header size: {len(header)} bytes")
>>> print(f"Chunks shape: {chunks.shape}")
>>> print(f"Packed data: {packed.nbytes} bytes")