research!rsc
Thoughts and links about computer programming, by Russ Cox
QArt Codes
QR codes are 2-dimensional bar codes that encode arbitrary text strings. A common use of QR codes is to encode URLs so that people can scan a QR code (for example, on an advertising poster, building roof, volleyball bikini, belt buckle, or airplane banner) to load a web site on a cell phone instead of having to “type” in a URL.
QR codes are encoded using Reed-Solomon error-correcting codes, so that a QR scanner does not have to see every pixel correctly in order to decode the content. The error correction makes it possible to introduce a few errors (fewer than the maximum that the algorithm can fix) in order to make an image. For example, in 2008, Duncan Robertson took a QR code for “http://bbc.co.uk/programmes” (left) and introduced errors in the form of a BBC logo (right):

That’s a neat trick and a pretty logo, but it’s uninteresting from a technical standpoint. Although the BBC logo pixels look like QR code pixels, they are not contribuing to the QR code. The QR reader can’t tell much difference between the BBC logo and the Union Jack. There’s just a bunch of noise in the middle either way.

Since the BBC QR logo appeared, there have been many imitators. Most just slap an obviously out-of-place logo in the middle of the code. ThisDisney poster is notable for being more in the spirit of the BBC code.
There’s a different way to put pictures in QR codes. Instead of scribbling on redundant pieces and relying on error correction to preserve the meaning, we can engineer the encoded values to create the picture in a code with no inherent errors, like these:

This post explains the math behind making codes like these, which I call QArt codes. I have published the Go programs that generated these codes at code.google.com/p/rsc and created a web site for creating these codes.
Background
For error correction, QR uses Reed-Solomon coding (like nearly everything else). For our purposes, Reed-Solomon coding has two important properties. First, it is what coding theorists call a systematic code: you can see the original message in the encoding. That is, the Reed-Solomon encoding of “hello” is “hello” followed by some error-correction bytes. Second, Reed-Solomon encoded messages can be XOR’ed: if we have two different Reed-Solomon encoded blocks b1 and b2 corresponding to messages m1 and m2, b1 ⊕ b2 is also a Reed-Solomon encoded block; it corresponds to the message m1 ⊕ m2. (Here, ⊕ means XOR.) If you are curious about why these two properties are true, see my earlier post, Finite Field Arithmetic and Reed-Solomon Coding.
QR Codes
A QR code has a distinctive frame that help both people and computers recognize them as QR codes. The details of the frame depend on the exact size of the code—bigger codes have room for more bits—but you know one when you see it: the outlined squares are the giveaway. Here are QR frames for a sampling of sizes:

The colored pixels are where the Reed-Solomon-encoded data bits go. Each code may have one or more Reed-Solomon blocks, depending on its size and the error correction level. The pictures show the bits from each block in a different color. The L encoding is the lowest amount of redundancy, about 20%. The other three encodings increase the redundancy, using 38%, 55%, and 65%.

(By the way, you can read the redundancy level from the top pixels in the two leftmost columns. If black=0 and white=1, then you can see that 00 is L, 01 is M, 10 is Q, and 11 is H. Thus, you can tell that the QR code on the T-shirt in this picture is encoded at the highest redundancy level, whilethis shirt uses the lowest level and therefore might take longer or be harder to scan.
As I mentioned above, the original message bits are included directly in the message’s Reed-Solomon encoding. Thus, each bit in the original message corresponds to a pixel in the QR code. Those are the lighter pixels in the pictures above. The darker pixels are the error correction bits. The encoded bits are laid down in a vertical boustrophedon pattern in which each line is two columns wide, starting at the bottom right corner and ending on the left side:

We can easily work out where each message bit ends up in the QR code. By changing those bits of the message, we can change those pixels and draw a picture. There are, however, a few complications that make things interesting.
QR Masks
The first complication is that the encoded data is XOR’ed with an obfuscating mask to create the final code. There are eight masks:

An encoder is supposed to choose the mask that best hides any patterns in the data, to keep those patterns from being mistaken for framing boxes. In our encoder, however, we can choose a mask before choosing the data. This violates the spirit of the spec but still produces legitimate codes.
QR Data Encoding
The second complication is that we want the QR code’s message to be intelligible. We could draw arbitrary pictures using arbitrary 8-bit data, but when scanned the codes would produce binary garbage. We need to limit ourselves to data that produces sensible messages. Luckily for us, QR codes allow messages to be written using a few different alphabets. One alphabet is 8-bit data, which would require binary garbage to draw a picture. Another is numeric data, in which every run of 10 bits defines 3 decimal digits. That limits our choice of pixels slightly: we must not generate a 10-bit run with a value above 999. That’s not complete flexibility, but it’s close: 9.96 bits of freedom out of 10. If, after encoding an image, we find that we’ve generated an invalid number, we pick one of the 5 most significant bits at random—all of them must be 1s to make an invalid number—hard wire that bit to zero, and start over.
Having only decimal messages would still not be very interesting: the message would be a very large number. Luckily for us (again), QR codes allow a single message to be composed from pieces using different encodings. The codes I have generated consist of an 8-bit-encoded URL ending in a # followed by a numeric-encoded number that draws the actual picture:
http://swtch.com/pjw/#123456789…The leading URL is the first data encoded; it takes up the right side of the QR code. The error correction bits take up the left side.
When the phone scans the QR code, it sees a URL; loading it in a browser visits the base page and then looks for an internal anchor on the page with the given number. The browser won’t find such an anchor, but it also won’t complain.
The techniques so far let us draw codes like this one:

The second copy darkens the pixels that we have no control over: the error correction bits on the left and the URL prefix on the right. I appreciate the cyborg effect of Peter melting into the binary noise, but it would be nice to widen our canvas.
Gauss-Jordan Elimination
The third complication, then, is that we want to draw using more than just the slice of data pixels in the middle of the image. Luckily, we can.
I mentioned above that Reed-Solomon messages can be XOR’ed: if we have two different Reed-Solomon encoded blocks b1 and b2 corresponding to messages m1 and m2, b1 ⊕ b2 is also a Reed-Solomon encoded block; it corresponds to the message m1 ⊕ m2. (In the notation of the previous post, this happens because Reed-Solomon blocks correspond 1:1 with multiples of g(x). Since b1 and b2 are multiples of g(x), their sum is a multiple of g(x) too.) This property means that we can build up a valid Reed-Solomon block from other Reed-Solomon blocks. In particular, we can construct the sequence of blocks b0, b1, b2, …, where bi is the block whose data bits are all zeros except for bit i and whose error correction bits are then set to correspond to a valid Reed-Solomon block. That set is a basis for the entire vector space of valid Reed-Solomon blocks. Here is the basis matrix for the space of blocks with 2 data bytes and 2 checksum bytes:
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111The missing entries are zeros. The gray columns highlight the pixels we have complete control over: there is only one row with a 1 for each of those pixels. Each time we want to change such a pixel, we can XOR our current data with its row to change that pixel, not change any of the other controlled pixels, and keep the error correction bits up to date.
So what, you say. We’re still just twiddling data bits. The canvas is the same.
But wait, there’s more! The basis we had above lets us change individual data pixels, but we can XOR rows together to create other basis matrices that trade data bits for error correction bits. No matter what, we’re not going to increase our flexibility—the number of pixels we have direct control over cannot increase—but we can redistribute that flexibility throughout the image, at the same time smearing the uncooperative noise pixels evenly all over the canvas. This is the same procedure as Gauss-Jordan elimination, the way you turn a matrix into row-reduced echelon form.
This matrix shows the result of trying to assert control over alternating pixels (the gray columns):
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111The matrix illustrates an important point about this trick: it’s not completely general. The data bits are linearly independent, but there are dependencies between the error correction bits that mean we often can’t have every pixel we ask for. In this example, the last four pixels we tried to get were unavailable: our manipulations of the rows to isolate the first four error correction bits zeroed out the last four that we wanted.
In practice, a good approach is to create a list of all the pixels in the Reed-Solomon block sorted by how useful it would be to be able to set that pixel. (Pixels from high-contrast regions of the image are less important than pixels from low-contrast regions.) Then, we can consider each pixel in turn, and if the basis matrix allows it, isolate that pixel. If not, no big deal, we move on to the next pixel.
Applying this insight, we can build wider but noisier pictures in our QR codes:

The pixels in Peter’s forehead and on his right side have been sacrificed for the ability to draw the full width of the picture.
We can also choose the pixels we want to control at random, to make Peter peek out from behind a binary fog:

Rotations
One final trick. QR codes have no required orientation. The URL base pixels that we have no control over are on the right side in the canonical orientation, but we can rotate the QR code to move them to other edges.

Further Information
All the source code for this post, including the web server, is at code.google.com/p/rsc/source/browse/qr. If you liked this, you might also like Zip Files All The Way Down.
Acknowledgements
Alex Healy pointed out that valid Reed-Solomon encodings are closed under XOR, which is the key to spreading the picture into the error correction pixels. Peter Weinberger has been nothing but gracious about the overuse of his binary likeness. Thanks to both.
Comments? Please join the Google+ discussion.
Gustavo Niemeyer (2 days ago) Wow, this is brilliant. I had missed the original post with the background somehow. Great read, thank you.
Jaron Swab (2 days ago) I am always making Qr codes for sites I am apart of; this might have to be a new challenge of mine.
Ralph Corderoy (2 days ago) +Peter Weinberger, the W in AWK and the face in that QR code above, is here on G+ in case any want to add him to circles. He’s posted little publicly so far but perhaps a wider audience may encourage more. :-)
Jeff Wendling (2 days ago) I especially liked how you used the fragment to smuggle in the arbitrary data. Wonderful.
JP Sugarbroad (2 days ago) Have you considered using uppercase to reduce the fixed region?
stanley whyte (2 days ago) let me suggest great QR tool at http://www.qrhacker.com/ and if you need a hosted business card (where you can point your great QR code) http://mybest.tel
Russ Cox (2 days ago) +JP Sugarbroad, I did look into that, but I wasn’t thrilled about the HTTP://ALL.CAPS. Maybe most damning, that alphabet doesn’t have a #, so you have to do server side tricks to arrange for the URL to be valid. It would work, but I’ve found using short URLs to be easier and look nicer.
Brad Parks (2 days ago) Brilliant! And I love the deep dive into how they work. Thank you.
Carrieanne Rowland (2 days ago) if u look at the code properly, it looks like a mans face with glasses on!!
Jason Phoenix (2 days ago) Thank you! Informative and highly useful for those of us that desire some aspect of human-readability along with our QR.
ALEJANDRO VALENTINO (2 days ago) awesome!
Ken Ferry (2 days ago) Wow. When you posed the xkcd code, I assumed you were relying on the error correction to bail you out. Very cool!
Christopher Bulle (2 days ago) that is well clever, thank you very much.
Marko Veelma (2 days ago) I have done something like this manually and it is damn hard to stay in allowed error limits. It is cool that somebody took this topic and show us how to make systematic changes without affecting data.
Alexander Gallego (2 days ago) You should revisit the idea of the technical magazine you once posted on your blog. I’d buy a subscription !
Brock French (2 days ago) Happy to have stumbled upon this before the 503. Great work.
Russ Cox (2 days ago) Un-503’ed, sorry about that.
Bert Massop (a day ago) Did you consider marking image regions as “don’t care”? That might vastly improve the quality of the result for non-rectangular images, as they can then be filled with the necessary pixels to match the “care” ones.
Russ Cox (a day ago) Yes, if the image you upload has transparent pixels then they get treated as don’t care. Also, areas of low contrast in the image get treated as “don’t care as much”.
Mark Berry (a day ago) The lack of # in the Alphanumeric (ALL CAPS) alphabet doesn’t seem to be a deal breaker - you just have to encode the # in byte encoding mode (of course you lose some of the gain by having to insert a mode switch code), but saving 2.5 bits per character in the URL means the break even point is reached even with short URLs.
Of course, QArt codes are mostly about how it looks, and it would be interesting to see if deliberately using variant/non optimal encoding of the URL portion (the bit you state you have no control over) could actually give a better image overall - for example placing an encoding mode indicator (plus the length value of 1) between each character in the URL would give lots of zero bits, which might look better.
I also see that there is an end of message encoding mode. How do decoders cope if they encounter this before the data has been exhausted? At the moment you produce your image as
URL + # + switch to numeric mode + data that shows image but is interpreted as a non existent anchor
Why not try
URL + end of message mode indicator + binary data of image that is hopefully simply ignored
(Since encoding necessarily uses a variable number of bits that do not necessarily exactly fit the square format of the code, then that end of message mode indicator would appear to be needed in all data streams, so the question is do any decoders go wrong if they encounter such a code “unrealistically early” in the data stream?)
From your description, the QR code is actually
URL + image data + error correction
I know that it wasn’t the point of your article, but seeing as we don’t care about the image data being interpreted correctly when decoded (in your original case since it’s just an anchor that is not sent to the server) could you not simply consider this as
URL + # + image data + error stuff for the URL + error stuff for the image
Since the error stuff for the image is correcting data that we don’t care about beyond it’s visual appearance, why not simply make that bit of the error stuff hold whatever you want - it will “correct” data that you are effectively discarding anyway.
Erik Westermann (a day ago) Can someone explain how to get this working under OS X? I installed Go and got the latest version of the QR generator, yet when I try to build (using command: go build qr.go png.go) I always get the same error:
qr.go:15:2: import “code.google.com/p/rsc/qr/coding”: cannot find package
Mathieu Lonjaret (about 6 hours ago) +Erik Westermann How did you acquire qr.go? if you just set your $GOPATH and then do ‘go getcode.google.com/p/rsc/qr’ it should fetch everything needed.
Mathieu Lonjaret (about 6 hours ago) Nice read, and fun to play with. Unfortunately, I was unable to make a pretty enough looking gopher QArt (with the running one, not just the face). It was just too much pixelated… I haven’t tweaked the code though, just tried directly onhttp://research.swtch.com/qr/draw