Ultra-fast CPU processing for 3D Gaussian Splatting (color, transform, filter, opacity)
Project description
gsmod
Processing Tools for 3D Gaussian Splatting
Features | Installation | Quick Start | Performance | Documentation
Overview
gsmod provides processing operations for 3D Gaussian Splatting data: color grading, 3D transforms, spatial filtering, opacity adjustment, histogram-based learning, and learnable modules for auto-adjustment. Works with both CPU (NumPy) and GPU (PyTorch) backends with unified processing interface.
Included Features:
- Color Grading: 15 adjustments (brightness, contrast, saturation, temperature, tint, gamma, shadows, highlights, fade, hue shift, split toning)
- 39 built-in presets: Film stocks, seasonal, time of day, artistic styles
- Opacity Adjustment: Format-aware opacity scaling (fade/boost) for both PLY (logit) and linear formats
- 12 opacity presets: Fade, boost, and special effects
- Histogram Learning: Learn color adjustments to match target distributions
- Gradient-based learning with
HistogramResult.learn_from() - Rule-based suggestions with
to_color_values()
- Gradient-based learning with
- 3D Transforms: translate, rotate, scale with quaternion/euler/axis-angle support
- Spatial Filtering: sphere, box, ellipsoid, frustum volumes + opacity/scale thresholds
- Include/Exclude modes with
invertparameter
- Include/Exclude modes with
- Unified Processing: GaussianProcessor auto-dispatches between CPU and GPU backends
- Learnable Modules: PyTorch nn.Modules for gradient-based color/transform/filter/opacity optimization
- Composable Pipelines: Method chaining, built-in presets, JSON serialization
- Scene Composition: Concatenate, merge, deduplicate, split scenes
- Format-Aware: Automatic SH/RGB format tracking and conversion
Features
-
Color Grading: 15 adjustments for comprehensive color control
- temperature, tint, brightness, contrast, gamma, saturation, vibrance
- shadows, highlights, fade, hue_shift
- Split toning: shadow_tint, highlight_tint
- LUT-based processing with zero-copy API
-
Opacity Adjustment: Format-aware opacity scaling
- Works with both PLY (logit) and linear [0,1] opacity formats
- Fade (reduce opacity) and boost (increase opacity) operations
- Multiplicative composition for combining adjustments
- Factory methods:
OpacityValues.fade(),OpacityValues.boost()
-
Unified Processing: Single API for CPU and GPU
GaussianProcessorauto-dispatches based on input type- Works with GSData/GSDataPro (CPU) and GSTensor/GSTensorPro (GPU)
- Methods:
color(),transform(),filter(),opacity() - Batch processing with
process()method
-
3D Transforms: Geometric operations on Gaussian positions and orientations
- translate, rotate, scale, combined transforms
- Rotation formats: quaternion, matrix, axis_angle, euler
- Quaternion utilities for orientation manipulation
- Shared rotation utilities for CPU/GPU code reuse
-
Spatial Filtering: Volume and property-based selection
- Volume filters: sphere, box, ellipsoid, frustum
- Property filters: opacity and scale thresholds
- Composable with AND/OR/NOT operators
- Multi-layer mask management (FilterMasks API)
-
Auto-Correction: Industry-standard automatic color correction (NEW!)
auto_enhance(): Combined enhancement like iOS Photos Auto Enhanceauto_contrast(): Photoshop-style percentile stretching (0.1% clipping)auto_exposure(): 18% gray midtone targetingauto_white_balance(): Gray World / White Patch methods- Self-referential analysis (no target histogram required)
-
Histogram Learning: Match target color distributions
HistogramResult.learn_from(): Gradient-based learning APIto_color_values(): Rule-based adjustments (vibrant, dramatic, bright, dark, neutral)- Perceptual loss functions with contrast preservation
- GPU acceleration support
- Works with CPU or CUDA tensors
-
Learnable Modules: PyTorch nn.Modules for training
LearnableColor: Gradient-based color parameter optimizationLearnableOpacity: Learnable opacity adjustment with format awarenessLearnableTransform: Learnable geometric transformationsLearnableFilter: Differentiable filtering parameters- Convert to/from config values for inference
-
Scene Composition: Multi-scene operations
- Concatenate, merge, deduplicate scenes
- Compose with transforms (position scenes in space)
- Split by spatial region
-
Parameterized Templates: Reusable pipelines with named parameters
- Efficient for parameter sweeps and animation
- Auto-cached for repeated use
-
Pre-Activation Stage (via gsply): Prepare log-domain GSData
gsply.apply_pre_activations()for scales, opacities, quaternions- Works in-place with automatic dtype/contiguity fixes
-
Built-in Presets: Ready-to-use configurations
- Color (39 presets): cinematic, warm, cool, neutral, vibrant, muted, dramatic, vintage
- Film Stock: kodak_portra, fuji_velvia, kodak_ektachrome, ilford_hp5, cinestill_800t
- Seasonal: spring_fresh, summer_bright, autumn_warm, winter_cold
- Time of Day: sunrise, midday_sun, golden_hour, sunset, blue_hour, moonlight, overcast
- Artistic: high_key, low_key, teal_orange, bleach_bypass, cross_process, faded_print, sepia_tone
- Technical: lift_shadows, compress_highlights, increase_contrast, desaturate_mild, enhance_colors
- Opacity (12 presets): fade_subtle, fade_mild, fade_moderate, fade_heavy, boost_mild, boost_moderate, boost_strong, ghost_effect, translucent, semi_transparent
- Filter: strict, quality, cleanup
- Transform: double_size, half_size, flip_x/y/z
- Color (39 presets): cinematic, warm, cool, neutral, vibrant, muted, dramatic, vintage
-
GPU Support: All operations available on GPU via PyTorch
-
Pure Python: NumPy + Numba for CPU, PyTorch for GPU
-
Type-safe: Full type hints with Python 3.12+ syntax
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, OpacityValues
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.opacity(OpacityValues(scale=0.8)) # Fade to 80%
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, OpacityValues
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
))
# Opacity adjustments (format-aware: works with PLY logit and linear)
data.opacity(OpacityValues(scale=0.5)) # Fade to 50%
data.opacity(OpacityValues.fade(0.7)) # Factory method: fade to 70%
data.opacity(OpacityValues.boost(1.5)) # Boost opacity (move toward 1.0)
# Spatial filtering
# Include mode (default): keep only what matches
data.filter(FilterValues(
min_opacity=0.1,
max_scale=2.5,
sphere_radius=0.8,
sphere_center=(0, 0, 0)
))
# Exclude mode: remove what matches, keep everything outside
data.filter(FilterValues(
sphere_radius=5.0,
invert=True # Keep points OUTSIDE the sphere
))
# Chain multiple filters with different modes
data.filter(FilterValues(sphere_radius=3.0, invert=False)) # Keep inside outer sphere
data.filter(FilterValues(sphere_radius=1.5, invert=True)) # Remove inside inner sphere
# Result: Hollow shell between r=1.5 and r=3.0
# Complex filtering with method chaining
(data
.filter(FilterValues(sphere_radius=5.0, invert=False)) # Include: inside sphere
.filter(FilterValues(min_opacity=0.5, invert=False)) # Include: high opacity
.filter(FilterValues(box_min=(-1,-1,-1), box_max=(1,1,1), invert=True)) # Exclude: box
)
# 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)
Unified Processing (Auto-dispatch CPU/GPU)
from gsmod import GaussianProcessor, ColorValues, TransformValues, OpacityValues
from gsmod import GSDataPro
from gsmod.torch import GSTensorPro
# Create processor (works with both CPU and GPU data)
processor = GaussianProcessor()
# CPU processing
cpu_data = GSDataPro.from_ply("scene.ply")
cpu_data = processor.color(cpu_data, ColorValues(brightness=1.2))
cpu_data = processor.opacity(cpu_data, OpacityValues.fade(0.8))
# GPU processing (automatically detected)
gpu_data = GSTensorPro.from_ply("scene.ply", device="cuda")
gpu_data = processor.color(gpu_data, ColorValues(brightness=1.2))
gpu_data = processor.opacity(gpu_data, OpacityValues.fade(0.8))
# Batch processing with process() method
result = processor.process(
cpu_data,
color=ColorValues(brightness=1.2),
transform=TransformValues.from_scale(2.0),
opacity_values=OpacityValues.fade(0.8)
)
GPU Pipeline
from gsmod.torch import GSTensorPro
from gsmod import ColorValues, FilterValues, TransformValues, OpacityValues
# Load data to GPU
data = GSTensorPro.from_ply("scene.ply", device="cuda")
# Apply operations
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))
data.opacity(OpacityValues.fade(0.7))
# 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")
Unified Pipeline Class
The Pipeline class provides a fluent interface for building complex processing workflows. It matches the PipelineGPU interface for consistency:
from gsmod import Pipeline, GSDataPro
data = GSDataPro.from_ply("scene.ply")
# Build pipeline with method chaining
pipe = (Pipeline()
.brightness(1.2)
.saturation(1.3)
.contrast(1.1)
.translate([1, 0, 0])
.scale(2.0)
.min_opacity(0.1))
# Apply pipeline to data
result = pipe(data, inplace=True)
# Reuse pipeline on different data
data2 = GSDataPro.from_ply("scene2.ply")
result2 = pipe(data2, inplace=False)
The Pipeline class supports all color, transform, and filter operations as methods. Operations are accumulated and executed in order when the pipeline is called.
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.
Histogram-Based Learning
Learn color adjustments to match a target histogram distribution using gradient descent:
from gsmod import GSDataPro, ColorValues
import torch
# Step 1: Get target histogram from reference scene
reference = GSDataPro.from_ply("reference.ply")
target_hist = reference.histogram_colors()
# Step 2: Load source data
source = GSDataPro.from_ply("source.ply")
source_colors = torch.tensor(source.sh0) # Works with CPU or CUDA
# Step 3: Learn color adjustment to match target (CONVENIENT API!)
learned = target_hist.learn_from(
source_colors,
params=["brightness", "contrast", "saturation", "gamma"],
n_epochs=100,
lr=0.02,
verbose=True # Print progress
)
# Step 4: Apply learned values
source.color(learned)
source.to_ply("matched.ply")
Rule-based adjustments (quick heuristics without learning):
# Analyze histogram and suggest adjustments
result = data.histogram_colors()
adjustment = result.to_color_values("vibrant") # Boost saturation, contrast
# adjustment = result.to_color_values("dramatic") # Strong contrast, dark shadows
# adjustment = result.to_color_values("bright") # Shift distribution higher
# adjustment = result.to_color_values("dark") # Shift distribution lower
# adjustment = result.to_color_values("neutral") # Flatten toward uniform
data.color(adjustment)
GPU acceleration for histogram learning:
from gsmod.torch import GSTensorPro
# Load to GPU
source_gpu = GSTensorPro.from_ply("source.ply", device="cuda")
# Learn on GPU (faster)
learned = target_hist.learn_from(
source_gpu.sh0, # Already on CUDA
params=["brightness", "contrast"],
n_epochs=200,
lr=0.05
)
source_gpu.color(learned)
Auto-Correction (Photoshop/Lightroom Style)
Industry-standard automatic color correction without requiring a target histogram:
from gsmod import GSDataPro
from gsmod.color import auto_enhance, auto_contrast, auto_exposure, auto_white_balance
data = GSDataPro.from_ply("scene.ply")
# Quick auto-enhance (like iOS Photos Auto)
result = auto_enhance(data, strength=0.8)
data.color(result.to_color_values())
# Or use individual corrections:
# Auto Contrast - Photoshop style (0.1% percentile clipping)
contrast_result = auto_contrast(data, clip_percent=0.1)
data.color(contrast_result.to_color_values())
# Auto Exposure - targets 18% gray midtone
exposure_result = auto_exposure(data, target_midtone=0.45)
data.color(exposure_result.to_color_values())
# Auto White Balance - Gray World assumption
wb_result = auto_white_balance(data, method="gray_world")
print(f"Temperature: {wb_result.temperature}, Tint: {wb_result.tint}")
data.color(wb_result.to_color_values())
data.to_ply("auto_corrected.ply")
Auto-correction algorithms:
- auto_enhance: Combines exposure, contrast, and white balance (iOS Photos style)
- auto_contrast: Percentile-based histogram stretching (Photoshop Auto Contrast)
- auto_exposure: Adjusts to 18% gray midtone target (0.45 in gamma space)
- auto_white_balance: Gray World (avg=neutral) or White Patch (brightest=white)
Using Presets
from gsmod import GSDataPro, OpacityValues
from gsmod import (
# Color presets - Basic
WARM, COOL, NEUTRAL, CINEMATIC, VIBRANT, MUTED, DRAMATIC, VINTAGE, GOLDEN_HOUR, MOONLIGHT,
# Color presets - Film Stock
KODAK_PORTRA, FUJI_VELVIA, KODAK_EKTACHROME, ILFORD_HP5, CINESTILL_800T,
# Color presets - Seasonal
SPRING_FRESH, SUMMER_BRIGHT, AUTUMN_WARM, WINTER_COLD,
# Color presets - Time of Day
SUNRISE, MIDDAY_SUN, SUNSET, BLUE_HOUR, OVERCAST,
# Color presets - Artistic
HIGH_KEY, LOW_KEY, TEAL_ORANGE, BLEACH_BYPASS, CROSS_PROCESS, FADED_PRINT, SEPIA_TONE,
# Color presets - Technical
LIFT_SHADOWS, COMPRESS_HIGHLIGHTS, INCREASE_CONTRAST, DESATURATE_MILD, ENHANCE_COLORS,
# Opacity presets
FADE_MILD, FADE_MODERATE, BOOST_MILD, BOOST_MODERATE, GHOST_EFFECT, TRANSLUCENT,
# 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) # Classic cinematic look
data.color(WARM) # Warm tones
data.color(KODAK_PORTRA) # Film stock emulation
data.color(GOLDEN_HOUR) # Golden hour lighting
data.color(TEAL_ORANGE) # Popular teal/orange look
# Opacity presets
data.opacity(FADE_MODERATE) # Fade to 70%
data.opacity(GHOST_EFFECT) # Semi-transparent 20%
data.opacity(BOOST_MILD) # Boost opacity by 1.2x
# 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, get_opacity_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")) # Or "kodak_portra", "teal_orange", etc.
data.opacity(get_opacity_preset("fade_moderate")) # Or "ghost_effect", "boost_mild", etc.
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
OpacityValues Parameters
from gsmod import OpacityValues
OpacityValues(
scale=1.0 # Opacity scale factor
# 0.0-1.0: Multiplicative fade (reduce opacity)
# >1.0: Boost opacity (additive in remaining headroom)
)
# Factory methods
OpacityValues.fade(0.7) # Fade to 70% opacity
OpacityValues.boost(1.5) # Boost opacity by 1.5x
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)
invert=False, # Include (False) or Exclude (True) mode
)
# Include mode (invert=False, default): Keep only what matches
# Exclude mode (invert=True): Remove what matches, keep everything outside
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
-
CPU-GPU Consistency: All operations produce mathematically identical results
- Rotation matrices correctly use transpose for inverse transformations
- Filter operations (ellipsoid, box, frustum) match exactly between CPU and GPU
- Verified via comprehensive equivalence tests
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
prangefor multi-core utilization - fastmath optimization: Aggressive floating-point optimizations on all kernels
- Mathematically correct rotations: Rodrigues' formula implementation with proper axis normalization
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
OpacityValues - Opacity adjustment parameters
OpacityValues(scale=1.0)
OpacityValues.fade(0.7) # Fade to 70%
OpacityValues.boost(1.5) # Boost by 1.5x
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,
invert=False # Include (False) or Exclude (True) mode
)
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)
Histogram Analysis and Learning
Compute Histograms:
from gsmod import GSDataPro, HistogramConfig
data = GSDataPro.from_ply("scene.ply")
# Compute color histogram
hist = data.histogram_colors(HistogramConfig(n_bins=256))
print(f"Mean RGB: {hist.mean}")
print(f"Std RGB: {hist.std}")
# Analysis methods
hist.percentile(50, channel=0) # Median of red channel
hist.mode(channel=1) # Most frequent green value
hist.entropy(channel=2) # Blue channel entropy
hist.dynamic_range() # 99th - 1st percentile
Histogram-Based Learning:
import torch
# Learn color adjustments from target histogram
target_hist = reference.histogram_colors()
source_colors = torch.tensor(source.sh0)
learned = target_hist.learn_from(
source_colors,
params=["brightness", "contrast", "saturation", "gamma"],
n_epochs=100,
lr=0.02,
verbose=True
)
source.color(learned)
Rule-Based Adjustments:
# Generate ColorValues from histogram profile
hist = data.histogram_colors()
adjustment = hist.to_color_values("vibrant") # Boost saturation, contrast
# adjustment = hist.to_color_values("dramatic") # Strong contrast, dark shadows
# adjustment = hist.to_color_values("bright") # Shift distribution higher
# adjustment = hist.to_color_values("dark") # Shift distribution lower
# adjustment = hist.to_color_values("neutral") # Flatten toward uniform
data.color(adjustment)
Auto-Correction Functions
Industry-standard automatic color correction algorithms:
from gsmod.color import (
auto_enhance,
auto_contrast,
auto_exposure,
auto_white_balance,
compute_optimal_parameters,
AutoCorrectionResult,
)
auto_enhance(data, strength=1.0, preserve_warmth=True)
- Combined enhancement like iOS Photos Auto
- Blends exposure, contrast, and white balance
strength: How strong to apply (0-1)preserve_warmth: Keep some original warmth if True
auto_contrast(data, clip_percent=0.1, per_channel=False)
- Photoshop-style percentile stretching
clip_percent: Pixels to clip at each end (default 0.1%)per_channel: Stretch RGB independently (like Auto Levels)
auto_exposure(data, target_midtone=0.45, clip_percent=1.0)
- Targets 18% gray midtone (0.45 in gamma space)
target_midtone: Target for average luminanceclip_percent: Exclude extremes from calculation
auto_white_balance(data, clip_percent=1.0, method="gray_world")
method="gray_world": Average color should be neutral graymethod="white_patch": Brightest pixels should be white
compute_optimal_parameters(data, target_mean=None, target_std=None)
- Compute minimal adjustments to reach target statistics
- Preserves original character while matching targets
AutoCorrectionResult
- Dataclass with computed adjustments
- Convert to ColorValues:
result.to_color_values() - Fields: exposure, brightness, contrast, blacks, whites, temperature, tint, r_gain, g_gain, b_gain
Built-in Presets
Color Presets (39 total):
Basic Looks:
WARM,COOL,NEUTRALCINEMATIC,VIBRANT,MUTED,DRAMATICVINTAGE,GOLDEN_HOUR,MOONLIGHT
Film Stock Emulation:
KODAK_PORTRA,FUJI_VELVIA,KODAK_EKTACHROMEILFORD_HP5,CINESTILL_800T
Seasonal Looks:
SPRING_FRESH,SUMMER_BRIGHT,AUTUMN_WARM,WINTER_COLD
Time of Day:
SUNRISE,MIDDAY_SUN,SUNSET,BLUE_HOUR,OVERCAST
Artistic Styles:
HIGH_KEY,LOW_KEY,TEAL_ORANGEBLEACH_BYPASS,CROSS_PROCESS,FADED_PRINT,SEPIA_TONE
Technical Adjustments:
LIFT_SHADOWS,COMPRESS_HIGHLIGHTS,INCREASE_CONTRASTDESATURATE_MILD,ENHANCE_COLORS
Opacity Presets (12 total):
- Fade:
FADE_SUBTLE,FADE_MILD,FADE_MODERATE,FADE_HEAVY - Boost:
BOOST_MILD,BOOST_MODERATE,BOOST_STRONG - Special Effects:
GHOST_EFFECT,TRANSLUCENT,SEMI_TRANSPARENT
Filter Presets:
STRICT_FILTER- min_opacity=0.5, max_scale=1.0, sphere_radius=10.0QUALITY_FILTER- min_opacity=0.3, max_scale=2.0CLEANUP_FILTER- min_opacity=0.1, max_scale=5.0
Transform Presets:
DOUBLE_SIZE,HALF_SIZEFLIP_X,FLIP_Y,FLIP_Z
Preset Loading Functions
from gsmod.config.presets import (
get_color_preset, get_filter_preset, get_transform_preset, get_opacity_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,
)
# Load presets by name
color = get_color_preset("cinematic")
opacity = get_opacity_preset("fade_moderate")
filter_vals = get_filter_preset("strict")
transform = get_transform_preset("double_size")
Advanced Classes
For advanced use cases like mask computation and fine-grained control:
from gsmod import Pipeline, Color, Transform, Filter, FilterMasks
- Pipeline: Unified CPU pipeline (new in 0.1.3) - matches PipelineGPU interface
- Filter: Atomic filter with boolean operators (new in 0.1.3)
- Color, Transform: Legacy pipeline classes for backward compatibility
- FilterMasks: Multi-layer mask management
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:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run tests and benchmarks
- 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
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gsmod-0.1.4.tar.gz.
File metadata
- Download URL: gsmod-0.1.4.tar.gz
- Upload date:
- Size: 230.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bbcbd30fe5b4fe90f42ae07eec7df0dd39fd44e05790491967f71ad0ff4bfb2
|
|
| MD5 |
90811829923ee0da5c54b96306f830f2
|
|
| BLAKE2b-256 |
ff86c562b0ceb5701b415ca95c2c8c52f854713ece913d8cc975200147cd6d16
|
Provenance
The following attestation bundles were made for gsmod-0.1.4.tar.gz:
Publisher:
publish.yml on OpsiClear/gsmod
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gsmod-0.1.4.tar.gz -
Subject digest:
1bbcbd30fe5b4fe90f42ae07eec7df0dd39fd44e05790491967f71ad0ff4bfb2 - Sigstore transparency entry: 729042926
- Sigstore integration time:
-
Permalink:
OpsiClear/gsmod@3d4a25fc71b7d750755288d76ae2f0ef579b6967 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/OpsiClear
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3d4a25fc71b7d750755288d76ae2f0ef579b6967 -
Trigger Event:
release
-
Statement type:
File details
Details for the file gsmod-0.1.4-py3-none-any.whl.
File metadata
- Download URL: gsmod-0.1.4-py3-none-any.whl
- Upload date:
- Size: 176.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a050b3bcd77e29f96800673e0c89cad0e386e90dd88831f62ca2513dfead732
|
|
| MD5 |
98869c77af9c05f3213aa502fda38328
|
|
| BLAKE2b-256 |
801d8b81f6f8e31f02dfe65a3905af6e86292fe1f9d44c0d036ad93a00c2663e
|
Provenance
The following attestation bundles were made for gsmod-0.1.4-py3-none-any.whl:
Publisher:
publish.yml on OpsiClear/gsmod
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gsmod-0.1.4-py3-none-any.whl -
Subject digest:
9a050b3bcd77e29f96800673e0c89cad0e386e90dd88831f62ca2513dfead732 - Sigstore transparency entry: 729042941
- Sigstore integration time:
-
Permalink:
OpsiClear/gsmod@3d4a25fc71b7d750755288d76ae2f0ef579b6967 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/OpsiClear
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3d4a25fc71b7d750755288d76ae2f0ef579b6967 -
Trigger Event:
release
-
Statement type: