UNDERPRESSURED

compressthemall

Benchmarks

Each codec and setting was measured before it was added to Underpressured. Each test runs the app's choice and a common alternative on the same images. SSIMULACRA2 and Butteraugli score each output against its source file.

smaller JPEG files than ImageIO at the same quality
−11%
for Underpressured to resize and encode 100 photos of 12 MP
4.45 s
smaller screenshots, with every pixel unchanged
28%
SSIMULACRA2 points when the resize uses linear light, not gamma space
+10
Machine
Apple M2 (4 performance + 4 efficiency cores), 16 GB, macOS 27.
Images
40 files with open licenses from Wikimedia Commons. The set has 20 photos of 12 to 50 MP, 10 UI screenshots, 5 PNGs with transparency, and 5 edge cases, such as 16-bit grayscale.
SSIMULACRA2
Measures how similar the output looks to the source. Higher is better, and 100 means identical. At 90 and above, you cannot see a difference at 1:1 zoom.
Butteraugli
Measures how different the output looks from the source. Lower is better.
01 / JPEG

jpegli vs. Apple ImageIO

For each photo, each encoder searches for the lowest Quality that still reaches the target SSIMULACRA2 score. Then the file sizes are compared.

Result At the same quality, jpegli files are 11 to 14% smaller. At score 90, they are less than half the size.

jpegli file size as a share of ImageIO, same scoreMedian per file. ImageIO is 100%.
  • Score 7086%
  • Score 8089%
  • Score 9045%
Median per file over 20 photos. Size is the jpegli size divided by the ImageIO size. Pairs show jpegli / ImageIO.
TargetFilesSizeQualityButteraugli
70200.86257.5 / 413.17 / 3.85
80200.89084.5 / 592.08 / 2.44
909 reached by both0.44798 / 100—
  • At score 90, ImageIO missed the target on 11 of 20 photos, even at Quality 100. jpegli missed the target on 7.
  • At the same SSIMULACRA2 score, jpegli also has the lower Butteraugli distance. The size gain does not depend on one metric.
  • The two Quality scales are different. At SSIMULACRA2 80, jpegli Quality 85 gives about the same result as ImageIO Quality 59.
02 / PNG, lossy

quantizr vs. pngquant

Both quantizers were run on 10 screenshots and 5 PNGs with transparency. Each run used a palette of 16, 32, 64, 128, or 256 colors and a dithering level of 0, 0.5, or 1. oxipng then optimized each output. For each file, the result is the smallest output that reached the target.

Result Where both reach the target, quantizr files are 11 to 13% smaller at scores 80 and 85. quantizr also has a permissive license.

quantizr file size as a share of pngquantMedian over the files where both quantizers reached the target. pngquant is 100%.
  • Score 8087% · 11 files
  • Score 8589% · 6 files
  • Score 90102% · 2 files
Median SSIMULACRA2 of quantizr by palette size and dithering
ColorsDither 0Dither 0.5Dither 1
16222632
32475157
64637074
128767980
256838585
  • A fixed palette size does not work. At 64 colors, the score goes from 33 to over 80, depending on the image.
  • The app tries palettes from small to large. It keeps the first palette that reaches the score jpegli gets at the same Quality. If no palette reaches that score, or the file is not smaller, the image keeps full color.
  • At the default Web Preset Quality, 4 of 10 screenshots keep full color. Text and UI gradients need more than 256 colors.
  • pngquant has a GPL-3 license. It was used only for comparison and is not part of the app.
03 / PNG, lossless

oxipng compression levels

oxipng compresses a PNG again without changing its pixels. Higher levels try more compression strategies. A pixel-by-pixel check found every output identical to its source.

Result Level 2 saves 28.4% in 17.6 s. Level max saves 29.9%, but takes 209.5 s.

