@@ -14,9 +14,9 @@ API, SQLite storage, local file uploads, and an optional Capacitor mobile shell.
1414
1515- Own your data: production data lives in a local SQLite database and local
1616 upload directories under ` /data ` .
17- - Scan real cards quickly: camera captures are processed with RapidOCR, local
18- Scryfall matching, review queues, and image/art matching for exact-printing
19- confidence .
17+ - Scan real cards quickly: camera captures are cropped to the card guide, matched
18+ against a local Scryfall art hash index first, then RapidOCR is used only for
19+ fallback and exact-printing disambiguation .
2020- Treat decks as physical commitments: deck cards can be allocated to concrete
2121 collection items so one copy cannot accidentally be promised to multiple decks.
2222- Turn gaps into action: missing-card reports and buylist exports show what a
@@ -112,6 +112,28 @@ python3 -m venv .venv
112112mise exec -- mix manavault.ocr.setup
113113```
114114
115+ Build or refresh the complete local art hash index used by the art-first scanner
116+ path:
117+
118+ ``` sh
119+ mise exec -- mix manavault.scanner.art_index
120+ ```
121+
122+ Art-first live scanning refuses partial indexes because a nearest neighbor from a
123+ small subset can be confidently wrong. ` --limit ` is only for development and
124+ benchmarks.
125+
126+ Run the scanner benchmark against synthetic camera captures instead of perfect
127+ Scryfall images:
128+
129+ ``` sh
130+ mise exec -- mix manavault.ocr.benchmark --indexed-art --synthetic-camera --limit 10
131+ ```
132+
133+ In dev, rejected scanner frames are kept under
134+ ` data/uploads/scan-captures/scan_sessions/<session_id>/ ` so phone-camera samples
135+ can be reused for local benchmarks and crop tuning.
136+
115137For an Intel CPU/OpenVINO OCR trial, install the optional OCR dependencies and
116138run setup with the engine selected:
117139
@@ -370,11 +392,24 @@ Common optional values:
370392 Defaults to unset, which lets OpenVINO choose.
371393- ` MANAVAULT_OCR_TITLE_WIDTH` - pixel width for the small OCR crop used during
372394 camera scans. This crop includes the card title and footer/set line. Defaults
373- to `640 `.
395+ to `192 `.
374396- ` SCAN_IMAGE_MATCHING` - set to `false` to disable candidate image matching
375- during camera scans and use OCR-only recognition. Defaults to `true`. With the
376- title fast path enabled, image matching runs in the background to refine the
377- exact printing after the card name is recognized.
397+ during camera scans and use OCR-only recognition. Defaults to `true`. Global
398+ art-first matching only runs when the local art hash index covers the catalog;
399+ until then, live scans go straight to OCR-narrowed candidate image matching.
400+ The art hash index is built newest-printing-first and incrementally in the
401+ background on app startup and refreshed after catalog imports; each batch is
402+ persisted and logged so a full first-time build shows progress instead of going
403+ silent. Completed art matching keeps only the best ranks in memory while
404+ scoring, avoiding a full sort of the 100k+ hash index for every camera frame.
405+ Set `SCAN_ART_INDEX_WORKER=false` to disable the background art-index builder.
406+ - ` SCAN_CAPTURE_REQUIRES_ART_MATCH` - set to `false` to let live camera captures
407+ auto-accept OCR-only matches when image matching misses. Defaults to `true`, so
408+ camera captures require either an art-index hit or OCR narrowed candidates that
409+ pass candidate-scoped image matching.
410+ - ` SCAN_KEEP_REJECTED_CAPTURES` - set to `true` to keep rejected camera frames
411+ on disk for scanner debugging. Development config enables this; production
412+ defaults to `false`.
378413- ` SCAN_TITLE_OCR_FAST_PATH` - set to `false` to disable the title-crop OCR
379414 fast path and always OCR the full capture. Defaults to `true`.
380415- ` SCAN_ASYNC_IMAGE_REFINEMENT` - set to `false` to stop background exact
@@ -383,6 +418,18 @@ Common optional values:
383418- ` SCAN_FULL_OCR_FALLBACK` - set to `false` to prevent camera scans from
384419 falling back to blocking full-card OCR when the title/footer crop is weak.
385420 Defaults to `true` for production scanner reliability.
421+
422+ Scanner timing is emitted as Telemetry spans and debug logs. The main stop events are :
423+
424+ - ` [:manavault, :scanner, :capture, :stop]` — full live-capture request.
425+ - ` [:manavault, :scanner, :capture_write, :stop]` — frame persistence.
426+ - ` [:manavault, :scanner, :recognition, :stop]` — OCR/image recognition.
427+ - ` [:manavault, :scanner, :ocr, :stop]` — one OCR call, tagged by `ocr_crop`.
428+ - ` [:manavault, :scanner, :image_match, :stop]` — image matching, tagged by
429+ ` phase` (`initial`, `candidate`, or `refinement`).
430+ - ` [:manavault, :scanner, :candidate_match, :stop]` — OCR candidate scoring.
431+ - ` [:manavault, :scanner, :persist, :stop]` — recognized scan item persistence.
432+ - ` [:manavault, :scanner, :refinement, :stop]` — async exact-printing refinement.
386433- ` MANAVAULT_SKIP_MIGRATION_BACKUP` - skip automatic release backup before
387434 pending migrations. Defaults to unset.
388435
0 commit comments