Lossless Image File Formats

This was originally a two-part post on cohost.

Recently I needed to convert a large TIFF scan of a duochrome page into something reasonable, i.e. a web-supported image format that was still lossless since it seemed a shame to ruin such a nice high-definition scan with lossy compression. In terms of lossless formats, all browsers1 support PNG, WEBP, and AVIF, while I really hope JXL support is imminent.

I therefore wanted to see which file format would perform the best in terms of file size by converting my ~183 MiB TIFF to each of them using ImageMagick. For PNG, WEBP, and JXL, there’s an effort setting: lower effort means faster compression but larger size, while higher effort means slower compression but smaller size. I used the highest three settings for these, yielding sizes from ~50 MiB to ~20 MiB. (As a treat, I’ve also converted to JPG, WEBP, AVIF, and JXL at -quality 0, i.e. lossy with the worst settings.)

In summary:

  • PNG yields the worst results (as expected)
  • JXL yields the best, followed closely by WEBP
  • AVIF is as bad as PNG
  • difference between effort settings are modest
    • decrease is at most ~2 MiB to the next setting
    • WEBP actually worsened by 0.3 MiB but stabilized (??)
    • JXL takes an incredibly long time
Format Effort Size (MiB) Time (s)
TIFF N/A 183.049  
PNG 7 53.8905  
8 52.4796  
9 52.0456  
JXL 7 36.2194  
8 35.7587  
9 33.9196 1553.39
WEBP 5 38.0452  
6 38.3053  
7 38.3053  
AVIF (magick) 2 21.2929  
3 21.3375  
4 21.3673  
5 21.3684  
AVIF (avifenc) 5 53.5292 173.36
4 53.5242 179.75
3 53.3450 585.62
Format Effort magick command Alternate command
PNG 0 – 9 magick convert -define png:compression-level=%n in.tif out-%n.png optipng -o%n -out out-%n.png in.tif2
JXL 3 – 9 magick convert -quality 100 -define jxl:effort=%n in.tif out-%n.jxl cjxl -q 100 -e %n --brotli_effort=11 in.tif out-%n.jxl
WEBP 0 – 6 magick convert -quality 100 -define webp:lossless=true -define webp:method=%n in.tif out-%n.webp cwebp -lossless -q 100 -m %n -progress -o in.tif out-%n.webp
AVIF 0 – 10 magick convert -quality 100 -define heic:speed=%n in.tif out.avif avifenc -l -s %n in.png out-%n.avif3
Format (lossy) Size (KiB)
JPG 5848.97
JXL 1805.75
WEBP 695.534
AVIF 349.122

I didn’t think the effort settings would affect compression time that much, but it turns out that for JXL, the highest-effort setting takes a really, really long time. It didn’t occur to me to take timing measurements until I got to JXL’s penultimate effort setting, so I only timed it for its slowest effort setting, which also happened to be the very last conversion I was running. It took a whopping 25m 53.39s!

You’ll notice I have different AVIF file sizes for magick and avifenc. I originally only tried magick, but I was supicious of its extremely small file size, so I compared the output AVIF at speed 2 against the output PNG at compression level 9.

$ magick compare -verbose -metric mae out-2.avif out-9.png out-avif2-png9.png
  Channel distortion: MAE
    red: 91.0942 (0.00139001)
    green: 40.7945 (0.000622484)
    blue: 153.756 (0.00234616)
    all: 95.2148 (0.00145289)

For comparison, comparing WEBP to PNG gave distortions of 0 all around, so something suspicious is going on. AVIF’s colour space is YUV, so perhaps conversion from RGB is necessarily lossless. It was at this point I decided to try avifenc instead, comparing a conversion from PNG against the PNG itself, since this tool only takes JPG or PNG as input.

$ avifenc -l -s 5 out-9.png out-avifenc-5.avif
Encoded successfully.
 * Color AV1 total size: 56146081 bytes
 * Alpha AV1 total size: 0 bytes
Wrote AVIF: out-avifenc-5.avif

$ magick compare -verbose -metric mae out-avifenc-5.avif out-9.png out-avif5-png9.png
  Channel distortion: MAE
    red: 0 (0)
    green: 0 (0)
    blue: 0 (0)
    all: 0 (0)

So ImageMagick’s AVIF encoding was lossy, even with at quality 100! What’s more, it appears that with a true lossless encoding, the round-trip from RGB to YUV back to RGB is perfect, hence the 0 distortion. It’s also surprising that AVIF’s file size is so close to the original PNG’s. In any case, could then create true “lossless” AVIFs, and compare the various speed settings to the generated file size. This time, I’ve had the foresight to also measure elapsed time. I stopped at speed 3, since it was taking 9m 45.62s to encode.

  1. Except Edge doesn’t support AVIF, but who cares about Edge? 

  2. optipng goes up to -o7, but from the docs settings beyond -o5 seem unlikely to help. See also: A guide to PNG optimization

  3. For avifenc, -s is the speed setting, so 0 is the slowest and most effortful, while 10 is the fastest and least effortful.