Skip to main content

Ultra-fast CPU processing for 3D Gaussian Splatting (color, transform, filter)

Project description

gsmod

High-Performance Processing for 3D Gaussian Splatting

Python License: MIT Tests pre-commit

CPU: 1,389M colors/sec | 698M Gaussians/sec transforms | GPU: 1.09B Gaussians/sec | Up to 183x speedup

Features | Installation | Quick Start | Performance | Documentation


Overview

gsmod is a pure Python library for ultra-fast processing of 3D Gaussian Splatting data using Look-Up Tables (LUTs) and Numba-accelerated operations. Built for performance-critical applications, gsmod achieves color processing speeds up to 1,722M colors/sec, transform speeds up to 1,593M Gaussians/sec, and filtering speeds up to 447M Gaussians/sec.

Why gsmod?

  • Blazing Fast: Zero-copy APIs, LUT-based color ops, Numba JIT with parallel processing
  • GPU Acceleration: PyTorch-based GPU operations with up to 183x speedup (1.09B Gaussians/sec)
  • Pure Python: NumPy + Numba for CPU, PyTorch for GPU (no C++ compilation needed)
  • Composable: Pipeline API for chaining operations, built-in presets
  • Format-Aware: Automatic SH/RGB format tracking and conversion optimization
  • Complete: Color grading, 3D transforms, spatial filtering all in one library
  • Integrated with gsply: Built on gsply v0.3.0+ for advanced data management

Features

  • Fastest Color Processing: Peak performance of 1,722M colors/sec with zero-copy API

    • 100K colors: 0.072ms (1,389M/s) zero-copy, 0.473ms (211M/s) standard
    • 1M colors: 0.581ms (1,722M/s)
    • Operations: 15 color adjustments (temperature, tint, brightness, contrast, gamma, saturation, vibrance, shadows, highlights, fade, hue_shift, shadow_tint, highlight_tint)
    • Optimizations: Zero-copy API (6.6x faster), LUT-based processing, nogil=True for true parallelism
  • Fast 3D Transforms: Up to 1,593M Gaussians/sec for geometric operations

    • 1M Gaussians: 1.43ms (698M/s) combined transform
    • 500K Gaussians: 0.31ms (1,593M/s) peak performance
    • Operations: translate, rotate, scale, combined transforms
    • Rotation formats: quaternion, matrix, axis_angle, euler
    • Utilities: Quaternion multiply, format conversions
  • High-Performance Filtering: 62-447M Gaussians/sec full filtering pipeline

    • 1M Gaussians full filtering: 16.1ms (62M/s)
    • Individual operations: 392-447M Gaussians/sec
    • Volume filtering: Sphere and cuboid spatial selection
    • Property filtering: Opacity and scale thresholds with AND logic
    • Multi-layer masks: FilterMasks API with 55x faster Numba-optimized combination (0.026ms vs 1.447ms)
    • Optimizations: Fused kernels, parallel scatter pattern, nogil=True, adaptive mask combination
  • Pre-Activation Stage (via gsply): Prepare log-domain GSData for downstream GPU/CPU pipelines

    • Use gsply.apply_pre_activations() to exponentiate scales, sigmoid opacities, and normalize quaternions
    • 1.3ms for 1M Gaussians on a laptop CPU (~750M Gaussians/sec)
    • Works in-place with automatic dtype/contiguity fixes for data from gsply.plyread
  • Composable Pipeline: Chain operations with lazy execution

    • Built-in presets: 7 color grading looks (cinematic, warm, cool, vibrant, muted, dramatic)
    • Functional API: One-line color adjustments and preset application
    • Custom operations: Add user-defined processing steps
  • GPU Acceleration: PyTorch-based GPU pipeline for massive parallelism

    • 1M Gaussians: Up to 183x speedup over CPU
    • Throughput: 1.09 billion Gaussians/sec on RTX 3090 Ti
    • Format-aware: Automatic SH/RGB format tracking with lazy conversion
    • Operations: All CPU operations available on GPU (color, transform, filter)
  • Pure Python: NumPy + Numba JIT (no C++ compilation required)

  • Type-safe: Full type hints with Python 3.12+ syntax (PEP 695)

  • Production-ready: Comprehensive test suite, CI/CD pipeline, pre-commit hooks, detailed documentation


Installation

From PyPI

pip install gsmod

From Source

git clone https://github.com/OpsiClear/gsmod.git
cd gsmod
pip install -e .

Requirements: Python >= 3.10, NumPy >= 1.24.0, Numba >= 0.59.0

GPU Support (Optional):

pip install torch --index-url https://download.pytorch.org/whl/cu121

Quick Start

Simplified API (Recommended)

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues
from gsmod import CINEMATIC, STRICT_FILTER, DOUBLE_SIZE

# Load Gaussian splatting data
data = GSDataPro.from_ply("scene.ply")

# Apply operations with fluent chaining
data.color(ColorValues(brightness=1.2, saturation=1.3))
data.filter(FilterValues(min_opacity=0.1, sphere_radius=0.8))
data.transform(TransformValues.from_scale(2.0))

# Or use presets
data.color(CINEMATIC)
data.filter(STRICT_FILTER)
data.transform(DOUBLE_SIZE)

# Save result
data.to_ply("output.ply")

Using Config Values

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues

