Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Tsiro

A single-file lossless image codec (.tso).

The one thing it does that other codecs don't: given only the decoded pixels of an image that used to be a JPEG, it recovers a quantized-DCT representation that reproduces those pixels exactly, and stores that instead. The original .jpg is not needed.

Everything is bit-for-bit lossless. If a mode cannot guarantee an exact reconstruction, it is not used.

What it does

Four paths, picked automatically from the input:

Input What Tsiro does Honest verdict
Pixels of an image that was once a JPEG (4:4:4 or 4:2:0) Recovers the DCT lattice from the pixels alone, then codes it This is the point of the project. 2-6× smaller than JPEG XL
A camera RAW (.dng, .cr2, .nef, .arw, .orf, …) De-interleaves the Bayer mosaic into 4 color planes, MED prediction, JPEG-LS gradient contexts, adaptive binary range coder Beats JPEG XL and JPEG 2000 on every sensor tested
A JPEG file (.jpg) Re-encodes its quantized DCT coefficients, one static rANS model per coefficient position, DC predicted with a 2D MED 18% smaller than the .jpg, but JPEG XL does this better. Included for completeness
Any other image Adaptive quadtree + MED predictor + wedgelet + context-adaptive rANS Beats PNG. Loses to WebP and JPEG XL. A fallback, not a contender

Results

Pixels that came out of a JPEG

NASA photo, 8256×5504, 4:4:4 source, losslessly compressing the decoded pixels:

Codec Size
PNG 20.27 MB
WebP lossless 14.18 MB
JPEG XL lossless (best effort) 13.63 MB
Tsiro 2.26 MB

Same effect on a clean source (a demosaiced RAW crop, never a JPEG before, re-encoded at several qualities, 1024×1024), for both chroma layouts:

JPEG quality JPEG XL Tsiro JPEG XL Tsiro
4:4:4 4:4:4 4:2:0 4:2:0
60 44 KB 20 KB 55 KB 15 KB
75 110 KB 29 KB 68 KB 23 KB
85 363 KB 75 KB 189 KB 51 KB
90 531 KB 94 KB 332 KB 58 KB
95 649 KB 262 KB 452 KB 205 KB
98 880 KB 608 KB 637 KB 550 KB

4:2:0 is what most JPEGs in the wild use. Recovering it requires inverting libjpeg's fancy chroma upsampling on top of everything else; Tsiro clones that bit-exactly too.

The comparison this invites, answered honestly. If you still have the .jpg file, JPEG XL's lossless JPEG transcoding beats Tsiro outright:

Size Reconstructs
original test.jpg 2.63 MB
JPEG XL (jpegxl_encode_jpeg) 1.94 MB the original file, byte for byte
Tsiro, JPEG-file mode 2.15 MB pixels + EXIF
Tsiro, from pixels only 2.26 MB pixels

So Tsiro is not the best JPEG recompressor. Lepton, Brunsli and JPEG XL solved that, better. Tsiro's contribution is that it needs no .jpg at all: it works from pixels, where every general-purpose codec falls back to 13+ MB.

Camera RAW (Bayer sensor mosaic)

Sony α7R V, 14-bit, 9566×6374 (121.95 MB raw) · download original ↗

Codec Size
JPEG XL lossless (effort 7) 76.11 MB
JPEG 2000 lossless 66.66 MB
Tsiro 62.84 MB

Olympus E-M5 Mark II, 16-bit high-res, 9274×6926 (128.46 MB raw) · download original ↗

Codec Size
JPEG XL lossless (effort 7) 43.66 MB
JPEG 2000 lossless 37.64 MB
Tsiro 36.12 MB

A Canon 14-bit DNG gives the same picture (19.19 MB vs 20.77 JPEG 2000, 23.86 JPEG XL). Across three cameras: 4-8% better than JPEG 2000, 17-20% better than JPEG XL. All bit-for-bit lossless. Sample RAWs are CC0 from raw.pixls.us.

Images that were never JPEGs

The generic fallback, on a 2048×2048 crop of a demosaiced Canon DNG:

Codec Size
PNG 4.50 MB
Tsiro 3.96 MB
WebP lossless 3.76 MB
JPEG XL lossless 3.60 MB

It loses. Stated plainly because it is the truth: the quadtree/MED pipeline is a reasonable predictive coder, not a JPEG XL competitor.

Known limitations

  • At extreme quality (q98+) the advantage shrinks: quantization steps near 1 carry almost as much information as the pixels themselves. Tsiro still beats JPEG XL there, but not by much.
  • The DCT recovery requires image dimensions that are multiples of 8 (multiples of 16 for 4:2:0). Otherwise it declines.
  • Other chroma layouts (4:2:2, 4:1:1) are not yet handled; 4:4:4 and 4:2:0 cover the vast majority of real files.
  • When it declines, output is still lossless. It just gets bigger.

