Case study · build v0.1
RCPT
A receipt is a photograph of a table that somebody printed badly, folded, and then kept in a wallet for three weeks. RCPT turns that into rows on the phone with the radio off — not because offline is fashionable, but because a receipt is the one document you are holding at the exact moment you have no signal.
- Platform
- Android
- Stack
- Kotlin · PaddleOCR · TFLite · SQLite
- Network
- Not required
- Output
- Merchant, total, tax, date, lines
- Status
- Under build
- Source
- rcpt-public on GitHub
01
The constraint
Offline-first is a budget, not a feature. Every model, every lookup table, every megabyte has to fit on the device and finish inside the time a person will hold a phone over a counter. There is no server to hide behind and no second pass to fix an answer later.
So the pipeline is fixed at capture: camera, preprocessing, OCR, parsing, storage. Each stage can only use what the previous one produced, which sounds limiting and is the reason a bad result can be traced to one stage instead of five.
02
The pipeline
- Capture
- Camera into a single image buffer. Everything downstream is deterministic, which is what makes a failure reproducible.
- Preprocess
- Deskew, crop and a contrast pass before anything reads the image. Accuracy on a crumpled thermal receipt is decided here more than in the model.
- Read
- PaddleOCR on-device through TensorFlow Lite. Detection order is preserved, because a receipt is a document with a reading order, not a bag of words.
- Parse
- Rule-based extraction for merchant, total, tax, date and line items. Rules, not a model, at this size.
- Store
- SQLite. One local database, no sync, no account, and no upload button to remember not to press.
03
Why the parsing is rules
Parsing looks like the small half and is not. A model for field extraction needs labelled receipts per merchant, and the long tail of merchants is exactly the part you cannot label in advance. A rule set needs an afternoon per chain and fails legibly: when it gets a total wrong you can read the rule and see why.
The trade-off is honest. Rules do not generalise to a merchant nobody has written a rule for. At one merchant at a time that is a smaller problem than a model that is quietly wrong about a little of everything, and cannot tell you which little.
04
Where it breaks
- Thermal paper fades
- Within months it is grey. There is a contrast floor below which no amount of preprocessing recovers a glyph.
- The total moves
- TOTAL, TOTAL:, Amount Due, Grand Total — and on some prints, a number at the bottom with no label at all.
- Tax is not one thing
- Sometimes a rate, sometimes an amount, and discounts land before tax on some prints and after it on others.
- Glyph collisions
- Zero and capital O come out of the same font slot on some printers. That is an OCR problem that becomes a parsing problem.
05
Next
A labelled corpus first, then a small model for the fields the rules lose on. Not the other way round. A corpus is worth something on its own; a model without one is a guess with a build step.
OCR is the easy half. It reads every character correctly and still has no idea what the number at the bottom of the page means.