QR Codes and Barcodes
The Quirc QR decoder and the Barcode1D linear barcode decoder, and publishing decoded results to Neuro Studio.
The core bundles two CPU decoders that operate on an 8-bit greyscale image. The QR decoder is quirc, by Daniel Beer, vendored with one modification. The linear barcode decoder is Ohm Lab's. Neither allocates memory in its decode path.
QR codes
The QrReader example, under File > Examples > Quirc, has this structure:
#include <OV5640_Arduino.h>
#include <PostProcess.h>
#include <quirc.h>
#define QR_DIM 256
static uint8_t g_snapshot[QR_DIM * QR_DIM * 3] __attribute__((aligned(32), section(".ext_psram")));
static struct quirc_code s_code __attribute__((section(".ext_psram")));
static struct quirc_data s_data __attribute__((section(".ext_psram")));
OV5640 camera;
static struct quirc *g_qr = nullptr;
void setup() {
VisionConfig cfg;
cfg.camera = CAMERA_VGA;
cfg.nn.width = QR_DIM; cfg.nn.height = QR_DIM;
cfg.nn.aspect = ASPECT_CROP_CENTER;
cfg.af = AF_CONTINUOUS;
Vision.begin(cfg);
g_qr = quirc_new();
quirc_resize(g_qr, QR_DIM, QR_DIM);
}
void loop() {
if (!NN_capture_snapshot(g_snapshot, sizeof g_snapshot)) return;
int w, h;
uint8_t *img = quirc_begin(g_qr, &w, &h);
const uint8_t *src = g_snapshot;
for (int i = 0; i < w * h; i++, src += 3)
img[i] = (src[0] * 77u + src[1] * 151u + src[2] * 28u) >> 8;
quirc_end(g_qr);
for (int i = 0; i < quirc_count(g_qr); i++) {
quirc_extract(g_qr, i, &s_code);
if (quirc_decode(&s_code, &s_data) != QUIRC_SUCCESS) continue;
Serial.println((const char *)s_data.payload);
}
}quirc_begin returns the decoder's input buffer, which the sketch fills with grey values; quirc_end runs detection. quirc_count returns the number of codes located, quirc_extract retrieves one and quirc_decode reads it. The decoded structure holds the payload and its length, the QR version and the error correction level. The corners are in s_code.corners.
The result structures total about 13 KB and are placed in PSRAM to leave on-chip RAM for the camera.
The single local change to quirc neutralises its internal assertions, which call a stdio path that fails on this core's C library. The change is documented in quirc_internal.h.
Linear barcodes
Barcode1D decodes EAN-13, UPC-A and EAN-8 in one call:
#include <barcode1d.h>
barcode1d_result_t r;
if (barcode1d_scan_gray(g_gray, DIM, DIM, &r)) {
Serial.println(r.text); // the digits
}The decoder tries eleven horizontal scanlines between 20% and 80% of the image height. Each line is binarised against its own midpoint, run-length encoded, and searched for guard patterns. Digits are classified against the standard L, G and R tables, and the parity of the six left digits recovers the EAN-13 leading digit. Both orientations are tried. Only results passing the GS1 mod-10 checksum are returned. UPC-A is reported when an EAN-13 decodes with an implied leading zero.
The result structure contains the symbology, the digits, the winning row and the left and right edges in pixels. One barcode is returned per call. Work buffers are file-scope statics of about 10 KB, so the decoder is called from one task at a time.
A wide crop suits a linear barcode. VGA with ASPECT_STRETCH into a 320-wide snapshot, or an offset crop across the frame's middle, are both used.
Publishing results
The examples publish through the vision publisher, which draws a box on the video in Neuro Studio, labels it with the decoded text, and shows a crop thumbnail and payload in the Vision panel:
vnd_meta_vision_t vis;
vnd_meta_vision_begin(&vis, VND_META_SLOT_QR, META_QR_FRAME, g_payload, sizeof g_payload);
// per decoded code, box in snapshot-normalised coordinates
vnd_meta_vision_add(&vis, cx, cy, w, h, label, extra, extra_len, g_snapshot, QR_DIM, QR_DIM);
vnd_meta_vision_finish(&vis);finish() is called on every frame. A frame with no results clears the previous box. The extra bytes are displayed beside the result; the QR example packs the version, error correction level and text. vnd_meta_vision_announce(kind) is called about once per second to identify the sketch type to the host. The payload buffer is sized by the sketch and placed in PSRAM.
Operating notes
- Both decoders require focus.
cfg.af = AF_CONTINUOUSon the OV5640. The VD66GY has a fixed focus and the code is held at the lens's designed distance. - Under fixed lighting, locked exposure improves consistency. See Capture Controls.
- A QR code spanning about a quarter of a 256-pixel snapshot decodes reliably. Smaller codes require a larger snapshot.
- A code held in view decodes on every frame. The examples suppress repeated output of an unchanged payload.
- The two decoders share no state and may run on the same snapshot.
The QR reader also runs on the VD66GY with the camera include and declaration changed.