data = GSDataPro.from_ply("scene.ply")

# Color adjustments
data.color(ColorValues(
    brightness=1.2,
    contrast=1.1,
    saturation=1.3,
    temperature=0.6,  # 0=neutral, positive=warm, negative=cool
    gamma=1.05,
    shadows=0.1,
    highlights=-0.05
))

# Spatial filtering
data.filter(FilterValues(
    min_opacity=0.1,
    max_scale=2.5,
    sphere_radius=0.8,
    sphere_center=(0, 0, 0)
))

# 3D transforms
data.transform(TransformValues.from_translation(1.0, 0.0, 0.0))
data.transform(TransformValues.from_rotation_euler(0, 45, 0))  # degrees
data.transform(TransformValues.from_scale(1.5))

data.to_ply("output.ply")

Composing Values

from gsmod import GSDataPro, ColorValues, WARM, CINEMATIC

data = GSDataPro.from_ply("scene.ply")

# Compose presets with custom values using +
warm_bright = WARM + ColorValues(brightness=1.2)
data.color(warm_bright)

# Compose multiple presets
cinematic_warm = CINEMATIC + WARM
data.color(cinematic_warm)

GPU Pipeline (Recommended for Large Data)

from gsmod.torch import GSTensorPro
from gsmod import ColorValues, FilterValues, TransformValues

# Load data and convert to GPU
data = GSTensorPro.from_ply("scene.ply", device="cuda")

# Apply GPU-accelerated operations (up to 183x faster)
data.filter(FilterValues(min_opacity=0.1, sphere_radius=0.8))
data.transform(TransformValues.from_translation(1, 0, 0))
data.color(ColorValues(brightness=1.2, saturation=1.3))

# Save result
data.to_ply("output.ply")

Method Chaining

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues

data = GSDataPro.from_ply("scene.ply")

# Chain operations (all return self for fluent API)
(data
    .filter(FilterValues(min_opacity=0.1))
    .transform(TransformValues.from_scale(2.0))
    .color(ColorValues(brightness=1.2))
)

data.to_ply("output.ply")

Copy vs Inplace

from gsmod import GSDataPro, ColorValues

data = GSDataPro.from_ply("scene.ply")

# Inplace (default) - modifies data directly
data.color(ColorValues(brightness=1.2))

# Copy - returns new instance, original unchanged
result = data.color(ColorValues(brightness=1.2), inplace=False)

GSData Pre-Activation (Optional)

When your training or authoring pipeline stores Gaussians in log-space (log scales, logit opacities, non-normalized quats), fuse the conversion into a single CPU pass before uploading to the renderer:

import gsply

data = gsply.plyread("scene_raw_logits.ply")

gsply.apply_pre_activations(
    data,
    min_scale=1e-4,
    max_scale=100.0,
    min_quat_norm=1e-8,
    inplace=True,
)

gsply.apply_pre_activations exponentiates + clamps the scales, runs a numerically stable sigmoid on logit opacities, and normalizes quaternions—processing ~1M Gaussians in ≈1.3 ms (≈750M/sec). The helper automatically ensures float32 and contiguous buffers, so it pairs nicely with the zero-copy arrays returned by gsply.plyread.

Using Presets

from gsmod import GSDataPro
from gsmod import (
    # Color presets
    WARM, COOL, NEUTRAL, CINEMATIC, VIBRANT, MUTED, DRAMATIC, VINTAGE, GOLDEN_HOUR, MOONLIGHT,
    # Filter presets
    STRICT_FILTER, QUALITY_FILTER, CLEANUP_FILTER,
    # Transform presets
    DOUBLE_SIZE, HALF_SIZE, FLIP_X, FLIP_Y, FLIP_Z,
)

data = GSDataPro.from_ply("scene.ply")

# Apply built-in color grading presets
data.color(CINEMATIC)
data.color(WARM)
data.color(VIBRANT)

# Filter presets
data.filter(STRICT_FILTER)    # min_opacity=0.5, max_scale=1.0, sphere_radius=10.0
data.filter(QUALITY_FILTER)   # min_opacity=0.3, max_scale=2.0

# Transform presets
data.transform(DOUBLE_SIZE)   # scale 2x
data.transform(FLIP_X)        # flip around X axis

Loading from Dict/JSON

from gsmod import GSDataPro
from gsmod.config.presets import (
    color_from_dict, filter_from_dict, transform_from_dict,
    load_color_json, load_filter_json, load_transform_json,
    get_color_preset, get_filter_preset, get_transform_preset,
)

data = GSDataPro.from_ply("scene.ply")

# Load from dictionary
color_dict = {"brightness": 1.2, "saturation": 1.3}
data.color(color_from_dict(color_dict))

# Load from JSON file
data.color(load_color_json("my_color_preset.json"))

# Get preset by name
data.color(get_color_preset("cinematic"))
data.filter(get_filter_preset("strict"))
data.transform(get_transform_preset("double_size"))

Complete API Guide

GSDataPro API

Basic Usage

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues

# Load Gaussian splatting data
data = GSDataPro.from_ply("scene.ply")

# Apply operations
data.color(ColorValues(
    brightness=1.2,
    contrast=1.1,
    saturation=1.3,
    temperature=0.6,
    gamma=1.05,
    shadows=0.1,
    highlights=-0.05
))

