Skip to main content

CAGS: Color-Adaptive 3D Gaussian Splatting

Project description

CAGS: Color-Adaptive 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 install support
  • 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, and render

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

Runtime dependencies, including ScalableVQ, gaussian-splatting, and reduced-3dgs, are declared by the package metadata. For source builds, make sure the basic build tools are available:

pip install wheel setuptools

(Optional) If you need unreleased source versions of those packages, install them before installing CAGS:

pip install --upgrade git+https://github.com/yindaheng98/ScalableVQ.git@master
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 frame
  • quantize: compare quantized attribute ids and encode changed Gaussians
  • interframe: 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


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

coloradaptivegaussiansplatting-1.0.1-cp312-cp312-win_amd64.whl (478.4 kB view details)

Uploaded CPython 3.12Windows x86-64

coloradaptivegaussiansplatting-1.0.1-cp312-cp312-macosx_10_13_universal2.whl (652.2 kB view details)

Uploaded CPython 3.12macOS 10.13+ universal2 (ARM64, x86-64)

coloradaptivegaussiansplatting-1.0.1-cp311-cp311-win_amd64.whl (478.4 kB view details)

Uploaded CPython 3.11Windows x86-64

coloradaptivegaussiansplatting-1.0.1-cp311-cp311-macosx_10_9_universal2.whl (652.2 kB view details)

Uploaded CPython 3.11macOS 10.9+ universal2 (ARM64, x86-64)

coloradaptivegaussiansplatting-1.0.1-cp310-cp310-win_amd64.whl (478.4 kB view details)

Uploaded CPython 3.10Windows x86-64

coloradaptivegaussiansplatting-1.0.1-cp310-cp310-macosx_10_9_universal2.whl (652.2 kB view details)

Uploaded CPython 3.10macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file coloradaptivegaussiansplatting-1.0.1-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for coloradaptivegaussiansplatting-1.0.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d38de153312d57ebec1fd329043e4b5d6500299b07af41c2568b1a2b0c104ee7
MD5 7e67e95f35ed856c9334d759a71a542b
BLAKE2b-256 eb6aa62bd7c176c72c9db64307d224bfcd4d0838d8baab1e6c6aea50a22076bc

See more details on using hashes here.

File details

Details for the file coloradaptivegaussiansplatting-1.0.1-cp312-cp312-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for coloradaptivegaussiansplatting-1.0.1-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 15e75581ed2b821e386e084258c8c9e456addb75071959850a45a2e15129d338
MD5 507644abf810638e73bb4767ae34e86b
BLAKE2b-256 5022f62eee27af0ca326c5852e76fa31c5e0ad793142067015bf70c7f04ed070

See more details on using hashes here.

File details

Details for the file coloradaptivegaussiansplatting-1.0.1-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for coloradaptivegaussiansplatting-1.0.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 e923a9fea01147e32b506462e6c7255544e47d846f3cc6e71004232135064382
MD5 cccc71892544f0749ee3058a50132932
BLAKE2b-256 1247f217799b817f0f2517fe3bd76e26300d9ada8c21cb4df240859206c9e709

See more details on using hashes here.

File details

Details for the file coloradaptivegaussiansplatting-1.0.1-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for coloradaptivegaussiansplatting-1.0.1-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 e3a9f1f37f1518117191bdc5edca98acb411dd8a001234fdb17b8900244d68f6
MD5 a90588267dcf7e0e663d0b948974ad85
BLAKE2b-256 3b537c5fb7fb5b6d00eff0e0ccca2a14170a49cc105a68cff5c030c0b0b23f90

See more details on using hashes here.

File details

Details for the file coloradaptivegaussiansplatting-1.0.1-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for coloradaptivegaussiansplatting-1.0.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 8b3c5904ffb4f5bf26014ffbaf8992ff5ed1921f0b889d80daa5f24a9d8dafe7
MD5 67aa77bc2d1d293cf7297ad54021b350
BLAKE2b-256 7a04c7c3699c93992be1c25851b68bb30b53d474a827ad7ed4f083a0b61c6d47

See more details on using hashes here.

File details

Details for the file coloradaptivegaussiansplatting-1.0.1-cp310-cp310-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for coloradaptivegaussiansplatting-1.0.1-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 94f8cea8aeb0639add01f83aa4bb1486acd7c6a24f903c15e5ebd4cbccb1317e
MD5 cddf88b5a98ade313ee068dc44f21136
BLAKE2b-256 59a6f5e9506764ba665b4620ae6764982cde2ea3c8ad09c664913a82aad981a4

See more details on using hashes here.

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