Compare commits
1 Commits
v0.5.1
...
68e0cd8967
| Author | SHA1 | Date | |
|---|---|---|---|
| 68e0cd8967 |
@@ -0,0 +1,82 @@
|
||||
# 3-variant OCR cross-referencing experiment
|
||||
|
||||
Tested whether running Tesseract over three image variants of the same
|
||||
receipt photo (original color photo, plain grayscale, and the existing
|
||||
denoised/contrast-enhanced preprocessing) and asking the extraction LLM
|
||||
to cross-reference all three per field would recover more correct data
|
||||
than the single-OCR-pass baseline, particularly for blurry/low-quality
|
||||
photos.
|
||||
|
||||
## Result
|
||||
|
||||
Mixed. One more receipt matched overall, two fields improved
|
||||
meaningfully, but the original target field (`litres`) did not improve,
|
||||
and `total_price` regressed.
|
||||
|
||||
| Metric | Baseline (1 OCR pass) | New (3-variant cross-ref) |
|
||||
|---|---|---|
|
||||
| Matched to ground truth | 15/17 | 16/17 |
|
||||
| price_per_litre | 11/15 (73%) | 13/16 (81%) ↑ |
|
||||
| litres | 9/15 (60%) | 9/16 (56%) — unchanged, still worst |
|
||||
| discount | 11/15 (73%) | 15/16 (94%) ↑↑ |
|
||||
| total_price | 12/15 (80%) | 11/16 (69%) ↓ |
|
||||
|
||||
## Root causes identified
|
||||
|
||||
1. **The litres/price-per-litre line is the hardest OCR target**, regardless
|
||||
of variant count. Noise characters (`x2p 02`, `*/p`) precede the real
|
||||
numbers and the litre symbol `l` reads as `1`/`]`/`|` in all three
|
||||
variants alike, so this field stays wrong at the same rate.
|
||||
2. **Three OCR readings don't guarantee the model picks the right one.**
|
||||
On the blurry Jet receipt, the correct total `57,13` survives intact in
|
||||
the original-photo variant, but the model still output `37.13`. Extra
|
||||
(noisy) input sometimes confuses the small 3B model rather than helping
|
||||
it reconcile.
|
||||
3. **`total_price` regressed** — cross-referencing conflicting reads
|
||||
apparently made the model second-guess fields it used to get right from
|
||||
a single clean reading.
|
||||
4. **Severe performance/stability cost** on this 2-core, GPU-less VM:
|
||||
extraction latency became wildly inconsistent (1–19 minutes per receipt
|
||||
vs. seconds before), and one run hit a hard timeout before the request
|
||||
timeout was raised from 300s → 900s. The full 17-receipt batch that used
|
||||
to finish in ~11 minutes now takes well over an hour.
|
||||
|
||||
## Working examples (fully correct — all 4 fields + date matched)
|
||||
|
||||
- `IMG_20260726_095120207`, `095321692`, `095428941`, `095442114`,
|
||||
`095510481`, `095519964` — all Shell receipts with clean, high-contrast
|
||||
OCR text across all three variants.
|
||||
|
||||
## Not-working examples
|
||||
|
||||
- **`IMG_20260525_182252232`** (the blurry Jet receipt that prompted this
|
||||
experiment) — still no date match at all (extracted `13.05.2026` vs. true
|
||||
`15.05.2026`). All three OCR variants scramble the header; even though
|
||||
the correct total `57,13` is legible in the original-photo variant, the
|
||||
model output `37.13` instead.
|
||||
- **`IMG_20260525_182144541`** — worst case, everything wrong except
|
||||
date/time/transaction number: extracted `litres=1.205` (should be ~23),
|
||||
`price_per_litre=1.389` (plausible alone, but paired with nonsense
|
||||
litres), `discount=-0.08` (sign flipped), `total_price=5.33`. All three
|
||||
OCR variants are near-unreadable for this receipt.
|
||||
- **`IMG_20260525_182116766`** — `litres` read correctly (`20.24`, matches
|
||||
the OCR text `"20,24 1 1979 EUR/]"` cleanly) but `price_per_litre` came
|
||||
out as `6.01` instead of `~1.979` — a pure extraction/reconciliation
|
||||
error, not an OCR gap, since the right number is sitting right there in
|
||||
the text.
|
||||
- **`IMG_20260726_095312079`, `095335197`, `095416655`, `095452747`,
|
||||
`095501803`** — all wrong only on `litres`, the recurring weak field.
|
||||
- **`IMG_20260726_095215757`** — wrong on `litres` and `total_price`
|
||||
together; the OCR text for that line is heavily corrupted
|
||||
(`j 2 N N 4) ha 1: Hi A 1.869 EUR/`) in all three variants, so this one
|
||||
genuinely lacks clean signal anywhere.
|
||||
|
||||
## Conclusion / recommendation
|
||||
|
||||
The extra OCR variants help in a few places (`discount`, `price_per_litre`)
|
||||
but don't fix the core problem (`litres` line noise), introduce a new
|
||||
failure mode (the model mis-reconciling conflicting-but-sometimes-correct
|
||||
reads), and cost a lot of latency/stability on this hardware. Leaning
|
||||
toward reverting to single-OCR unless a confidence-triggered fallback
|
||||
(only pull extra variants when the primary reading looks incomplete) is
|
||||
tried instead.
|
||||
Reference in New Issue
Block a user