lucid.page shoots.page photo derivatives: Pillow vs ImageMagick (vs libvips)
Text size
Read time4 min

# shoots.page photo derivatives: Pillow vs ImageMagick (vs libvips)

The job being measured is exactly what processor/decode.py does per upload: decode, EXIF orient, ICC to sRGB, 2000 px preview (q88) + 480 px thumb (q82). Watermarking measured separately.

Contents

# Results

9 real camera JPEGs at native resolution, 1 pinned core (prod container is 1 vCPU), best of 3. CPU is whole-process time, so the Python rows include ~95 ms interpreter startup that the long-lived prod server never pays.

Pipeline CPU/photo (mean) Worst (61 MP) Peak RAM SSIMULACRA2 Butteraugli p3 Preview size
Pillow, prod as-is 2.08 s 3.39 s 496 MB 82.69 0.941 477 KB
Pillow, reordered 0.69 s 0.83 s 207 MB 82.52 0.950 475 KB
ImageMagick 7 Q16, tuned 1.31 s 1.38 s 266 MB 82.24 0.953 471 KB
ImageMagick 7 Q16, naive 2.26 s 2.41 s 522 MB 82.32 0.952 473 KB
ImageMagick 7 HDRI (official build), tuned 3.49 s 3.34 s 272 MB 82.21 0.955 472 KB
ImageMagick 7 HDRI (official build), naive 8.50 s 10.84 s 1040 MB 82.33 0.952 473 KB
libvips (control) 0.53 s 1.04 s 148 MB 81.78 1.057 475 KB

# Watermark

Overlay (tiled diagonal studio name, 30% white) built once per gallery, center-cropped per photo.

Per photo (composite + encode wm preview + wm thumb) One-off overlay build
Pillow 41 ms 177 ms
libvips 43 ms 73 ms
ImageMagick +242 ms (reloads overlay every process) 3.3 s

Watermarked preview is ~7% bigger (508 KB vs 475 KB).

# Setup

End