data.filter(FilterValues(
    min_opacity=0.1,
    max_scale=2.5,
    sphere_radius=0.8
))

data.transform(TransformValues.from_scale(2.0))
data.transform(TransformValues.from_translation(1, 0, 0))
data.transform(TransformValues.from_rotation_euler(0, 45, 0))

# Save result
data.to_ply("output.ply")

# Copy instead of inplace
result = data.color(ColorValues(brightness=1.2), inplace=False)

ColorValues Parameters

from gsmod import ColorValues

ColorValues(
    # Basic adjustments (multipliers, 1.0=no change)
    brightness=1.0,    # Multiplier (1.0=no change)
    contrast=1.0,      # Multiplier (1.0=no change)
    gamma=1.0,         # Gamma correction (1.0=linear)
    saturation=1.0,    # Multiplier (0=grayscale, 1.0=no change)
    vibrance=1.0,      # Selective saturation (1.0=no change)

    # White balance (additive, 0.0=no change)
    temperature=0.0,   # -1=cool/blue, 0=neutral, 1=warm/orange
    tint=0.0,          # -1=green, 0=neutral, 1=magenta

    # Tone adjustments (additive, 0.0=no change)
    shadows=0.0,       # Shadow lift/crush (-1 to 1)
    highlights=0.0,    # Highlight lift/crush (-1 to 1)
    fade=0.0,          # Black point lift for film look (0 to 1)

    # Color rotation
    hue_shift=0.0,     # Hue rotation in degrees (-180 to 180)

    # Split toning (shadow/highlight tinting)
    shadow_tint_hue=0.0,     # Shadow tint hue (-180 to 180 degrees)
    shadow_tint_sat=0.0,     # Shadow tint saturation (0 to 1)
    highlight_tint_hue=0.0,  # Highlight tint hue (-180 to 180 degrees)
    highlight_tint_sat=0.0,  # Highlight tint saturation (0 to 1)
)

# Factory methods
ColorValues.from_k(4500)  # Create from color temperature in Kelvin

FilterValues Parameters

from gsmod import FilterValues

FilterValues(
    min_opacity=0.0,         # Minimum opacity threshold
    max_opacity=1.0,         # Maximum opacity threshold
    min_scale=0.0,           # Minimum scale threshold
    max_scale=float('inf'),  # Maximum scale threshold
    sphere_radius=float('inf'),  # Sphere filter radius
    sphere_center=(0, 0, 0), # Sphere filter center
    box_min=None,            # Box filter min corner (x, y, z)
    box_max=None,            # Box filter max corner (x, y, z)
)

TransformValues Parameters

from gsmod import TransformValues

TransformValues(
    scale=1.0,                        # Uniform scale
    rotation=(1.0, 0.0, 0.0, 0.0),    # Quaternion (w, x, y, z)
    translation=(0.0, 0.0, 0.0),      # Translation vector
)

# Factory methods
TransformValues.from_scale(2.0)
TransformValues.from_translation(1.0, 0.0, 0.0)
TransformValues.from_rotation_euler(roll, pitch, yaw)  # degrees
TransformValues.from_euler_rad(roll, pitch, yaw)       # radians
TransformValues.from_rotation_axis_angle(axis, angle)  # degrees
TransformValues.from_axis_angle_rad(axis, angle)       # radians

Transform API

Basic Transform Operations

from gsmod import GSDataPro, TransformValues

# Load Gaussian splatting data
data = GSDataPro.from_ply("scene.ply")

# Individual operations
data.transform(TransformValues.from_translation(1, 0, 0))
data.transform(TransformValues.from_rotation_euler(0, 45, 0))
data.transform(TransformValues.from_scale(2.0))

# Save result
data.to_ply("transformed.ply")

Composed Transforms

from gsmod import GSDataPro, TransformValues

data = GSDataPro.from_ply("scene.ply")

# Compose multiple transforms (matrix multiplication)
combined = (
    TransformValues.from_translation(1.0, 0.0, 0.0)
    + TransformValues.from_rotation_euler(0, 45, 0)
    + TransformValues.from_scale(2.0)
)

data.transform(combined)
data.to_ply("output.ply")

Rotation Format Examples

from gsmod import TransformValues

# From euler angles (degrees)
TransformValues.from_rotation_euler(0, 45, 0)  # pitch 45 degrees

# From euler angles (radians)
TransformValues.from_euler_rad(0, 0.785, 0)

# From axis-angle (degrees)
TransformValues.from_rotation_axis_angle((0, 0, 1), 90)  # 90 degrees around Z

# From axis-angle (radians)
TransformValues.from_axis_angle_rad((0, 0, 1), 1.57)

# Direct quaternion
TransformValues(rotation=(0.9239, 0, 0, 0.3827))  # w, x, y, z

Filtering API

Basic Filtering

from gsmod import GSDataPro, FilterValues

# Load data
data = GSDataPro.from_ply("scene.ply")

# Apply filters
data.filter(FilterValues(
    min_opacity=0.1,     # Remove low-opacity Gaussians
    max_scale=2.5,       # Remove large-scale outliers
    sphere_radius=0.8,   # Keep 80% of scene
    sphere_center=(0, 0, 0)
))

data.to_ply("filtered.ply")

Filtering Options

from gsmod import GSDataPro, FilterValues

