For a recent project I had a need for a reliable and fast QR-code scanner. I had an Raspberry Pi 2 single-board computer on hand and coupled with the Pi Camera Module and some elbow grease I managed to come up with a rather sweet setup.
The official Raspberry Pi version 2 Camera Module consists of a 8-megapixel Sony IMX219 sensor mounted behind a fixed focus lens on a small circuit board. The board connects to the computer CSI connector via a flexible ribbon cable.
The stock ribbon cable supplied with the Camera module is too short (150 mm) to allow for a comfortable positioning of the module for QR-scanning. The are available replacement flex cables of various length that can be easily swapped in — I went with a 500 mm cable.
The camera system has a huge depth of field but the fixed lens is still focused too far away to get sharp shots at the distances one would want in a QR-scanner. The lens is fixed with some glue but when this is broken or scratched off is it actually possible to rotate it in its assembly thus adjusting the focus point of the system.
There are many instruction videos on the net showcasing various techniques to achieve this, what worked for me was a small pliers to keep the non-rotating part of the assembly secure while applying force on the lens knobs with a tiny pointed screwdriver to force the lens to rotate counter-clockwise.
Real force is actually required to break off the glue, but after this can the lens be adjusted rather easily. To determine the amount of rotation required I’d recommend to take frequent snapshots with the
raspistill CLI tool shipped with Raspbian during the operation, stopping when sharp shots at 10 cm are achieved.
According to reports should it be possible to unscrew the lens from the assembly if it’s rotated too much. I’d like to think it would be quite finicky to re-attach so be careful! It’s only a matter of ~ 360° of rotation to achieve the desired focus point.
The first step after attaching the camera is to enable it in
/boot/config.txt (can be done with simple
raspi-config utility), rebooting and verifying basic camera functionality with
For the next steps are there a plethora of write-ups. Many solutions appear to be quite fragile. I wanted something more robust, something not depending on python libraries that’s not installable with pip or downloads of binaries from unmaintained SourceForge-projects.
I finally ended up with agilerule’s QRScanner.java which utilizes the proven zxing java QR-code library (appears to be very popular in the Android eco-system) to detect QR-codes in snapshots taken by the standard raspistill command line tool. I threw in some optimizations:
- The time consumed by zxing to detect a QR-code is proportional to the resolution of the image. The full 8 megapixel sensor resolution is very much overkill, I finally settled for 400×300 pixel snapshots (0,12 megapixel) as a compromise between detection speed and accuracy. This low resolution means that the image-acquisition, QR-detection cycle is dominated by the acquisition phase and that it’s possible to detect ~1 QR-code/second.
- Rather than dumping the acquired images to the root file system one can utilize tmpfs, thus saving some wear-and-tear on the SD-card and also perhaps achieving a minuscule performance improvement.
To try it:
- Clone the repo:
git clone https://github.com/nordstrand/qr-scanner-for-raspberry-pi
- Install maven
mvn clean install exec:java
This QR-scanning setup is quite usable and responsive but cannot be compared with what you’d find at the super market checkout line when it comes to speed and accuracy.
The item to be scanned should ideally be positioned as parallel to the camera film plane as possible. Sharp shadow lines falling on the scanned item can cause problems. So can light sources shining directly into the lens. What worked best for me was an inverted mount, with the camera pointed down towards a dark surface. I’d also recommend to add a buzzer that can make some beeps after succesful scans.
My main focus of improvement would be to increase the scanning speed. That would entail using something other than raspistill still image acquisitions, perhaps time lapses or trying to access the camera through a lower-level C interface.