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.tif 2 |
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.avif 3 |
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.
-
Except Edge doesn’t support AVIF, but who cares about Edge? ↩
-
optipng
goes up to-o7
, but from the docs settings beyond-o5
seem unlikely to help. See also: A guide to PNG optimization. ↩ -
For
avifenc
,-s
is the speed setting, so0
is the slowest and most effortful, while10
is the fastest and least effortful. ↩