Skip to content

Commit daa54b3

Browse files
MagicalTuxclaude
andcommitted
docs: bring README in line with current detection status
- demo blurb: App Clip generation and the tracked live-scanning overlay - QR footnote: describe the hardened sampler as it exists today (adaptive binarization ladder, synthesized finder fallback, timing-anchored thin-plate dewarp, biased module classification, format-info brute force) with the real-capture envelope in tests/real_world.rs - locator section: the precision gates (scene-blob cap, text rejection, ink banding, duplicate suppression, finder-hit synthesis) and fresh bench numbers — the old table understated the detect-vs-decode speedup by an order of magnitude (1.8x vs the actual ~11-41x) - describe the demo's live wiring (half-res locate, family-routed crop worker, full-frame 2D worker, position-tracked locks) - CLI decode line now lists App Clip Code and rotation-complete 1D (verified: anyd decode reads a rasterized App Clip Code PNG) - imgproc module list gains thin-plate-spline warps and orientation estimation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent c49b344 commit daa54b3

1 file changed

Lines changed: 38 additions & 17 deletions

File tree

‎README.md‎

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
A from-scratch Rust library for **decoding and encoding 1D and 2D barcodes**, aiming
99
for the widest possible symbology coverage while preserving every variation of a code.
1010

11-
**▶ [Live WASM demo](https://karpeleslab.github.io/anydcode/)** — encode to a canvas,
12-
decode an image, and run live-camera detection, all in the browser (the library
13-
compiled to WebAssembly, no wasm-bindgen).
11+
**▶ [Live WASM demo](https://karpeleslab.github.io/anydcode/)** — generate any
12+
symbology (Apple App Clip Codes included), decode an image, and run live-camera
13+
scanning with a tracked target-lock overlay, all in the browser (the library compiled
14+
to WebAssembly, no wasm-bindgen).
1415

1516
## Goals
1617

@@ -79,10 +80,16 @@ Legend: ✅ done · 🚧 in progress · ⬜ planned.
7980
| Royal Mail Mailmark (Barcode C & L) | ✅ | ✅ | ⬜ |
8081
| Australia Post 4-State · Japan Post 4-State | ✅ | ✅ | ⬜ |
8182

82-
¹ QR ships a full image sampler: Otsu binarization → finder-pattern detection →
83-
perspective recovery (via the bottom-right alignment pattern) → sub-pixel grid
84-
sampling → decode. Robust to any-angle rotation, moderate tilt, blur and noise
85-
(envelope documented in `tests/harness_image.rs`).
83+
¹ QR ships a hardened image sampler: a global-Otsu → adaptive (Bradley/Sauvola)
84+
binarization ladder, dual-axis finder detection with a synthesized-third-corner
85+
fallback, perspective recovery via the alignment pattern, and — for curved or
86+
degraded captures — a thin-plate-spline dewarp anchored on every finder, alignment
87+
pattern and timing-pattern crossing, biased module classification, and a
88+
Reed–Solomon-arbitrated brute force over all 32 format configurations when blur
89+
wrecks the format fields. Robust to any-angle rotation, tilt, blur, noise, uneven
90+
lighting and cylinder curvature; clean-render envelope in `tests/harness_image.rs`,
91+
real-capture envelope (bottle and can labels down to ≈4.6 px/module) in
92+
`tests/real_world.rs`.
8693

8794
² 1D detection is the shared **`scan1d`** luminance front-end (edge detection →
8895
run-length → normalized `LinearPattern`), feeding each symbology's decoder. The
@@ -143,7 +150,12 @@ assert_eq!(encoder.encode(&decoded).unwrap(), encoding);
143150
For a camera feed you rarely want to fully decode every frame. The `detect` module
144151
provides a fast, symbology-agnostic **locator** that returns the *position* of every
145152
code (a bounding quad + coarse family guess) in a single downscaled pass, **without
146-
decoding** — the cheap stage of the two-stage `Detect` → `Analyze` pipeline:
153+
decoding** — the cheap stage of the two-stage `Detect` → `Analyze` pipeline. It is
154+
recall-biased but structurally picky: concentric-finder detection backs matrix
155+
guesses, scene-sized merged blobs, text rows/columns masquerading as barcodes and
156+
non-code ink densities are gated out, overlapping duplicates of one physical code are
157+
suppressed, and a close-up code that defeats the texture pass is still reported from
158+
its finder hits alone:
147159

148160
```rust
149161
use anyd::detect::{locate, LocateOptions};
@@ -158,17 +170,24 @@ for c in &candidates {
158170
Benchmark (`cargo bench --bench detect`, dependency-free, `std::time::Instant`),
159171
locating **all** codes of all families per frame vs. a full QR decode:
160172

161-
| resolution | locate median | locate FPS | full-decode FPS | detect speedup |
162-
|-----------:|--------------:|-----------:|----------------:|---------------:|
163-
| 640×480 | 0.30 ms | ~3350 | ~1725 | 1.9× |
164-
| 1280×720 | 0.83 ms | ~1200 | ~635 | 1.9× |
165-
| 1920×1080 | 1.85 ms | ~545 | ~310 | 1.8× |
173+
| resolution | locate median | locate FPS | full QR decode | detect speedup |
174+
|-----------:|--------------:|-----------:|---------------:|---------------:|
175+
| 640×480 | 0.29 ms | ~3400 | 12.1 ms | ~41× |
176+
| 1280×720 | 0.70 ms | ~1430 | 14.1 ms | ~20× |
177+
| 1920×1080 | 1.53 ms | ~650 | 17.4 ms | ~11× |
166178

167179
Recall 100% and false-positive rate 0% on the benchmark's planted-code frames.
168180
Locating is comfortably real-time at every resolution; decoding then runs only on the
169181
regions the locator hands off (and `Hints` lets already-decoded codes be skipped
170182
across frames).
171183

184+
The demo page shows the intended live wiring: the main thread runs `locate` on a
185+
half-resolution grab ~30×/s, a crop worker decodes located regions off-thread —
186+
routed by family, so linear crops take the fast 1D path (`scan_1d` is
187+
rotation-complete via mirrored patterns and gradient-orientation derotation) — a
188+
second worker sweeps the full frame for self-localizing 2D codes, and decoded locks
189+
are position-tracked across frames so the overlay follows the code between reads.
190+
172191
## Command-line tool (`anyd`)
173192

174193
An optional binary is bundled behind the **`cli`** feature, so the library itself
@@ -184,7 +203,8 @@ $ anyd encode qr "https://example.com" --format png --out qr.png --scale 8
184203
$ anyd encode ean13 5901234123457 --format svg --out barcode.svg
185204
$ anyd encode qr "HELLO" --format unicode # half-block art in the terminal
186205

187-
# Decode a PNG (tries QR, Data Matrix, PDF417, and the 1D front-end)
206+
# Decode a PNG (tries QR, Data Matrix, PDF417, App Clip Code, and the
207+
# rotation-complete 1D front-end)
188208
$ anyd decode qr.png
189209
QR Code: https://example.com
190210

@@ -212,9 +232,10 @@ Live video: GrayFrame ──▶ Detect ──▶ Candidate ──▶ Analyze
212232
```
213233

214234
Supporting modules: **`imgproc`** (Otsu/adaptive binarization, connected components,
215-
4-point homography, sub-pixel grid sampling — the 2D sampler toolkit), **`scan1d`**
216-
(generic 1D luminance front-end), **`render`** (Encoding → grayscale) and
217-
**`transform`** (rotation/perspective/blur/noise) for the test harness.
235+
4-point homography, thin-plate-spline warps, dominant-orientation estimation,
236+
sub-pixel grid sampling — the 2D sampler toolkit), **`scan1d`** (generic 1D luminance
237+
front-end), **`render`** (Encoding → grayscale) and **`transform`**
238+
(rotation/perspective/blur/noise) for the test harness.
218239

219240
Correctness is anchored to independent reference vectors, not self-consistency: the
220241
ISO/IEC 18004 QR example, the ISO/IEC 16022 Data Matrix example, ISO/IEC 15438 +

0 commit comments

Comments
 (0)