QR codes and pixel art scaling algorithms, part 2

I've known for a while that EPX/Scale2x makes QR codes unreadable, at least on some devices. (Turns out the iOS camera app can read them.) I run into creative QR code renderings all the time, so why should EPX be uniquely bad? I think I finally figured out why.

As long as you leave the finder patterns alone, you can render the rest of the code as little dots instead of full size blocks and it stays readable.

(hover or tap!)

It's as if when the reader needs to determine whether a module is black or white it only looks at one pixel from the source image near where the module's center ought to be instead of averaging over the whole region. A cursory glance at some decoder libraries seems to confirm the theory.

EPX expands each source pixel into a block of four and applies edge smoothing. The spots the reader is sampling lie where the four pixels in a block meet, and many of those pixels will be the wrong color. The distortions introduced by the scaling process are exactly where we don't want them to be.

... but this scaled version has the correct colors in all the places that the decoder looks, so it's still scannable.

Applying EPX twice expands each source pixel into a 4x4 block, and the four pixels in the center of each block are almost always all the same color. The result tends to scan well.

We'd predict that

hq3x will work,
hq4x will work,
and hq2x will be iffy,

and that's exactly what we find.

It also stands to reason that you could scale the code up by a factor of 3, keep the center pixels in each 3x3 block constant, replace the rest with noise, and still have something scannable.

I can't believe that worked.


Published 2024-07-05

Previous: You can stream text (but not Markdown) without using JS