You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A `Bitmap` input must contain the exact borderless QR module matrix. For a rendered image whose integer module geometry is already known, `grid` skips photographic binarization and finder detection. Centered generated grids are also recognized automatically when their quiet zone and finder patterns validate.
177
179
180
+
`multiPass` controls how much work the decoder may spend on one image:
181
+
182
+
-`true` (default) — adaptive multi-pass detection. Large frames are detected
183
+
on a box-downscaled raster first (full resolution stays as the fallback);
184
+
failed decodes are retried with inverted colors (light-on-dark codes,
185
+
screens) and on 2x..32x downscaled rasters (codes overflowing the frame,
186
+
screen moire). Extra passes only run after the primary pass fails, so
187
+
images that already decode pay nothing.
188
+
-`'max'` — additionally tries rotated, cropped, and upscaled fallback
189
+
candidates. Decodes noticeably more hard photos, but can be several times
190
+
slower on images that still fail. Good for one-shot file decoding; avoid it
191
+
for per-frame camera scanning, where the next frame is usually a better
192
+
candidate than a slow retry.
193
+
-`false` — strict single-pass decode: one binarization, one detection, no
194
+
retries. Deterministic latency for tests and trusted inputs.
195
+
178
196
### Decoding algorithm
179
197
180
198
QR code decoding is challenging; it is essentially a computer vision problem. There are two main scenarios:
@@ -186,18 +204,19 @@ The state-of-the-art approach for this, as with other computer vision problems,
186
204
187
205
The implemented reader algorithm is inspired by [ZXing](https://github.com/zxing/zxing):
188
206
189
-
1.`toBitmap`: Convert the image to a bitmap of black and white segments. This is the slowest part and the most important.
190
-
2.`detect`: Find three finder patterns and one alignment pattern (for versions > 1). This is tricky—they can be rotated and distorted by perspective. A square might appear as a quadrilateral with unknown size. The best we can do is count runs of the same color and select patterns with almost the same ratio of runs.
191
-
3.`transform`: Once patterns have been found, correct the perspective and transform the quadrilateral into a square.
192
-
4.`decodeBitmap`: Execute the encoding in reverse: read information via a zig-zag pattern, de-interleave bytes, correct errors, convert to bits, and finally, read segments from bits to create the string.
193
-
5.**Finished!**
207
+
1. Fast paths: exact module grids (`Bitmap` input, `grid` geometry) and clean generated / screenshot rasters are sampled directly, skipping the photographic pipeline below. Frames larger than 4MP run detection on a box-downscaled raster first, with full resolution kept as the fallback.
208
+
2.`toBitmap`: Convert the image to a bitmap of black and white segments using per-block adaptive thresholds. This is the slowest part and the most important.
209
+
3.`detect`: Find three finder patterns by scanning for 1:1:3:1:1 runs of the same color, cross-checking each candidate in four directions, and ranking candidate triples — several QR codes may share one frame. The BCH-protected version words are read straight from the image, since the size estimated from finder distances drifts on dense codes.
210
+
4.`transform`: Correct perspective and sample the quadrilateral into a square grid — through one homography, or, for dense v7+ codes, cell-by-cell against the full alignment-pattern grid so distortion cannot accumulate.
211
+
5.`decodeBitmap`: Execute the encoding in reverse: read information via a zig-zag pattern, de-interleave bytes, correct errors, convert to bits, and finally, read segments from bits to create the string.
212
+
6. If every candidate fails and `multiPass` allows it, the pipeline reruns with inverted colors, on progressively downscaled rasters, and with perturbed sampling grids; `multiPass: 'max'` adds rotated, cropped, and upscaled candidates.
194
213
195
214
### Decoding test vectors
196
215
197
216
To test our QR code decoding, we use an excellent dataset
198
217
from [BoofCV](http://boofcv.org/index.php?title=Performance:QrCode). BoofCV decodes 73% of the test cases,
199
-
while ZXing decodes 49%. Our implementation is nearly at parity with ZXing, primarily because ECI (Extended
200
-
Channel Interpretation) support is not yet included. The test vectors are preserved in a Git repository at
218
+
while ZXing decodes 49%. Our implementation decodes 63% with default options and 69% with
219
+
`multiPass: 'max'`, surpassing ZXing. The test vectors are preserved in a Git repository at
**Note for Testing on iOS Safari:** Accessing the camera on iOS Safari requires HTTPS. This means that the file: protocol or non-encrypted http cannot be used. Ensure your testing environment uses https:.
@@ -274,6 +293,19 @@ nuintun x 51 ops/sec @ 19ms/op
274
293
instascan x 133 ops/sec @ 7ms/op ± 31.77% (3ms..164ms)
275
294
```
276
295
296
+
Versus native [zxing-cpp](https://github.com/zxing-cpp/zxing-cpp) 3.1 (gcc -O2, default reader
297
+
options — the configuration phone scanning stacks approximate), measured on one Ryzen 9900X core:
298
+
299
+
```
300
+
# decode vs native zxing-cpp
301
+
clean v1, 116x116 qr 5.6μs zxing-cpp 23μs (4.1x faster)
302
+
clean 768-char code qr 50μs zxing-cpp 113μs (2.3x faster)
303
+
12MP photo qr 12.8ms zxing-cpp 17.3ms (1.4x faster)
304
+
305
+
# BoofCV detection dataset accuracy (508 photos)
306
+
qr 63%, qr multiPass max 69%, zxing-cpp fast mode 50%, zxing-cpp default 75%
0 commit comments