Honest scope

  • The large factor applies only to pixels whose history includes a JPEG. It is a structural exploit, not a general compression advance. On a never-compressed image Tsiro loses to JPEG XL.
  • On camera RAW there is no such factor, and there cannot be. The low bits of sensor data are essentially random: bits 0 through 3 have a marginal frequency of 0.5000. Tsiro emits 6.85 bits per pixel on the Canon, and at least ~4 of those look irreducible. Every codec clusters near the same entropy floor. A percentage win there is the whole game.
  • None of the components are new: quadtrees, wedgelets, MED/LOCO-I prediction, ANS, JPEG-LS contexts, JPEG recompression. Recovering the DCT lattice from pixels alone is the part I have not found in a mainstream codec.
  • The wedgelet is nearly vestigial: on real photographs the rate-distortion decision picks it for about 0.1% of quadtree leaves. MED wins 96.7% of them.
  • Tsiro does not recover the original coefficients, only a quantized-DCT representation that decodes to exactly the input pixels. That is all losslessness requires.

If a tool promises multi-× lossless compression of a real RAW, it is either wrong or not lossless.

Usage

python tsiro.py photo.jpg          # JPEG recompression (from the .jpg)
python tsiro.py photo.jpg pixel    # force pixel mode (DCT-from-pixels recovery)
python tsiro.py image.png          # generic / ex-JPEG pixels
python tsiro.py shot.dng           # camera RAW (Bayer mosaic)

Each run compresses to <name>.tso, decompresses, and verifies the reconstruction is bit-for-bit identical before printing any number, alongside PNG / WebP / JPEG XL / JPEG 2000 baselines.

Programmatic API:

import tsiro, numpy as np

blob = tsiro.compress_pixels(img_rgb)      # np.uint8 HxWx3
img  = tsiro.decompress_any(blob)

blob = tsiro.compress_raw(mosaic_u16)      # np.uint16 HxW Bayer mosaic
mosaic = tsiro.decompress_raw(blob)

How it works

DCT recovery from pixels. Estimates the quantization tables from the pixel data alone: it first tries the standard IJG tables (quality 1-100, which cover most real files), validating each entry against the observed coefficient lattice, and falls back to a per-position lattice estimator (trimmed mean-squared distance to the nearest multiple, noise-floor corrected, largest lattice among near-ties) for custom tables. Coefficients that are always zero are forced to zero; anything unreliable falls back to a step of 1, which stores the coefficient exactly. Underestimating a step costs bits, never fidelity.

It clones libjpeg's integer IDCT (jidctint), its fixed-point YCbCr conversion, and its fancy h2v2 chroma upsampler bit-exactly in numba, and inverts the color transform by candidate search rather than by float rounding. For 4:2:0 sources it first inverts the chroma upsampling (iterative linear solve, then greedy integer polish against the exact integer upsampler). Then, per 8×8 block (per 16×16 MCU in 4:2:0), it flips borderline coefficients and re-decodes through the full chain until the region reproduces the exact input pixels. Whatever still mismatches is stored as an explicit patch list, so the result is bit-exact by construction rather than by convergence. If no lattice is found, the mode declines and the generic coder runs.

RAW. Bayer de-interleave into 4 planes, so the predictor never crosses colors. That single choice is why JPEG-LS scores 30 MB on the raw mosaic and 19 MB per-plane. Then MED prediction, sign-folded 3-gradient JPEG-LS contexts, median bias correction per context, and an adaptive binary range coder.

Entropy coders, both written from scratch:

  • A static byte-wise rANS, verified by exact roundtrip on randomized skewed distributions.
  • An adaptive binary range coder (LZMA-style, capped-unary magnitude class, bypass mantissa bits). On synthetic data it lands at 3.918 bits/symbol against an entropy of 3.884, about 0.9% above the floor, and it transmits no frequency tables.

Pixel mode. Reversible YCoCg-R color transform, adaptive quadtree, and a per-leaf rate-distortion choice between a flat mean, a 32-orientation wedgelet, and a MED predictor. Residuals are coded losslessly in all cases, so the choice affects size only, never fidelity.

Requirements

pip install numpy==2.2.6 numba pillow jpeglib rawpy imagecodecs

numpy, numba, pillow and jpeglib are required to import the module. rawpy is needed only for RAW files and imagecodecs only for the benchmark comparisons; both are imported lazily.

AI disclosure

Tsiro was designed and built by Daxlia with the assistance of AI coding tools. Every number in this README was measured on real files, not estimated. Every run enforces losslessness by a full bit-exact roundtrip, including odd image dimensions.

License

Apache License 2.0 (SPDX: Apache-2.0), see LICENSE and NOTICE.

Use it, modify it, ship it commercially. Apache 2.0 carries an explicit patent grant, which matters in a field with as much patent history as image compression.

Author

Daxlia, 2026.

About

Single-file lossless codec - up to 6× smaller than JPEG XL on JPEG-sourced pixels, and beats JPEG XL / JPEG 2000 on camera RAW.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages