Skip to content

shrink: smaller images where they sit, proven pixel for pixel

The problem

Lossless optimisers such as jpegtran and oxipng often take 5 to 20 percent off photos and screenshots with no visible change. Running one over a folder of originals means trusting it: a wrong flag, a buggy build or a lossy tool used by mistake, and the damage is silent and permanent. So most people never run it, or keep a second copy of everything, which defeats the point.

The method

Don't trust the optimiser. Check its work the way a viewer sees it.

  • Shrink to a temp copy beside the original. Same folder, so the final rename never crosses a disk.
  • Keep it only if it is smaller.
  • Decode both and compare. JPEG through djpeg -ppm, PNG through ImageMagick at 16 bits with alpha; the decoded bytes are hashed and must match exactly. A failed decode counts as a failed proof.
  • Check the original did not change while you worked (size, modified time, inode).
  • Copy dates and mode across, then rename over the original. Atomic: the file is the old one or the new one, never half of each.

Measuring is the default; nothing changes until --write. Because the proof checks the result rather than the tool, any shrink command plus any decode command is a rule, and a lossy one is caught rather than trusted. A JPEG re-saved at quality 30 is refused; jpegtran's output is accepted.

Sample

The proof and the swap, in Python:

a, b = decoded(decode_cmd, path), decoded(decode_cmd, tmp)   # SHA-256 of raw pixels
if a is None or a != b:
    return "failed"                   # the original stays
now = path.stat()
if (now.st_size, now.st_mtime_ns, now.st_ino) != (st.st_size, st.st_mtime_ns, st.st_ino):
    return "changed"                  # written to while we worked
shutil.copystat(path, tmp)            # dates and mode
os.replace(tmp, path)                 # atomic

Use it:

shrink photos/                 # measure: shrink and prove each file, change nothing
shrink --write photos/         # replace the proven, smaller files
shrink --rule .webp "cwebp -lossless -z 9 {in} -o {out}" "dwebp {in} -ppm -o -" photos/

Limits

  • It proves pixels, not bytes. The built-in rules keep all metadata; a rule that strips metadata still passes.
  • The proof is only as fine as the decoder's output, so PNGs decode at 16 bits.
  • Hard-linked files, symlinks and hidden folders are skipped.
  • On macOS, extended attributes and Finder tags are not copied across.

Code

methods/shrink/ in the methods repository: the script, a README and a test.