Time to optimize 10 screenshots on one coreTotal seconds for 9.5 MB of screenshots.
  • -o 217.6 s · 28.4% saved
  • -o 432.8 s · 29.7% saved
  • -o max209.5 s · 29.9% saved
Screenshots, 10 files, 9.5 MB
LevelMedian savedTotal savedMedian ms, all coresMedian ms, one core
-o 2 (default)33.8%28.4%1,2691,544
-o 435.8%29.7%2,3763,094
-o max36.4%29.9%5,73820,082
  • Zopfli saves about 3% more than level max, but takes 40 to 85 s for each 1 MB file. The app does not offer it.
  • The app processes several images at the same time, so the time on one core is the important number. At level 2, a typical file takes 1.5 to 3.7 s.
  • A 37 MB 16-bit grayscale file gets 0.5% smaller at every level. On this file, higher levels only add time.
04 / Resize

Resizing in linear light

Four ways to make an image smaller on macOS were compared: vImage in linear light, vImage in gamma space, ImageIO thumbnails, and Core Graphics. The target sizes were 2048 px and 1280 px on the long edge. The reference is a hand-written Lanczos-4 filter that works in linear light.

Result vImage Lanczos in linear light gives the best score on every image.

SSIMULACRA2 against the reference, photos at 2048 pxMedian over 20 photos. Higher is better.
  • vImage, linear light92.9
  • vImage, gamma space82.1
  • ImageIO thumbnail70.3
  • Core Graphics68.8
Median SSIMULACRA2 with the lowest score in brackets, and median ms per photo
MethodPhotos 2048Photos 1280Screenshots 1280Photo ms, 2048
vImage, linear light92.9 (91.6)93.9 (90.7)90.8 (87.6)169
vImage, gamma space82.1 (71.8)77.4 (63.2)61.7 (18.9)98
ImageIO thumbnail70.3 (46.5)65.4 (34.7)53.8 (12.8)66
Core Graphics68.8 (56.4)63.2 (47.9)54.5 (14.7)112
  • Resizing gamma-encoded pixels makes fine detail darker or lighter, for example text, foliage, and highlights. A bigger size reduction gives a bigger error.
  • Linear light adds 60 to 70 ms per photo. For a 50 MP photo, peak memory is 1.3 GB, against 602 MB in gamma space.
05 / Speed

100 photos, start to finish

Underpressured decoded 100 photos of about 12 MP, four at a time. It resized each photo to 2048 px in linear light, encoded it with jpegli at Quality 80, and wrote it to disk.

Result Underpressured takes 4.45 s on M2. The target is 30 s on a base M1.

Median time per photo by stageMilliseconds, with four photos in progress at the same time.
  • Decode31.8 ms
  • Resize95.0 ms
  • Encode21.3 ms
  • Write0.5 ms
Wall time for the full batch of 100 photos
RunSeconds
14.359
24.452
34.492
Median4.452
  • This is about 22 photos per second. Peak memory is about 0.8 GB.
  • For a base M1, the same batch is estimated at 5.3 s. If memory bandwidth slows it more than expected, the estimate is 6.7 s. Both are less than a quarter of the 30 s target.
  • The queue is not a bottleneck. It schedules 10,000 items in 89 ms.

Method and limits

Pinned versions
ToolVersionLicense
SSIMULACRA2, Butterauglilibjxl 0.12.0BSD-3
jpeglicommit 031a007BSD-3
oxipng10.2.1MIT
quantizr1.4.3MIT-style
pngquant (comparison only)3.0.3GPL-3.0
  • Most timings come from single runs on a machine that also ran other benchmarks. Treat differences under 10% as noise. The rankings stayed the same between runs.
  • The scores measure how close each output is to its source. They do not measure taste. I also checked the PNG results by eye.
  • All inputs were in the disk cache. The tests did not cover HEIC or RAW decoding.

The bench folder of the repository has the scripts, the image list with licenses and authors, and the full results. You can reproduce every result with those scripts.

← Back to Underpressured overview