data = GSDataPro.from_ply("scene.ply")

# Option 1: Sphere filtering
data.filter(FilterValues(
    sphere_radius=0.8,
    sphere_center=(0, 0, 0)
))

# Option 2: Box filtering
data.filter(FilterValues(
    box_min=(-0.5, -0.5, -0.5),
    box_max=(0.5, 0.5, 0.5)
))

# Option 3: Property filtering only
data.filter(FilterValues(
    min_opacity=0.05,
    max_scale=2.5
))

# Combined filtering
data.filter(FilterValues(
    min_opacity=0.1,
    max_scale=2.5,
    sphere_radius=0.8
))

Composed Filters

from gsmod import GSDataPro, FilterValues, STRICT_FILTER

data = GSDataPro.from_ply("scene.ply")

# Compose filters (stricter values win)
combined = FilterValues(min_opacity=0.3) + FilterValues(min_opacity=0.5)
# combined.min_opacity == 0.5  (stricter)

# Combine preset with custom
custom = STRICT_FILTER + FilterValues(sphere_radius=5.0)
data.filter(custom)

Atomic Filter API

The Filter class provides an atomic, composable approach to filtering. Each filter is a single operation that can be combined using Python operators:

from gsmod import Filter

# Create atomic filters using factory methods
sphere = Filter.sphere(center=(0,0,0), radius=5.0)
box = Filter.box(size=(3,3,3), rotation=(0, 0, 0.5))
ellipsoid = Filter.ellipsoid(radii=(2,3,4))
frustum = Filter.frustum(position=(0,0,10), fov=60)
opacity = Filter.min_opacity(0.1)
scale = Filter.max_scale(3.0)

# Combine with Python operators
combined = sphere & opacity & scale      # AND logic
alternative = sphere | box               # OR logic
inverted = ~sphere                       # NOT logic
complex_filter = (sphere & opacity) | box

# Apply to data
filtered = combined(data, inplace=False)

# Get mask only (fast - no data copying)
mask = combined.get_mask(data)
print(f"Keeping {mask.sum()}/{len(mask)} Gaussians")

# Utility methods
print(sphere.summary(data))  # "850/1000 (85.0%)"
count = sphere.count(data)   # 850

Combining Filters

from gsmod import Filter

# Individual filter masks
sphere_mask = Filter.sphere(radius=5.0).get_mask(data)
opacity_mask = Filter.min_opacity(0.1).get_mask(data)
scale_mask = Filter.max_scale(3.0).get_mask(data)

# Combine masks with boolean logic
combined_and = sphere_mask & opacity_mask & scale_mask  # All must pass
combined_or = sphere_mask | opacity_mask                # Any must pass
inverse = ~sphere_mask                                  # Outside sphere

# Apply combined mask
filtered = data[combined_and]

# Or combine filters directly (more readable)
combined = Filter.sphere(radius=5.0) & Filter.min_opacity(0.1) & Filter.max_scale(3.0)
filtered = combined(data, inplace=False)

Volume Filters

from gsmod import Filter

# Sphere filter
sphere = Filter.sphere(center=(0,0,0), radius=5.0)

# Box filter with optional rotation (axis-angle in radians)
box = Filter.box(center=(0,0,0), size=(3,3,3), rotation=(0, 0, 0.5))

# Ellipsoid filter with optional rotation
ellipsoid = Filter.ellipsoid(center=(0,0,0), radii=(2,3,4), rotation=(0.1, 0, 0))

# Camera frustum filter
frustum = Filter.frustum(
    position=(0, 0, 10),
    rotation=(0, 0, 0),  # axis-angle
    fov=60,              # degrees
    aspect=1.0,
    near=0.1,
    far=100.0
)

Multi-Layer Mask Management (FilterMasks)

For complex filtering scenarios requiring multiple independent mask layers with different combination strategies, use the FilterMasks API for managing named mask layers:

from gsmod import GSDataPro, Filter
from gsmod.filter import FilterMasks

data = GSDataPro.from_ply("scene.ply")

# Create FilterMasks manager
masks = FilterMasks(data)

# Add multiple named mask layers using atomic filters
masks.add("opacity", Filter.min_opacity(0.3))
masks.add("sphere", Filter.sphere(radius=5.0))
masks.add("scale", Filter.max_scale(2.0))

# Inspect mask layers
masks.summary()
# Output:
# opacity: 45231/100000 (45.2%)
# sphere: 67890/100000 (67.9%)
# scale: 89123/100000 (89.1%)

# Combine masks with AND logic (all conditions must pass)
combined_and = masks.combine(mode="and")
print(f"{combined_and.sum():,} Gaussians pass all filters")

# Combine masks with OR logic (any condition passes)
combined_or = masks.combine(mode="or")
print(f"{combined_or.sum():,} Gaussians pass any filter")

# Apply masks directly to filter data
filtered = masks.apply(mode="and", inplace=False)

# Access individual mask layers
opacity_mask = masks["opacity"]

Performance: FilterMasks uses Numba-optimized mask combination with automatic strategy selection:

  • 1 layer: NumPy (0.006ms, lower overhead)
  • 2+ layers: Numba parallel (0.026ms vs 1.447ms numpy, 55x faster)
  • Large-scale (1M Gaussians, 5 layers): 0.425ms vs 14.587ms (34x faster)

