Repository navigation
volume-cartographer: volcodec_ng lossless zarr chunks, smaller and faster than vc-delta3d LPC on held-out CT - #2232
ChaseHendrick wants to merge 2 commits into
Conversation
… next to volcomp
volcodec_ng is a lossless codec for 128^3 uint8 chunks, the same atom
volcomp uses: every voxel decodes exactly as stored. It gets its own codec
name ("volcodec_ng", chunks start with the magic VCNG) instead of changing
how volcomp chunks are read; volcomp is untouched.
- libs/volcodec_ng: the codec as one header (MIT), lossless modes only,
plus a small C surface compiled once, like libs/volcomp. Integer-only
decoder; AVX2 kernels picked at run time on x86-64, plain C elsewhere,
same bytes either way.
- utils/volcodec_ng_codec: C++ shim mirroring utils/volcomp_codec.
- VcDataset: CompressorId::VolcodecNg from the zarr v2 compressor
{"id": "volcodec_ng"} or the v3 codec {"name": "volcodec_ng"}, in the
codec registry, sharded arrays and createZarrDataset (uint8, 128^3
chunks); the streaming fetcher keeps its chunks verbatim in the disk
cache as .volcodec_ng.
- python/CMakeLists.txt installs the two new libraries next to volcomp's.
- core/test/test_vcdataset_volcodec_ng: exact v2 round trips, a v3 shard
with the index at the end and a crc32c, constant and incompressible
chunks, the shim's guards, and the encoded bytes of five synthetic
chunks pinned so a format change fails the test.
|
@ChaseHendrick is attempting to deploy a commit to the scroll Team on Vercel. A member of the Team first needs to authorize it. |
The vendored header gains volcodec_ng's mode 6 and the encoder now writes it; the decoder reads modes 2, 3, 5 and 6. Mode 6 predicts each voxel from 40 of 82 causal neighbours (same row, three rows above, two previous slices) with per-chunk least-squares weights in 16-bit fixed point, and codes the residual with static rANS tables per activity class in 32 interleaved states. Still lossless and integer-only on decode; AVX2 on x86-64 (picked at run time), NEON on arm64, plain C elsewhere, same bytes from all of them. - libs/volcodec_ng/volcodec_ng_lossless.h regenerated with mode 6 (its stream starts with a version byte; another version is refused with "unsupported stream version", status -6). - volcodec_ng_lib: VOLCODEC_NG_LIB_ERR_VERSION; kernels may read "neon". - test_vcdataset_volcodec_ng: the five pinned chunks now code as mode 6 (new digest), and a stream of another version is refused. - README: how mode 6 codes a chunk; arm64 uses NEON.
|
Thanks. Before your latest changes, I would have said that we settled on the 0.2.0 LPC solution. But you iterated fast ;) Adding context modelling would have been the next thing I tried as well. 5-10% more compression + decoding speed benefits might be worthwhile integrating. Can you open up a PR against vc_delta3d instead, we won't create yet another compression branding and the latest changes are really an incremental improvement on the LPC idea? I'll have a look on Monday. |
|
Moved to vc-delta3d as bruniss asked: ScrollPrize/vc-delta3d#6 It's a new |
Thanks! It's up now. Yeah, it's an incremental step on your LPC, so it's a 3rd predictor next to Delta and LPC. It integrates some context modeling work. Anything that isn’t lossless 128^3 uint8 also falls back to exactly the LPC output. |
Draft only because I'm at villa's open PR limit; I'll mark it ready when a slot frees.
In one sentence: Adds volcodec_ng as a lossless zarr chunk codec in
VcDatasetand VC3D. On 12 held-out CT crops it made files 8.1 % smaller than vc-delta3d 0.2.0's LPC mode, and on one thread it decoded 1.18x and encoded 1.40x as fast, faster on every crop.No quality loss: volcodec_ng is lossless only. Every voxel of every crop came back byte for byte.
Motivation:
vc-delta3d LPC is villa's best lossless codec on CT. When I opened this PR, volcodec_ng only beat it on write speed, and its files were about 30 % larger. I kept working on the coder until it beat LPC on size too, without giving up speed. I fixed the test before picking the crops: smaller files than LPC, and encode and decode each at least as fast as LPC, one thread, same machine, on crops nobody had tuned on.
One real example: A 256³ crop of PHerc0500P2 (open data volume 20250821110041, 0.55 µm, origin z 4096, y 4352, x 1536) is 16.8 MB raw. vc-delta3d LPC stores it in 4.16 MB and volcodec_ng in 3.90 MB. Both decode back identical to the source.
The same crop, slice by slice: the original, the slice after a volcodec_ng round trip, and their difference.
Before: For lossless chunks villa has vc-delta3d (delta by default, LPC since 0.2.0), volcomp at q 0, and the general byte compressors (Blosc, zstd, lz4, about 1.0 to 1.6x on CT).
VcDatasetreads vc-delta3d arrays; they're written byscripts/recompress_zarr.pyor the Python package, and VC3D's chunk cache writes delta.After this PR: A zarr v2 array with
"compressor": {"id": "volcodec_ng"}, or a v3 array with the codec{"name": "volcodec_ng"}(usually insidesharding_indexedwith 128³ inner chunks), opens, reads and writes throughVcDataset, so VC3D and the tools built on it can use it.createZarrDataset(..., "volcodec_ng", ...)makes a new one (uint8, 128³ chunks). The streaming fetcher keeps its chunks verbatim in the disk cache as.volcodec_ng. volcomp and vc-delta3d are untouched.Proof: 12 held-out 256³ crops from 10 public scans, 3 each at 0.55, 1.13, 2.4 and 8.64 to 9.36 µm. A fixed rule picked them after the codec was frozen, away from every crop used in development. Data: Vesuvius Challenge open data, CC BY-NC 4.0.
Speeds are MB/s of raw voxels on one thread, pinned to one core of an AMD EPYC 7R13 (AVX2), gcc 13.3. Both codecs were timed in the same process, interleaved, best of 9 per crop. LPC timed twice agreed with itself to within 1 %. vc-delta3d was built from its v0.2.0 tag with its own CMake (Release), as villa fetches it. By resolution, volcodec_ng gets 4.26x, 6.23x, 4.32x and 2.88x at 0.55, 1.13, 2.4 and 8.64 to 9.36 µm, and LPC 3.99x, 5.54x, 3.98x and 2.65x.
Why / where this is useful: Anywhere villa stores uint8 CT losslessly. Recompressing or uploading volumes, writing volumes from C++ tools through
VcDataset, and VC3D's chunk cache all get smaller files without slower reads, at least on x86-64 machines with AVX2.How it differs
How I tested it
e1770003on main131d9ac(it also merges cleanly ontoe2254eb): a minimal volume-cartographer build with GCC 13.3 on Ubuntu 24.04, and its 4 codec tests pass (test_vcdataset_volcodec_ngwith 5 cases, plus volcomp, shard index and compressors). I haven't built the full villa tree, macOS or Windows.Limits
volcodec_ngwith no new config key. New writes use this coder; arrays written with the earlier version of this PR still read.