I am doing some work with QR codes, and to test my code, I was comparing it against the example QR code at the top of the Wikipedia page for QR codes:
While working on my code to attempt to re-generate this QR code and ensure I got the same output, I encountered a surprise. In the QR standard Section 4.15 says that remainder bits must be appended to the code data, and they must all be zeros. I believe the Wikipedia example is using ones.
My script (code at the bottom of this question) generates the following QR code when attempting to recover Wikipedia's QR code:
You will notice that almost all the pixels are identical. The exception is in the lower left. At the far left edge, immediately above the lower-left Finder target, my script produces a shape that looks like a left-facing pitchfork, or the number 3. The Wikipedia one does not. The discrepant pixels are exactly those pixels marked with the "X" in this other Wikipedia image, indicating them as the remainder:
Now, based on this, the Python QR code library could be wrong, or Wikipedia could be wrong. The QR Code Mask (which XORs the code with one of 8 predefined patterns) makes it hard to tell which code is right. We want to know which code is encoding zeros at those 7 pixels. To work that out, my script (again, code below) runs through all 8 possible Mask patterns, un-masks them, and shows the codes. They should all mostly match (proving that the unmasking was successful), and indeed they do, and they show pure white in that region:
And in a QR code, white is a "0" bit. This leads me to conclude that my generator is correctly using zeros for the remainder, and Wikipedia's generator did not.
Of course, these remainder bits are meaningless and should be ignored by any QR decoder. That's why this mistake has persisted for so many years on Wikipedia. But a standard is a standard, and the example QR code at the top of the page ought to be a fully standard-compliant one.
Am I correct in believing that I have demonstrated that Wikipedia has a mistake? Does my logic (and code) check out?
Code here, relies on qrcode:
import qrcode
import numpy as np
import matplotlib.pyplot as plt
payload = "http://en.m.wikipedia.org"
version = 3
QR_size = 17 + 4 * version
# Generate a list of the 8 QR codes with the 8 possible masks
all_8_codes = []
for mask_num in range(8):
code_maker = qrcode.QRCode(version = version,
error_correction = 3,
mask_pattern = mask_num)
code_maker.add_data(payload)
code_maker.make()
all_8_codes.append(np.array(code_maker.modules))
plt.title(f"Wikipedia's QR code (Almost)")
plt.imshow(all_8_codes[1], cmap = "Greys")
# Across the 8 codes, identify any pixels which are constant and should not get the mask
anti_mask = np.zeros([QR_size, QR_size])
for i in range(QR_size):
for j in range(QR_size):
px_cast = [arr[i][j] for arr in all_8_codes]
anti_mask[i][j] = all(x == px_cast[0] for x in px_cast)
# Iterate through the 8 codes, re-applying the XOR mask and therefore removing it
for mask_num, code in enumerate(all_8_codes):
# Using the library, get the lambda function that indicates whether a bit flips
mask_lambda = qrcode.util.mask_func(mask_num)
for i in range(QR_size):
for j in range(QR_size):
# Don't apply the mask to areas identified as anti-mask
if not anti_mask[i][j]:
code[i][j] ^= mask_lambda(i,j)
# Show what should be 8 nearly-identical codes with the masks removed
# This proves we successfully demasked the message.
plt.figure()
plt.title(f"Demasked {mask_num}")
plt.imshow(code, cmap = "Greys")
plt.show()
Edit: as a second point of reference, it appears this post has been linked on the Talk page for that Wikipedia article. Responses there, should they appear, could be insightful for this question. Link: https://en.m.wikipedia.org/wiki/Talk:QR_code
