Reading QR codes without a computer
qr.blinry.org
qr.blinry.org
So I ended up writing a Basic program on the Atari to read data from the disk sector by sector and paint it on the screen (with the large 4-color pixels of graphics mode 3). The Atari was connected to the TV card of my PC and a Delphi program I wrote was running on the PC that kept taking screen shots and trying to decode the data from there. I quickly learned that empty sectors threw off my pixel position calibration so I added a mask pattern and a checksum. The sector address was also included. With that, I was able to transfer all my disk contents to my PC. To this day I consider it my greatest engineering achievement :)
Some ten years later, I went on to build an SIO2PC program called AspeQt. A more up-to-date community fork called RespeQt is still the most popular tool in that category used by the community. It even has its own subforum on AtariAge[1].
[1] https://forums.atariage.com/forum/184-respeqt-sio2pc-softwar...
This should be explained better... I mean sure, the math can be hard.. but is the error correction appended at the end? After every byte? In the middle? Can we still read a qr code by hand if it has error correction (assuming it's not damaged, and we can skip the math part?)?
Then, depending on the size and error correction level (not covered in the article where this is decoded from) the bitstream is split up into blocks, ecc data is calculated for both, and then the blocks are interleaved.
So this method will fail for any qr version above 5 for any level of ecc, and 3-5 with high levels of ecc.
Fun story: The barcode was ripped and the checksum # was frayed off of an item I was returning to Home Depot. I frantically tried to do the math quicker that the checker could manually look it up. But they won and I'll never forget it.
https://www.gs1.org/services/how-calculate-check-digit-manua...
https://www.simplybarcodes.com/barcode_check_digit_calculato...
So I wonder if the checker had to manually look anything up, assuming it was only the checksum digit that was frayed off.
Perhaps some POS systems do require the checksum digit for manually-entered barcodes, though.
That is hilarious to me. The obviously safer approach of checking the check digit was not merely neglected, but actively forbidden.
As a checksum, the last digit of the barcode is not part of the data (product number), it's metadata; when the barcode reader beeps, that means it has computed the check digit and it matches the digit on the barcode. The barcode data is sent to the computer, but the check digit is not. The check digit is not in the database. The computer using the UPC leaves it to the barcode reader or human to ensure the input is correct.
You'll might also notice that similar barcodes exist in multiple places as well--unopened cases of products might have an identifier that includes the UPC, and so does the price sticker on the shelf. Both of these include more data than just the UPC.
Another fun fact: in the intro to The Simpsons, Maggie gets scanned by the clerk as a valid item with a price shown on the register.
Decoding small QR codes by hand (2012) - https://news.ycombinator.com/item?id=36173441 - Jun 2023 (69 comments)
How a QR code works - https://news.ycombinator.com/item?id=32837565 - Sep 2022 (114 comments)
Creating a QR Code step by step - https://news.ycombinator.com/item?id=24119124 - Aug 2020 (41 comments)
Creating a QR Code step by step - https://news.ycombinator.com/item?id=18360847 - Nov 2018 (34 comments)
Does anyone know how much it actually helps QR code readers to have this mask pattern applied?
Last month, I tried to find out what the optimal error correction setting is (answer: none, if the matrix isn't damaged, like in my use-case where it's shown on a screen), which wasn't easy to find. I didn't find anyone who actually went out into the real world and tried out different settings and different scanning implementations to see what the effect is of choosing a good mask or choosing a good error correction value. In the end, a theoretical answer was enough for my application because I can assume the QRs to be pristine, but in a perfect world we also wouldn't need a mask setting. I then tried to do some research of my own by reading a small QR off my screen with different error correction levels and an intentionally shaking camera to simulate read issues when someone points a smartphone, but found no significant difference between any of the settings. With there being at least four alignment markers, a huge quiet area, and a "timing pattern", does it really matter if there are white blobs due to not having a mask?
(The secondary reason why I gave up trying to read QRs visually is because I noticed they always have the URL written below as fallback anyway. Since then, I did see a few times where there was no fallback, but it's very rare)
Depending on the length of your message, sometimes you can turn up the error correction level without impacting the final QR dimensions, and in those cases it's a win/win.
By making the pixels of the code bigger, the camera can be a bit blurry to get the code big enough, and then it'll read.
One might reasonably expect error correction to help with the decode of blurry inputs, but that wasn't really the case in my experiments.
I think people are working on both as we speak.
As for the practical limits, try using your phone to take a picture of something very far away, or very tiny, and see what happens.
While it is theoretically possible, nobody has yet done so...
This is very convenient when you control the QR reader and need to represent long numeric identifiers like UUIDs.
For example:
9728983f-7d7d-4189-b624-f92781e36650 (lowercase UUID):
=> length=36, 15 pixels between markers
JWM9GFVXFN0RKDH4Z4KR3RV6A0 (base32 UUID):
=> length=26, 11 pixels between markers
9728983F-7D7D-4189-B624-F92781E36650 (uppercase UUID):
=> length=36, 11 pixels between markers
200924207194334734815443970355691218512 (decimal UUID):
=> length=39, 7 pixels between markers
The uppercase UUID has bigger pixels because it used a different encoding, and gets the same results as the shorter base32 uuid.The decimal UUID is a longer string, but results in much bigger pixels because it can use numeric encoding.
I have a QR code base attendance tracker [1], where attendees show the code [2] on their phones (glares, etc.), in bad lighting conditions, etc. Bigger pixels means scanning stays quick. Same with parcel tracking [3] where field agents might need to scan QR codes in barely-lit hallways, etc.
[1] https://workspace.google.com/marketplace/app/qr_code_pass_fo...
-4J5BJ+%F$C881NIMV-IG2.C (base45, 132 bits in QR)
JWM9GFVXFN0RKDH4Z4KR3RV6A0 (base32, 143 bits in QR)
200924207194334734815443970355691218512 (decimal, 130 bits in QR)
It will make use of all allowed characters in the alphanumeric mode, while being significantly shorter than base32 and as dense as decimal. And decimal encoding in general needs bignum, because there is no suitable 10^k which is only slightly larger than powers of two so no convenient binary-to-decimal encoding exists (conversely, QR code itself does make use of the fact 2^10 is only slightly larger than 10^3 for this mode). Base45 always works on three-byte groups and maintains the similar efficiency in comparison.Error correction doesn't seem to be for easier scanning, but for errors that might occur
(... and stupid logo overlays)
but this information is annoyingly hard to find. Hence my question about the mask xor overlay: does that actually help? Did anyone try it, or did it just sound like a good idea?
At least this is the way I did it years ago when I thought it would be fancy to have my contact data as a QR Code on my visit card.
My interactive web page on creating a QR code step by step (so basically the inverse process): https://www.nayuki.io/page/creating-a-qr-code-step-by-step
[0] https://docs.beaconstac.com/en/articles/6018654-what-is-erro...
Of course, that's what's enabled things like fast and reliable mobile networks.
Here's the puzzle for anyone curious, though the QR code deciphering comes in the 2nd half: https://puzzles.mit.edu/2023/abcde.puzzlefactory.place/puzzl...
I wonder if QR code was invented today, could it be improved? Maybe for compactness? Ease of read?
Maybe there's not that much left to improve. (unless changing the concept drastically, e.g. NFC)
There could maybe be two solutions:
* Darkest/Brightest allowed by the QR algorithm,
* Darkest/Brightest technically valid, which would would not be chosen by the algorithm (i.e. if one manually chooses the masking pattern.
Perhaps also more solutions - 'Darkest/Brightest recognized by software X/Y/Z'
[1] https://github.com/lifthrasiir/qr.js/blob/52f0409a22c5ece6a5...
It became very popular over here with the Green Passes (EU Digital Covid Vaccination Certificates). Waiters, etc. were supposed to check whether the guests are vaccinated, but 99% of them just demanded any random QR code to be shown to them, took a look at it and considered it done.
Unless we are a country of hidden math geniuses :)
>For the ASCII encoding mode
What's this encoding mode? There's no mention of an ASCII "encoding mode" in the rest of the article. wtf? Did you mean "bytes"?
>bytes case
Suppose it's not bytes, but any of the other modes. How do we read these?
>The error correction is generated by some fancy math, and we don't care about it
Please include error correction. Even if reading by hand, I want to know if I read properly! For that, I need to calculate the error correction.
Even just saying "there's a CRC-32/ISO-HDLC here" is better than saying nothing. I understand CRC is outside the scope of the article.
Yes, and it lists encoding modes Numeric, Alphanumeric, Byte, ECI, and Kanji. It then proceeds to focus on Byte.
Article does notably not list an "encoding mode" called ASCII.
Thus, there's an issue in the article.
I was hoping to learn from the article how to manually read QR codes, but that format, at least, is much too complex to do with any efficiency.
At the end of the day, it's bits. Simplist case: 7-bit ASCII; eliminate masks (is that really so difficult for modern software to read sans the masks?); put error correction, length etc. in some corner where humans can ignore them ...
I was visiting an unmanned business (no-one around) and my phone was dead. I wanted to connect to the wireless but the WiFi sign had only a QR code and no plain text password. I didn't have a QR code scanner on my computer. One of my services uses a QR scanner on the web but I didn't have that project cloned on my MacBook.
Regardless I scanned dependencies of dependencies hoping that some useless NPM dependency or some python package would have a QR code scanner but to no avail.
In my search at least I found (several) parsers which knew how to scan the QR code (exactly what the article is describing) from a picture but no code to go from an image to the binary data. I didn't have OpenCV or any other library I'm familiar with to process images and I deemed it hard to do something without documentation - so I took a photo with PhotoBooth and started converting the picture manually square by square into 0 or 1.
A few minutes later (and plenty of double checking) I had the data and I piped it into one of the parsers et voilà, I had internet.
I was considering doing a project with low quality camera to scan QR codes. We got him a phone before I started down that path.
At best, I've learned to recognize a base64 string that begins with "eyJ" is likely JSON.
Also, if you asked me what the base64 encoding of "password" is, but if you showed me "cGFzc3dvcmQ=" and asked me what the decoded value is, I'd recognize it as "password". Though you could probably change a few digits and fool me.
All my research suggests these are reader implementation specific and not guaranteed to work per the official spec.
But I find it hard to believe these codes are becoming more prevalent if they aren’t guaranteed to read.
Anyone have more info on this?
I also sometimes see QR codes that are inverted. Not all readers detect them but many enough do that they end up in print.
- Apple's barcode reader
- Google's ML Kit
- and ZXing, probably behind most third party QR code scanning apps
covers most QR code readers that users will actually use, so testing them is fairly easy.
The process starts by scanning for the 3 markers on the corners. That give you the square containing the qr content and it's orientation. The next jobs are figuring out the resolution of the square and the version of the qr code used. Eventually you end up knowing the grid dimensions. And then you simply scan the grid as the article outlines by simply averaging the pixels in each grid square to be "white" or "black". As long as you have enough contrast, most grid squares will scan correctly.
You can abuse this quite a bit before it starts breaking. There are some interesting QR codes out there that are barely recognizable as QR codes for people. But if you open them in an image editor and play with the levels and contrast a bit, they are obviously qr codes. And there's error correction too so it's OK if some of the qr code is scanned incorrectly.
Also: A more fun and technical sounding term for "finder pattern" is "Fiducial" https://en.wikipedia.org/wiki/Fiducial_marker