Skip to content

Commit fa4d24c

Browse files
committed
fix: scanner improvements
1 parent c0a21bd commit fa4d24c

41 files changed

Lines changed: 5909 additions & 485 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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
112112
mise 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+
115137
For an Intel CPU/OpenVINO OCR trial, install the optional OCR dependencies and
116138
run 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

‎assets/react/src/gql/gql.ts‎

Lines changed: 18 additions & 18 deletions
Large diffs are not rendered by default.

‎assets/react/src/gql/graphql.ts‎

Lines changed: 12 additions & 12 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)