CAGS: Color-Adaptive 3D Gaussian Splatting
Project description
CAGS: Color-Adaptive 3D Gaussian Splatting
(SIGGRAPH 2026) CAGS: Color-Adaptive Volumetric Video Streaming with Dynamic 3D Gaussian Splatting
This repo is the Python implementation of CAGS, a package for scalable compression and reconstruction of 3D Gaussian Splatting point clouds. It is built on top of gaussian-splatting, reduced-3dgs, ScalableVQ, and Google's Draco geometry codec.
CAGS converts Gaussian attributes into scalable base and enhancement layers, optionally compresses the base layer with Draco, splits large point clouds into spatial tiles, and stores only changed Gaussians for later frames in a dynamic sequence. The package provides command-line tools for single-frame quantization, tiled quantization, inter-frame encoding / decoding, progressive layer pickup, and rendering reconstructed results.
The paper also introduces PRPA (Post-Render Perspective Align), a post-render perspective alignment algorithm. Its implementation is maintained separately at PostRenderPerspectiveAlign.
Features
- Standard Python package with
pip installsupport - Scalable vector quantization for 3DGS attributes: position, rotation, opacity, scaling, DC colour and higher-order SH features
- Base-layer / enhancement-layer layout for progressive quality reconstruction
- Optional Draco compression for compact base-layer geometry storage
- Morton-order tiling and tile stitching for large Gaussian point clouds
- Inter-frame coding modes for dynamic sequences: full frame, quantized-difference and attribute-threshold difference
- Standalone CLI modules for
quantize,tile,encode,decode,pickup, andrender
Install
Prerequisites
- Python >= 3.10
- PyTorch (>= v2.4 recommended)
- CUDA Toolkit (12.4 recommended, match with PyTorch version)
- CMake and a C++ compiler for building bundled Draco tools
ScalableVQgaussian-splattingreduced-3dgs
Install dependencies that are not declared by the package metadata:
pip install wheel setuptools
pip install --upgrade git+https://github.com/yindaheng98/ScalableVQ.git@master
(Optional) If you have trouble with gaussian-splatting or reduced-3dgs, install them from source:
pip install --upgrade git+https://github.com/yindaheng98/gaussian-splatting.git@master --no-build-isolation
pip install --upgrade git+https://github.com/yindaheng98/reduced-3dgs.git@main --no-build-isolation
PyPI Install
pip install --upgrade ColorAdaptiveGaussianSplatting
or build the latest version from source:
pip install wheel setuptools
pip install --upgrade git+https://github.com/yindaheng98/ColorAdaptiveGaussianSplatting.git@main --no-build-isolation
Development Install
git clone --recursive https://github.com/yindaheng98/ColorAdaptiveGaussianSplatting.git
cd ColorAdaptiveGaussianSplatting
pip install tqdm plyfile scikit-learn numpy opencv-python
pip install --upgrade --target . --no-deps git+https://github.com/yindaheng98/ScalableVQ.git@master
pip install --upgrade --target . --no-deps git+https://github.com/yindaheng98/gaussian-splatting.git@master
pip install --upgrade --target . --no-deps git+https://github.com/yindaheng98/reduced-3dgs.git@main
pip install --upgrade --target . --no-deps .
The Draco encoder and decoder are built from the submodules/draco submodule during installation. Use git clone --recursive or run git submodule update --init --recursive before local builds.
Data Layout
CAGS reads and writes the same directory layout used by Gaussian Splatting:
scene_or_frame/
|-- cfg_args
|-- cameras.json
`-- point_cloud/
`-- iteration_30000/
`-- point_cloud.ply
For sequence coding, the initial frame is passed separately with --source_init, while the remaining frames are addressed by --source, --frame_format, --frame_start, and --frame_end.
sequence/
|-- frame0/
| `-- point_cloud/iteration_30000/point_cloud.ply
|-- frame1/
| `-- point_cloud/iteration_30000/point_cloud.ply
`-- frame2/
`-- point_cloud/iteration_30000/point_cloud.ply
Command-Line Usage
Quantize a Single Frame
python -m cags.quantize \
-s output/frame0 \
-d compressed/frame0 \
-i 30000 \
--draco \
-o "n_bit_baselayer=4" \
-o "n_bits_proposal=[2,2,2,2]"
This writes point_cloud_quantized.ply plus codebook and enhancement-layer sidecar files under compressed/frame0/point_cloud/iteration_30000/.
Dequantize a Single Frame
python -m cags.quantize \
-s compressed/frame0 \
-d compressed/frame0 \
-i 30000 \
--draco \
--dequantize
Dequantization loads point_cloud_quantized.ply from the destination directory and writes a reconstructed point_cloud.ply next to it.
Tile and Stitch a Frame
python -m cags.tile \
-s output/frame0 \
-d compressed_tiled/frame0 \
-i 30000 \
--draco \
-o "n_bit_baselayer=4" \
-o "n_bits_proposal=[2,2,2,2]"
To reconstruct tiled output:
python -m cags.tile \
-s compressed_tiled/frame0 \
-d compressed_tiled/frame0 \
-i 30000 \
--draco \
--stitching
Tiling uses MortonTiling by default, stores shared codebooks beside the base layer, and writes per-tile quantized files in a *_tiles/ directory.
Encode a Dynamic Sequence
python -m cags.encode \
--source_init output/frame0 \
--destination_init compressed/frame0 \
--iteration_init 30000 \
-s output/sequence \
-d compressed/sequence \
-i 30000 \
--frame_format "frame%d" \
--frame_start 1 \
--frame_end 30 \
--interframe interframe \
--draco \
-o "n_bit_baselayer=4" \
-o "n_bits_proposal=[2,2,2,2]"
The first frame initializes the codec state. Later frames are encoded as differences from the previous reconstructed frame and store a packed .mask.npz file indicating which Gaussians changed.
Available inter-frame modes:
none: encode every Gaussian in every framequantize: compare quantized attribute ids and encode changed Gaussiansinterframe: compare attributes with configurable thresholds and encode changed Gaussians
If you disable first-frame tiling with --no_tiling_first, also pass --no_tiling_rest.
Decode a Dynamic Sequence
python -m cags.decode \
--source_init compressed/frame0 \
--destination_init reconstructed/frame0 \
--iteration_init 30000 \
-s compressed/sequence \
-d reconstructed/sequence \
-i 30000 \
--frame_format "frame%d" \
--frame_start 1 \
--frame_end 30 \
--interframe interframe \
--draco
The decoder reconstructs a full point_cloud.ply for the initial frame and every requested sequence frame.
Pick Up Progressive Layers
python -m cags.pickup \
-s compressed/frame0 \
-d preview/frame0 \
-i 30000 \
--draco \
--pickup_sh_degree 1 \
-l rotation_re=0 \
-l rotation_im=0 \
-l opacity=1 \
-l scaling=1 \
-l features_dc=1 \
-l features_rest_0=0
pickup copies only selected enhancement layers and immediately reconstructs a preview point_cloud.ply. The layer dictionary must include every attribute key required by the selected SH degree.
Render Reconstructed Results
python -m cags.render \
-s data/frame0 \
-d reconstructed/frame0 \
-i 30000 \
--mode base \
--device cuda
Rendered RGB images, ground truth images, depth previews, depth arrays, and camera JSON files are saved under reconstructed/frame0/ours_30000/.
API Usage
Codec
from gaussian_splatting import GaussianModel
from cags.encode import prepare_codec
codec = prepare_codec(
draco=True,
tiling_first=True,
tiling_rest=True,
interframe="interframe",
n_bit_baselayer=4,
n_bits_proposal=[2, 2, 2, 2],
)
frame0 = GaussianModel(3).to("cuda")
frame0.load_ply("output/frame0/point_cloud/iteration_30000/point_cloud.ply")
codec.encode_init(frame0, "compressed/frame0/point_cloud/iteration_30000/point_cloud.ply")
frame1 = GaussianModel(3).to("cuda")
frame1.load_ply("output/frame1/point_cloud/iteration_30000/point_cloud.ply")
codec.encode_next(frame1, "compressed/frame1/point_cloud/iteration_30000/point_cloud.ply")
Codec keeps state between frames: the initial frame defines codebooks, tile order and inter-frame reference data; each following frame is encoded against the previous reconstructed frame.
Decode With the Codec
from gaussian_splatting import GaussianModel
from cags.encode import prepare_codec
codec = prepare_codec(
draco=True,
tiling_first=True,
tiling_rest=True,
interframe="interframe",
)
frame0 = codec.decode_init(
GaussianModel(3).to("cuda"),
"compressed/frame0/point_cloud/iteration_30000/point_cloud.ply",
)
frame0.save_ply("reconstructed/frame0/point_cloud/iteration_30000/point_cloud.ply")
frame1 = codec.decode_next(
GaussianModel(3).to("cuda"),
"compressed/frame1/point_cloud/iteration_30000/point_cloud.ply",
)
frame1.save_ply("reconstructed/frame1/point_cloud/iteration_30000/point_cloud.ply")
Direct Quantizer Usage
from gaussian_splatting import GaussianModel
from cags.quantization import DracoCompressedScalableQuantizer
gaussians = GaussianModel(3).to("cuda")
gaussians.load_ply("output/frame0/point_cloud/iteration_30000/point_cloud.ply")
quantizer = DracoCompressedScalableQuantizer(
n_bit_baselayer=4,
n_bits_proposal=[2, 2, 2, 2],
)
quantizer.save_quantized(
gaussians,
"compressed/frame0/point_cloud/iteration_30000/point_cloud_quantized.ply",
)
reconstructed = quantizer.load_quantized(
GaussianModel(3).to("cuda"),
"compressed/frame0/point_cloud/iteration_30000/point_cloud_quantized.ply",
)
Design: Scalable 3DGS Coding
The core abstraction separates attribute quantization, spatial tiling, and inter-frame extraction so they can be composed by Codec.
Scalable Quantizer
ScalableQuantizer extends the reduced-3DGS quantizer by converting each Gaussian attribute into a base layer and zero or more enhancement layers:
Gaussian attributes -> Vector quantization -> Base layer + enhancement layers
The base layer stores coarse ids and codebooks. Enhancement layers refine the same attributes progressively, which enables lower-bitrate preview reconstruction through pickup.
Draco-Compressed Base Layer
DracoCompressedScalableQuantizer stores base-layer codes in a temporary PLY, compresses them into .drc with the bundled Draco encoder, and restores them with the bundled decoder during loading. Enhancement layers remain .npz sidecar files.
Tiling
TillingScalableQuantizer wraps any scalable quantizer with a tiling strategy:
Gaussian point cloud -> Morton / average split tiles -> Per-tile quantization -> Stitching
MortonTiling sorts Gaussians by Morton code before splitting, keeping nearby points in the same tile. AverageSplitTiling is also used for changed Gaussians in inter-frame residuals.
Inter-Frame Extraction
The codec first encodes an initialization frame. For each later frame, an inter-frame extractor selects changed Gaussians and stores a packed mask:
Previous frame + current frame -> Difference mask -> Quantized changed Gaussians
NoInterframeExtractor always stores full frames, QuantizedInterframeExtractor compares quantized ids, and InterframeExtractor compares positions, rotations, opacity, scaling and SH features with configurable thresholds.
Extending: Adding a Custom Component
CAGS components are small Python classes. To add a new spatial partitioner, implement AbstractTiling.produce_tiling and pass it to TillingScalableQuantizer. To add a new inter-frame policy, implement AbstractInterframeExtractor.diff_mask and pass it to Codec.
from cags.codec import Codec
from cags.interframe import AbstractInterframeExtractor
from cags.quantization import ScalableQuantizer
from cags.tilequant import TillingScalableQuantizer
from cags.tiling import MortonTiling
class MyInterframeExtractor(AbstractInterframeExtractor):
def diff_mask(self, frame):
# Return a bool tensor with one entry per Gaussian.
...
codec = Codec(
frame_extractor=MyInterframeExtractor(),
frame_quantizer=TillingScalableQuantizer(
ScalableQuantizer(n_bit_baselayer=4),
MortonTiling(),
),
tiling_first=True,
)
Acknowledgement
This repo is developed based on 3D Gaussian Splatting, gaussian-splatting (packaged), reduced-3dgs, ScalableVQ, and Draco. Many thanks to the authors for open-sourcing their codebases.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
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 coloradaptivegaussiansplatting-1.0.0-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: coloradaptivegaussiansplatting-1.0.0-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 481.7 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c9614b1a0ae20b12f550eea609236bbbd4952d3a9b8c518379c3f228f3c3d08e
|
|
| MD5 |
4293cda3855bbbbd8ddbbe37506000b3
|
|
| BLAKE2b-256 |
0aa2a1ff57eab04586f4d8909c2991e7c6cfa2e575d3365d810987252a9a632f
|
File details
Details for the file coloradaptivegaussiansplatting-1.0.0-cp312-cp312-macosx_10_13_universal2.whl.
File metadata
- Download URL: coloradaptivegaussiansplatting-1.0.0-cp312-cp312-macosx_10_13_universal2.whl
- Upload date:
- Size: 633.3 kB
- Tags: CPython 3.12, macOS 10.13+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3b0958b90853780737736bf615e60eae4ef95fe93aff73dcf167671ca7f2f9e
|
|
| MD5 |
c70a5aaf4497f4319749934cc1e25dec
|
|
| BLAKE2b-256 |
4febdf674b7f2970739f50dbc5d79380b565060878f9cc0248dc1504e67b5b33
|
File details
Details for the file coloradaptivegaussiansplatting-1.0.0-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: coloradaptivegaussiansplatting-1.0.0-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 481.7 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0be77aed938514e6d468a57414c07565829c03b883475abe405716c3904fd3c
|
|
| MD5 |
cd63630f8e9ff133e6bfdee138933faa
|
|
| BLAKE2b-256 |
bcccceec5922ad40da39117c545d9fadb26ed0c3720856e2bf4ea5aef4524102
|
File details
Details for the file coloradaptivegaussiansplatting-1.0.0-cp311-cp311-macosx_10_9_universal2.whl.
File metadata
- Download URL: coloradaptivegaussiansplatting-1.0.0-cp311-cp311-macosx_10_9_universal2.whl
- Upload date:
- Size: 633.3 kB
- Tags: CPython 3.11, macOS 10.9+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6eac436a9e3cf7a69ea6edc287ba7222e68bc82d49f2e5bbdb8246d4f317e0e4
|
|
| MD5 |
84b2c3844aad2680ef8f38881f0c3f98
|
|
| BLAKE2b-256 |
7306e1867d1e027013e5a2f150446b9704cbb6212b26e4be14965e811c7e9e3b
|
File details
Details for the file coloradaptivegaussiansplatting-1.0.0-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: coloradaptivegaussiansplatting-1.0.0-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 481.7 kB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1872e3372799e2f5d7d3fe7c5ecc08c283665629115eed08e30eba09567e52f
|
|
| MD5 |
9d2be3a3df5b1b8304a3878503fc8d13
|
|
| BLAKE2b-256 |
108e92d13a5b1e28219df90b983340fcd80f8e8d3377977c70a8191cc9e385ac
|
File details
Details for the file coloradaptivegaussiansplatting-1.0.0-cp310-cp310-macosx_10_9_universal2.whl.
File metadata
- Download URL: coloradaptivegaussiansplatting-1.0.0-cp310-cp310-macosx_10_9_universal2.whl
- Upload date:
- Size: 633.3 kB
- Tags: CPython 3.10, macOS 10.9+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b9249875b706a8272977641745bee42331ae97d5fbef9280537ea74f78203a6
|
|
| MD5 |
6930f151237a75853388ed98aadedd32
|
|
| BLAKE2b-256 |
af3bb1649de68179808884e1608fde72d21c81fba7bef05f89e4fb5d0ee033fe
|