The mask combination overhead is negligible (3.8% of total filtering time) thanks to Numba optimization, making multi-layer filtering practical for interactive applications.

Using Low-Level Utilities

from gsmod.filter import (
    calculate_scene_bounds,
    calculate_recommended_max_scale
)
from gsmod import GSDataPro, FilterValues

data = GSDataPro.from_ply("scene.ply")

# Calculate scene bounds (for reference)
bounds = calculate_scene_bounds(data.means)
print(f"Scene center: {bounds.center}")
print(f"Scene size: {bounds.sizes}")

# Calculate recommended scale threshold
max_scale = calculate_recommended_max_scale(data.scales, percentile=99.5)
print(f"Recommended max_scale: {max_scale:.4f}")

# Use in filtering with absolute values
data.filter(FilterValues(
    sphere_center=tuple(bounds.center),
    sphere_radius=bounds.max_size * 0.8,  # 80% of scene size
    max_scale=max_scale
), inplace=True)

Complete Processing Example

A full example combining all operations:

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues
from gsmod import CINEMATIC

# Load data
data = GSDataPro.from_ply("scene.ply")

# Apply all operations with method chaining
(data
    # Filtering
    .filter(FilterValues(
        min_opacity=0.1,
        max_scale=2.5,
        sphere_radius=5.0  # Absolute radius in world units
    ))
    # Transforms
    .transform(TransformValues.from_translation(1, 0, 0))
    .transform(TransformValues.from_rotation_euler(0, 45, 0))
    .transform(TransformValues.from_scale(1.5))
    # Color grading
    .color(ColorValues(
        temperature=0.6,
        brightness=1.2,
        contrast=1.1,
        saturation=1.3
    ))
)

# Save
data.to_ply("output.ply")

Using Presets with Custom Values

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues
from gsmod import CINEMATIC, WARM, STRICT_FILTER, DOUBLE_SIZE

data = GSDataPro.from_ply("scene.ply")

# Compose preset with custom adjustments
color_style = CINEMATIC + ColorValues(brightness=1.1)
data.color(color_style)

# Combine presets
warm_cinematic = WARM + CINEMATIC
data.color(warm_cinematic)

# Apply filter and transform presets
data.filter(STRICT_FILTER)
data.transform(DOUBLE_SIZE)

data.to_ply("output.ply")

Performance

Color Processing (100K colors)

API Time Throughput Speedup
apply() 0.473 ms 211 M/s 1.00x
apply_numpy() 0.480 ms 208 M/s 0.99x
apply_numpy_inplace() 0.072 ms 1,389 M/s 6.57x

Color Batch Scaling (apply_numpy_inplace)

Batch Size Time Throughput
1K 0.016 ms 64 M/s
10K 0.022 ms 461 M/s
100K 0.086 ms 1,162 M/s
1M 0.581 ms 1,722 M/s

3D Transform (1M Gaussians)

Operation Time Throughput
Combined transform 1.743 ms 574 M G/s

Transform Batch Scaling

Batch Size Time Throughput
10K 0.09 ms 106 M G/s
100K 0.12 ms 863 M G/s
500K 0.31 ms 1,593 M G/s
1M 1.43 ms 698 M G/s
2M 7.31 ms 273 M G/s

Filtering Performance (1M Gaussians)

Operation Time Throughput
Scene bounds (one-time) 35.7 ms 28 M/s
Recommended scale (one-time) 6.4 ms 157 M/s
Sphere filter (nogil=True) 2.5 ms 405 M/s
Cuboid filter (nogil=True) 4.8 ms 207 M/s
Opacity filter (nogil=True) 2.6 ms 392 M/s
Scale filter (nogil=True) 2.2 ms 447 M/s
Combined filter 3.6 ms 276 M/s
Full filtering (filter_gaussians) 16.1 ms 62.1 M/s

Key Performance Highlights

CPU Performance:

  • Peak Color Processing: 1,722M colors/sec (1M batch, zero-copy)
  • Peak Transform Speed: 1,593M Gaussians/sec (500K batch)
  • Peak Filtering Speed: 447M Gaussians/sec (scale filter)
  • Full Pipeline: 62.1M Gaussians/sec (complete filtering)

GPU Performance (1M Gaussians, RTX 3090 Ti):

  • Peak Speedup: 183.9x (sphere filter)

  • Average Speedup: 43.2x across all operations

  • Throughput: 1.09 billion Gaussians/sec

  • Transform Operations: 86-93x speedup (translate, scale)

  • Color Operations: 15-31x speedup (brightness, saturation)

  • Scalability: Linear scaling from 1K to 2M Gaussians on both CPU and GPU

Optimization Details

  • Zero-copy APIs: Direct memory operations without allocation overhead (6.6x speedup)
  • LUT-based processing: Pre-computed look-up tables for color operations
  • nogil=True: True parallelism by releasing GIL (+15-37% performance)
  • Fused kernels: Combined opacity+scale filtering in single pass
  • Parallel processing: Numba JIT with prange for multi-core utilization
  • fastmath optimization: Aggressive floating-point optimizations on all kernels

API Reference

GSDataPro

Main data class extending GSData with processing methods.

from gsmod import GSDataPro

# Loading
data = GSDataPro.from_ply("scene.ply")
data = GSDataPro.from_gsdata(gsdata)

# Methods (all return self for chaining)
data.color(values: ColorValues, inplace: bool = True) -> GSDataPro
data.filter(values: FilterValues, inplace: bool = True) -> GSDataPro
data.transform(values: TransformValues, inplace: bool = True) -> GSDataPro
data.clone() -> GSDataPro

# Saving
data.to_ply("output.ply")

GSTensorPro (GPU)

GPU tensor class with same interface.

from gsmod.torch import GSTensorPro

# Loading
data = GSTensorPro.from_ply("scene.ply", device="cuda")
data = GSTensorPro.from_gsdata(gsdata, device="cuda")

# Same methods as GSDataPro
data.color(values, inplace=True)
data.filter(values, inplace=True)
data.transform(values, inplace=True)

# Saving
data.to_ply("output.ply")
output = data.to_gsdata()

Config Value Classes

ColorValues - Color adjustment parameters

ColorValues(
    brightness=1.0, contrast=1.0, gamma=1.0, saturation=1.0,
    vibrance=1.0, temperature=0.0, tint=0.0, shadows=0.0, highlights=0.0,
    fade=0.0, hue_shift=0.0, shadow_tint_hue=0.0, shadow_tint_sat=0.0,
    highlight_tint_hue=0.0, highlight_tint_sat=0.0
)
ColorValues.from_k(kelvin)  # From color temperature

FilterValues - Filter parameters

FilterValues(
    min_opacity=0.0, max_opacity=1.0, min_scale=0.0, max_scale=inf,
    sphere_radius=inf, sphere_center=(0,0,0), box_min=None, box_max=None
)

TransformValues - Transform parameters

TransformValues(scale=1.0, rotation=(1,0,0,0), translation=(0,0,0))
TransformValues.from_scale(factor)
TransformValues.from_translation(x, y, z)
TransformValues.from_rotation_euler(roll, pitch, yaw)  # degrees
TransformValues.from_euler_rad(roll, pitch, yaw)
TransformValues.from_rotation_axis_angle(axis, angle)  # degrees
TransformValues.from_axis_angle_rad(axis, angle)

Built-in Presets

Color Presets:

  • WARM, COOL, NEUTRAL
  • CINEMATIC, VIBRANT, MUTED, DRAMATIC
  • VINTAGE, GOLDEN_HOUR, MOONLIGHT

Filter Presets:

  • STRICT_FILTER - min_opacity=0.5, max_scale=1.0, sphere_radius=10.0
  • QUALITY_FILTER - min_opacity=0.3, max_scale=2.0
  • CLEANUP_FILTER - min_opacity=0.1, max_scale=5.0

Transform Presets:

  • DOUBLE_SIZE, HALF_SIZE
  • FLIP_X, FLIP_Y, FLIP_Z

Preset Loading Functions

from gsmod.config.presets import (
    get_color_preset, get_filter_preset, get_transform_preset,
    color_from_dict, filter_from_dict, transform_from_dict,
    load_color_json, load_filter_json, load_transform_json,
    save_color_json, save_filter_json, save_transform_json,
)

Legacy Classes (Still Available)

For advanced use cases like mask computation:

from gsmod import Pipeline, Color, Transform, Filter, FilterMasks

Low-Level Utilities

Quaternion operations:

from gsmod.transforms import (
    quaternion_multiply,
    quaternion_to_rotation_matrix,
    rotation_matrix_to_quaternion,
    axis_angle_to_quaternion,
    euler_to_quaternion,
    quaternion_to_euler
)

Scene bounds:

from gsmod.filter.bounds import (
    calculate_scene_bounds,        # -> SceneBounds
    calculate_recommended_max_scale  # -> float
)

bounds = calculate_scene_bounds(positions)
# Returns: SceneBounds(min, max, sizes, max_size, center)

max_scale = calculate_recommended_max_scale(scales, percentile=99.5)

Rotation methods: from_rotation_euler(), from_euler_rad(), from_rotation_axis_angle(), from_axis_angle_rad()

Scene Composition

Functions for combining and manipulating multiple Gaussian scenes:

from gsmod import (
    concatenate,
    compose_with_transforms,
    deduplicate,
    merge_scenes,
    split_by_region
)

# Load multiple scenes
scene1 = GSDataPro.from_ply("scene1.ply")
scene2 = GSDataPro.from_ply("scene2.ply")

# Simple concatenation
combined = concatenate([scene1, scene2])

# Compose with transforms (position scenes in space)
transforms = [
    TransformValues.from_translation(-2, 0, 0),
    TransformValues.from_translation(2, 0, 0),
]
composed = compose_with_transforms([scene1, scene2], transforms)

# Remove duplicate Gaussians
cleaned = deduplicate(combined, threshold=0.01)

# Split by spatial region
left, right = split_by_region(combined, axis=0, threshold=0.0)

Parameterized Templates

Create reusable pipeline templates with named parameters for efficient parameter sweeps and animation:

from gsmod import Color, Param

# Create template with named parameters
template = Color.template(
    brightness=Param("b", default=1.2, range=(0.5, 2.0)),
    contrast=Param("c", default=1.1, range=(0.5, 2.0)),
    saturation=Param("s", default=1.3, range=(0.0, 3.0))
)

# Use with different parameters (auto-cached for performance)
result1 = template(data, params={"b": 1.5, "c": 1.2, "s": 1.4})
result2 = template(data, params={"b": 0.8, "c": 1.0, "s": 1.0})

# Animation use case - cached LUTs for efficiency
import numpy as np
for t in np.linspace(0, 1, 100):
    brightness = 1.0 + t * 1.0  # Animate 1.0 to 2.0
    result = template(data, params={"b": brightness})

Learnable Modules (Training)

PyTorch nn.Module classes for gradient-based optimization:

from gsmod.torch import GSTensorPro, LearnableColor, LearnableTransform, LearnableFilter
from gsmod import ColorValues
import torch

# Create learnable module from initial values
initial = ColorValues(brightness=1.0, contrast=1.0, saturation=1.0)
learnable = initial.learn("brightness", "saturation")

# Or create directly
learnable = LearnableColor(brightness=True, contrast=False, saturation=True)

# Use in training loop
optimizer = torch.optim.Adam(learnable.parameters(), lr=0.01)

for epoch in range(100):
    result = learnable(data.sh0)
    loss = compute_loss(result, target)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# Extract learned values
learned_values = learnable.to_values()

PipelineGPU (Unified GPU Pipeline)

Chain all operations in a single fluent GPU pipeline:

from gsmod.torch import GSTensorPro, PipelineGPU

# Load data to GPU
data = GSTensorPro.from_ply("scene.ply", device="cuda")

# Create unified pipeline
pipeline = (
    PipelineGPU()
    .min_opacity(0.1)
    .within_sphere(radius=5.0)
    .translate([1, 0, 0])
    .rotate_euler(0, 45, 0)
    .scale(2.0)
    .brightness(1.2)
    .saturation(1.3)
)

# Apply all operations
result = pipeline(data, inplace=True)

Example: Full Processing Pipeline

from gsmod import GSDataPro, ColorValues, FilterValues, TransformValues
from gsmod import CINEMATIC

# Load Gaussian splatting data
data = GSDataPro.from_ply("scene.ply")

# Apply full processing pipeline
(data
    # Filtering (remove unwanted Gaussians)
    .filter(FilterValues(
        min_opacity=0.1,        # Remove low-opacity
        max_scale=2.5,          # Remove large-scale outliers
        sphere_radius=5.0       # Absolute radius in world units
    ))
    # Geometric transforms
    .transform(TransformValues.from_translation(1.0, 0.0, 0.0))
    .transform(TransformValues.from_rotation_euler(0, 45, 0))
    .transform(TransformValues.from_scale(1.5))
    # Color grading
    .color(ColorValues(
        temperature=0.6,        # Cool tones
        brightness=1.2,         # Increase brightness
        contrast=1.1,           # Boost contrast
        saturation=1.3          # Vibrant colors
    ))
)

# Save processed scene
data.to_ply("output.ply")

# Performance notes:
# - inplace=True (default): Zero-copy modification for maximum performance
# - Filtering: 62M Gaussians/sec full pipeline
# - Transforms: 698M Gaussians/sec combined operations
# - Colors: 1,389M colors/sec with LUT-based processing

Development

Setup

# Clone repository
git clone https://github.com/OpsiClear/gsmod.git
cd gsmod

# Install in development mode
pip install -e .[dev]

# Set up pre-commit hooks (recommended)
pre-commit install

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ -v --cov=gsmod --cov-report=html

Pre-commit Hooks

This project uses pre-commit to maintain code quality:

# Install hooks (one-time setup)
pre-commit install

# Run manually on all files
pre-commit run --all-files

# Update hook versions
pre-commit autoupdate

The pre-commit hooks will automatically:

  • Run ruff linting with auto-fix
  • Format code with ruff
  • Check for common issues (trailing whitespace, YAML syntax, etc.)
  • Validate Python syntax

See .github/PRE_COMMIT_SETUP.md for detailed setup instructions.

Project Structure

gsmod/
├── src/gsmod/
│   ├── __init__.py            # Public API
│   ├── pipeline.py            # Unified Pipeline class
│   ├── compose.py             # Scene composition
│   ├── params.py              # Parameterized pipelines
│   ├── protocols.py           # Protocol definitions
│   ├── validators.py          # Input validation
│   ├── constants.py           # Global constants
│   ├── utils.py               # Shared utilities
│   ├── color/                 # Color processing
│   │   ├── pipeline.py        # Color class
│   │   ├── presets.py         # ColorPreset class
│   │   └── kernels.py         # Numba kernels
│   ├── transform/             # 3D transformations
│   │   ├── api.py             # Quaternion utilities
│   │   ├── pipeline.py        # Transform class
│   │   └── kernels.py         # Numba kernels
│   ├── filter/                # Spatial filtering
│   │   ├── api.py             # Core implementation
│   │   ├── pipeline.py        # Filter class
│   │   ├── masks.py           # FilterMasks API
│   │   ├── bounds.py          # Scene bounds
│   │   ├── config.py          # FilterConfig
│   │   └── kernels.py         # Numba kernels
│   └── torch/                 # GPU operations
│       ├── gstensor_pro.py    # GSTensorPro class
│       ├── color.py           # ColorGPU
│       ├── transform.py       # TransformGPU
│       ├── filter.py          # FilterGPU
│       └── pipeline.py        # PipelineGPU
├── tests/                     # Unit tests
├── benchmarks/                # Performance benchmarks
├── docs/                      # Documentation
│   └── GPU_API_REFERENCE.md
├── .github/workflows/         # CI/CD
├── pyproject.toml             # Package config
└── README.md                  # This file

