8000 GitHub - vclab/atlas-hsi · GitHub
[go: up one dir, main page]

Skip to content
vclabPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ATLAS

ATLAS (Adaptive Transfer of Low-rank Abundance Splatting) compresses hyperspectral images (HSI) with low-rank spectral Gaussian splatting.

It first decomposes a data cube with NMF into a small set of endmember spectra and low-rank abundance maps, then represents those abundance maps as a compact set of optimizable 2D Gaussians. Each Gaussian stores only k abundance coefficients (k is about 10-12) rather than the full l spectral bands, which is what makes Gaussian splatting possible in HSI. The trained representation is itself the compressed file for that cube.

ATLAS builds on LoR-SGS (IEEE TGRS 2025) and ports the GaussianImage++ (AAAI 2026) techniques that transfer to the HSI domain: distortion-driven densification, two-stage LSQ+ quantization, learnable endmembers, and gradient-guided initialization. It drops the ones that do not (content-aware filters, position and progressive quantization, spatial delta coding). On four benchmarks it beats published LoR-SGS on 6 of 8 rate-distortion targets, by +1.20 dB PSNR on average.

ATLAS is a per-image representation, like an INR. You train it on the cube you want to compress, and there is no pretrained model to download.

How it works

The ATLAS pipeline: (a) NMF initialization, (b) training and reconstruction with 2D Gaussian splatting, (c) two-stage QAT and entropy coding

The pipeline figure from the paper. NEW marks the techniques ATLAS adds to LoR-SGS, and sections (a) to (c) match the three steps below.

  1. Decompose. NMF factors the cube X ≈ A·E into abundance maps A and endmembers E (rank k ≪ l).
  2. Splat. Fit N 2D Gaussians (position, Cholesky covariance, k abundances) to reconstruct A. D3 grows N by placing Gaussians at high-error pixels.
  3. Quantize and encode. Two-stage QAT applies LSQ+ (FP16 positions, signed Cholesky, unsigned abundances), degenerate Gaussians are pruned, and parameters are entropy-coded.

Installation

ATLAS needs an NVIDIA GPU and PyTorch 2.10 or newer built with CUDA 12 or 13.

# 1. PyTorch built for your CUDA version (here CUDA 13.0; use cu128 for CUDA 12.8)
pip install torch --index-url https://download.pytorch.org/whl/cu130

# 2. ATLAS
pip install atlas-hsi
# optional extras:
pip install "atlas-hsi[viz]"    # matplotlib plotting helpers
pip install "atlas-hsi[demo]"   # Streamlit demo app

On Linux (x86_64) and Windows, the package includes the CUDA kernel prebuilt for CUDA 12 and 13, so nothing compiles and you do not need a CUDA toolkit. The prebuilt kernel runs on GPUs from the RTX 20 series (compute capability 7.5) onward, and with CUDA 12 also on V100 (7.0). We tested it with PyTorch built for CUDA 12.8 and 13.0.

On other platforms, or when you install from source, the kernel compiles the first time you train or decode (12 s on our desktop), and later runs reuse it. Compiling needs a CUDA toolkit (nvcc) with the same major version as PyTorch's CUDA, and on Windows also the MSVC compiler from Visual Studio Build Tools.

pip install "git+https://github.com/vclab/atlas-hsi"   # latest development version
pip install -e ".[viz,dev]"                            # local development, from a clone

Troubleshooting the install

If training cannot find CUDA, or the kernel will not load, run:

atlas doctor

It reports your PyTorch build and GPU, and whether a prebuilt kernel matches it. Without a prebuilt kernel it also checks the CUDA toolkit, ninja, and on Windows MSVC, and prints what is missing. It finds the toolkit through CUDA_HOME, nvcc on PATH, or the default install folders (/usr/local/cuda* on Linux). Add --build to load the kernel right away, compiling it first if needed, as a final check.

Quickstart

# 1. NMF initialization, once per dataset/rank. Writes data/init/*.npy
atlas init --input paviau --rank 12

# 2. Train (FP32). The checkpoint in atlas_runs/ is the compressed representation.
atlas train --input paviau --num_points 14500 --iterations 7000

# 3. Recommended pipeline: D3 densification, two-stage QAT, learnable endmembers, SAM
atlas train --input paviau --num_points 7250 --max_num_points 14500 \
  --grow_stop_iter 2000 --iterations 10500 --qat_start 5250 \
  --prune_interval 100 --endmember_lr 1e-4 --sam_weight 0.05 \
  --cholesky_bits 8
# A quantized (QAT) run also writes a compressed bitstream at <logdir>/compressed.atlas

# 4. Decompress a bitstream back into a cube (.npy). --reference reports reconstruction PSNR
atlas decompress atlas_runs/HSI/.../compressed.atlas -o recon.npy --reference paviau

You can also pass a path to your own cube instead of a dataset name:

atlas init  --input /path/to/cube.mat --rank 12
atlas train --input /path/to/cube.mat --num_points 8000 --iterations 7000

Python API

import atlas

cube = atlas.load_cube("paviau")                 # (H, W, C), normalized
E    = atlas.load_endmember("paviau", rank=12)   # (rank, channels)
A0   = atlas.load_abundance("paviau", rank=12)    # for gradient-guided init
gt   = atlas.to_tensor(cube)                      # (1, C, H, W)

result = atlas.train_nd(gt, endmember=E, image_name="paviau",
                        iterations=7000, num_points=14500, A0=A0)
print(result["psnr"], result["bpppb"])

Visualization ([viz] extra)

The optional [viz] extra adds matplotlib helpers under atlas.viz. They operate on plain numpy arrays and need no GPU, so you can inspect cubes, abundance maps, and endmembers on a machine without CUDA:

from atlas import viz

cube = atlas.load_cube("jasperridge")
E    = atlas.load_endmember("jasperridge", rank=10)
A0   = atlas.load_abundance("jasperridge", rank=10)   # flattened (H*W, k)
H, W = cube.shape[:2]

viz.show_pseudo_rgb(cube, show=True)                       # quick RGB preview
viz.show_abundance_maps(A0, shape=(H, W), show=True)       # grid of abundance maps
viz.plot_endmembers(E, show=True)                          # endmember spectra
viz.plot_spectra(cube, recon, n=3, show=True)              # orig vs recon signatures
viz.show_error_map(cube, recon, metric="sam", show=True)   # per-pixel error map
viz.plot_rd_curve({"ATLAS": [(0.10, 37.2), (0.06, 31.5)]}, show=True)  # RD curve

Every helper returns (fig, ax) so you can compose or save them; pass show=True to display immediately. show_abundance_maps accepts (H, W, k), (k, H, W), or flattened (H*W, k) (the NMF init layout, with shape=(H, W)).

CLI reference (atlas train)

Flag Default Description
--input / --dataset required Dataset name (urban/salinas/jasperridge/paviau) or .mat/.npy path
--iterations 8000 Training iterations
--num_points required Number of Gaussians (start count if D3 enabled)
--max_num_points =num_points D3: grow to this many Gaussians
--grow_stop_iter 5000 D3: stop densifying at this iter
--qat_start 0 Iteration to begin two-stage QAT (0 = FP32, no quantization)
--prune_interval 100 Prune degenerate Gaussians every N iters during QAT
--endmember_lr 0 Learnable-endmember LR (0 = frozen; 1e-4 recommended)
--sam_weight 0.0 SAM loss weight (0.05 recommended)
--rank per-dataset NMF rank (10 for JasperRidge, else 12)
--output-dir atlas_runs Where checkpoints and logs are written
--abundance_bits / --cholesky_bits 6 / 10 advanced: LSQ+ bit-widths (used under QAT)
--lsq-grad-scale off experimental: LSQ+ gradient scaling for the quantizer scale and offset (rate-distortion-neutral with the Adan optimizer)

Techniques the paper found do not transfer to HSI are not exposed: content-aware filters, spatial delta coding, and progressive QAT.

Compressed bitstream

A QAT run writes compressed.atlas next to its checkpoints. This is the compressed file for the cube: a binary container holding positions and the endmember as FP16, and the cholesky covariance and abundances as LSQ+ integer codes, ANS entropy-coded. atlas decompress reads it back and reconstructs the cube with the same forward pass, so the decoded result matches the trained model:

atlas decompress run/compressed.atlas -o recon.npy --reference paviau

The trainer's reported bpppb is the entropy payload of this file (the on-disk file adds a small fixed container header). For programmatic access, use atlas.codec.write_bitstream(model, path) and atlas.codec.decompress(path).

Datasets

Dataset Shape Rank Typical N Iterations
PaviaU 340x340x103 12 10000-23000 7000
Pavia Centre (paviac) 1096x715x102 12 36000-72000 12000
Salinas 180x180x204 12 5000 7000
JasperRidge 100x100x198 10 600-2750 8000
Urban 307x307x162 12 14500-29000 7000
WHU-Hi-HongHu (whu_honghu) 940x475x270 12 28000-56000 12000
Chikusei (chikusei) 2372x2196x128 16 80000-160000 10000

Place raw dataset files (.mat, .npy, or ENVI .bsq/.hdr) in data/raw/; atlas init writes NMF outputs to data/init/. Override with --data-dir and --init-dir. Chikusei is auto-cropped to its largest all-valid region; the whole cube trains in VRAM (no tiling), so it needs a large-VRAM GPU (~24 GB) and the self-test skips it on smaller cards.

Testing

I had an LLM write all the test cases, the suite checks the codec, data loading, atlas doctor, the plots, and the CUDA kernel. The self-test trains, compresses, and decodes every dataset you have downloaded, and checks the PSNR and rate against recorded values. Run both from a clone of the repository:

pytest tests/ -m "not gpu"           # CPU tests only
python tests/selftest.py --quick     # every layer, with a short training schedule

tests/README.md lists the test cases and flags, and explains how to add a dataset.

Reproducing the paper

Appendix B of the paper lists the training configuration behind each rate-distortion target. The self-test (python tests/selftest.py) trains one operating point per dataset, including the three larger scenes of the scaling study, and checks it against recorded values in tests/golden.json. The downstream classification study (Appendix G) has its own folder, reproduce/downstream/, with the scripts, the full command list, and the per-sample records behind every number in its table.

Citation

@inproceedings{rempel2026atlas,
  title     = {{ATLAS}: Hyperspectral Image Compression via Adaptive Transfer of Low-rank Abundance Splatting},
  author    = {Rempel, Jacob and Qureshi, Faisal Z.},
  booktitle = {NeurIPS 2026 Workshop on Advances in Representation Learning for Earth Observation (REO-2)},
  year      = {2026}
}

Built on LoR-SGS (Li et al., IEEE TGRS 2025), which builds on GaussianImage (Zhang et al., ECCV 2024), and ports techniques from GaussianImage++ (Li et al., AAAI 2026).

License

MIT (see LICENSE), which also carries the copyright notices of the LoR-SGS and GaussianImage code that ATLAS builds on. atlas/optimizer.py is the Adan optimizer (Garena Online), licensed under Apache-2.0 (see LICENSE-APACHE). The CUDA kernel in atlas/_kernel/ is Apache-2.0, derived from nerfstudio gsplat.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0