You may have seen sylnsfar's "Artistic QR Code" library. It produces non-standard QR codes that incorporate arbitrary pictures, but are nonetheless machine-readable [3].

Of course, I had to understand how it worked ☺

It doesn't take much to find that the magic happens in the `combine`
function in `myqr.py`:

if ver > 1: aloc = alig_location[ver-2] for a in range(len(aloc)): for b in range(len(aloc)): if not ((a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)): for i in range(3*(aloc[a]-2), 3*(aloc[a]+3)): for j in range(3*(aloc[b]-2), 3*(aloc[b]+3)): aligs.append((i,j)) for i in range(qr.size[0]-24): for j in range(qr.size[1]-24): if not ((i in (18,19,20)) or (j in (18,19,20)) or (i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24) or ((i,j) in aligs) or (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)): qr.putpixel((i+12,j+12), bg.getpixel((i,j)))

That code is full of magic numbers, and the multiple nested loops don't really help legibility much. What's going on in there?

To understand where those numbers came from, I studied a QR code tutorial. The most useful page for our purposes is the one that explains "function patterns".

Everything in that function assumes a 3-pixel QR module, so:

- the first block collects the placements of the alignment
patterns
- but only if the code is large enough to need them (
`ver > 1`) - and ignoring those that would collide with finder patterns
(
`(a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)`means "top-left, top-right, bottom-left corners")

- but only if the code is large enough to need them (
- the second block replaces pixels in the QR code (
`qr.putpixel`) with pixels from the fancy picture (`bg.getpixel`) if:- they're not in timing patterns (
`(i in (18,19,20)) or (j in (18,19,20))`) - they're not in finder patterns (
`(i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24)`) - they're not in alignment patterns (
`(i,j) in aligs`) - and two more conditions I'll explain later

- they're not in timing patterns (

So all those magic numbers define the placements of finder / timing /
alignment patterns: those *really* have to be there, otherwise the
scanning / recognition code won't even realise it's looking at a QR
code.

The last two conditions are:

(i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)

The second one skips transparent pixels in the picture (`bg0` is a
ARGB-version of the input picture `bg`, and the pixel value at
position 3 is the alpha value).

The first one makes sure to leave the central pixel of each 3×3 QR module untouched: we want the QR data to remain there!

So, another way to get the same result would be:

- paint a normal black & white QR code
- layer the fancy picture on top of it, honouring the alpha channel
- paint all non-data modules on top
- paint the data modules on top, but only their central pixels

Of course, it's not trivial to know whether a module is part of the
data or of a function pattern, but we're in luck: the `libqrencode` C
library provides us with that information!
It generates the QR code as a matrix of bytes, and each byte is a
bitfield:

MSB 76543210 LSB |||||||`- 1=black/0=white ||||||`-- data and ecc code area |||||`--- format information ||||`---- version information |||`----- timing pattern ||`------ alignment pattern |`------- finder pattern and separator `-------- non-data modules (format, timing, etc.)

The Perl modules `Text::QRCode` and `Imager::QRCode` wrap that
library, but do not expose the internal byte matrix. So of course I
wrapped it again myself ☺

`Alien::QREncode` uses `Alien::Base` to install `libqrencode`, then
`Data::QRCode` binds it via `Inline::Module` and provides a Perlish
API (see the test file for an
example).

Now, we only need to deal with the actual images: let's use `Imager`,
which seems simple and flexible enough. The actual code is maybe
too flexible, but the core of the logic is:

my $back_image = qr_image($qr_code); return $back_image unless $fancy_picture; my $front_image = qr_dots_image($qr_code); my $target = Imager->new(xsize=>$size,ysize=>$size); $target->paste(src => $back_image); $target->compose(src => $fancy_picture); $target->compose(src => $front_image); return $target;

which is a direct translation of the procedure outlined before.

Once we've installed `Alien::QREncode`, `Data::QRCode`, and
`Imager::QRCode::Fancy`, we can say:

perl overlay-qr.pl 'http://pokketmowse.deviantart.com/art/In-the-Kawaii-of-the-Beholder-177076230' kawaii-beholder.png kawaii-beholder-qr.png

and get this:

Or maybe:

perl overlay-qr.pl 'Squirrel Girl is the best' squirrel-girl.png squirrel-girl-qr.png

and get this:

Notice how the size of the "dots" adapts to the resolution of the picture (although it could be smarter), and how transparency is maintained.

more or less: I think you need a high-enough-resolution camera and some pretty forgiving recognition code