Benchmarking

Run performance benchmarks to measure library performance:

# Run all benchmarks
cd benchmarks
uv run run_all_benchmarks.py

# Run specific benchmark
uv run benchmark_color_lut.py

# Run large-scale benchmark (1M+ Gaussians)
uv run benchmark_large_scale.py

The benchmarks measure:

  • Color processing: LUT application across different batch sizes
  • Transform performance: Geometric operations on Gaussians
  • Filtering performance: Spatial and property-based filtering
  • Scalability: Performance across varying data sizes

Testing

gsmod has comprehensive test coverage with passing tests:

# Run all tests
pytest tests/ -v

# Run specific test file
pytest tests/test_color_pipeline.py -v

# Run with coverage report
pytest tests/ -v --cov=gsmod --cov-report=html

Test categories:

  • Color LUT operations (all adjustment types, caching, edge cases)
  • 3D transforms (translation, rotation, scaling, combined)
  • Quaternion utilities (conversions, multiplication)
  • Pipeline API (composition, presets, custom operations)
  • Filtering system (volume filters, property filters, combined logic)

Documentation

For detailed documentation see:

  • OPTIMIZATION_COMPLETE_SUMMARY.md - Complete optimization history and performance analysis
  • AUDIT_FIXES_SUMMARY.md - Bug fixes and validation methodology
  • .github/WORKFLOWS.md - CI/CD pipeline documentation
  • benchmarks/README.md - Benchmark suite documentation

CI/CD

gsmod includes a complete GitHub Actions CI/CD pipeline:

  • Multi-platform testing: Ubuntu, Windows, macOS
  • Multi-version testing: Python 3.10, 3.11, 3.12, 3.13
  • Automated benchmarking: Performance tracking on PRs
  • Build verification: Wheel building and installation testing
  • PyPI publishing: Automated release on GitHub Release

See .github/WORKFLOWS.md for details.


Architecture

Color Processing:

  • Phase 1: LUT-capable operations (temperature, brightness, contrast, gamma) pre-compiled into 1D LUTs
  • Phase 2: Dependent operations (saturation, shadows/highlights) with branchless code
  • Single fused Numba kernel with interleaved LUT layout for cache locality
  • Zero-copy API eliminates 80% memory allocation overhead

3D Transforms:

  • Matrix-based operations (scale, rotate, translate)
  • Numba-accelerated quaternion operations
  • Fused transforms for single-pass processing
  • Parallel processing with prange

Filtering System:

  • Fused opacity+scale kernel for 1.95x speedup
  • Parallel scatter pattern with prefix sum for lock-free writes
  • fastmath optimization on all kernels (5-10% speedup)
  • Numba JIT compilation with parallel execution

Contributing

Contributions are welcome! Please see .github/CONTRIBUTING.md for guidelines.

Quick start:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Run tests and benchmarks
  5. Submit a pull request

License

MIT License - see LICENSE file for details.


Citation

If you use gsmod in your research, please cite:

@software{gsmod2025,
  author = {OpsiClear},
  title = {gsmod: High-Performance Processing for 3D Gaussian Splatting},
  year = {2025},
  url = {https://github.com/OpsiClear/gsmod}
}

Related Projects

  • gsply: Ultra-fast Gaussian Splatting PLY I/O library (required dependency)
    • v0.3.0+ adds concatenation optimizations (6.15x faster bulk merging)
    • make_contiguous() for manual optimization of iterative workflows (100+ operations)
    • Multi-layer mask management (used by FilterMasks in gsmod)
    • See gsply documentation for details
  • gsplat: CUDA-accelerated Gaussian Splatting rasterizer
  • nerfstudio: NeRF training framework with Gaussian Splatting support
  • 3D Gaussian Splatting: Original paper and implementation

Made with Python and NumPy

Report Bug | Request Feature | Documentation

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gsmod-0.1.0.tar.gz (125.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gsmod-0.1.0-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file gsmod-0.1.0.tar.gz.

File metadata

  • Download URL: gsmod-0.1.0.tar.gz
  • Upload date:
  • Size: 125.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gsmod-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3be9c5cb54b56038a2ac40414a6314e0da3543b097f6fce61dc30509cb4ffe09
MD5 391643b9578c33275ff041e6b05362c6
BLAKE2b-256 31a8593910c76e424e83b65a523f075661bad24fc9b4c40e2d3c11eac4df0036

See more details on using hashes here.

Provenance

The following attestation bundles were made for gsmod-0.1.0.tar.gz:

Publisher: publish.yml on OpsiClear/gsmod

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gsmod-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gsmod-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gsmod-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5238c1cdb4d8bbb644763fc84ea70aac3170313f42dd7d435979a65591ec489c
MD5 604fde76fe0b8157963e8ad7933d0ccb
BLAKE2b-256 ace5ea36b8395ea2610dbfb83dddffd4283955af7e710f8b4711e615c35126ce

See more details on using hashes here.

Provenance

The following attestation bundles were made for gsmod-0.1.0-py3-none-any.whl:

Publisher: publish.yml on OpsiClear/gsmod

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page