01 — Overview

What this project is.

The receipt detection system is an end-to-end ML product: a fine-tuned object detection model capable of localising receipts within arbitrary photographs, wrapped in a production-grade web application with authentication and a CDN-served frontend.

The core problem is simple — given a photo that may or may not contain a receipt, draw a tight bounding box around any receipt present. The receipts appear at every angle, under variable lighting, partially obscured, crumpled, or on cluttered surfaces. A general-purpose detector struggles; a purpose-trained one does not.

This writeup covers every decision: where the data came from, how I labelled it, why YOLO was chosen, how I prevented overfitting with negative samples, and how the entire system is deployed on AWS.

YOLO small Python FastAPI AWS ECS Fargate Amazon Cognito CloudFront LabelImg Ultralytics

02 — Dataset

Building a balanced dataset.

Good models are made of good data. I combined two sources to create a dataset that is both comprehensive in receipt variety and robust against false positives.

Source A — Receipt Images
  • DatasetVoxel51 Consolidated Receipt
  • HostHugging Face
  • ContentDiverse receipt photos
  • LabellingManual (LabelImg)
  • Classreceipt
Source B — Hard Negatives
  • DatasetCOCO 2017
  • HostHugging Face (awsaf49)
  • Count800 images
  • ContentRandom non-receipt scenes
  • ClassNone (background only)

The Voxel51 Consolidated Receipt Dataset is a curated collection of real-world receipt images spanning diverse lighting conditions, receipt types (grocery, restaurant, retail), and photographic angles. I manually labeled 800 images from this dataset, which initially formed the positive class for training.

After training the first version of the model, I observed clear signs of overfitting along with a high rate of false positives. The model frequently misclassified non-receipt objects—particularly text-heavy or rectangular surfaces—as receipts. To address this imbalance, I introduced 800 non-receipt images sampled randomly from the COCO 2017 dataset. These images were included with empty annotation files, explicitly teaching the model that such scenes do not contain receipts. This significantly reduced false positives and improved generalization.

In a later evaluation phase, I identified another limitation: the model struggled to detect receipts captured at angles. To improve robustness to orientation, I augmented the dataset by rotating 800 receipt images by 45 degrees to the left and right, generating an additional 1,600 images.

Through this iterative process—initial labeling, dataset balancing with negative samples, and targeted augmentation—the final dataset was expanded to a total of 3,000 images, resulting in a more generalizable and reliable receipt detection model.

Design note

Including hard negatives is often undervalued in custom dataset construction. Without them, a model trained only on positives will frequently hallucinate detections on white paper, price tags, or books — any rectangular, text-bearing surface. The 800 COCO negatives act as a regulariser at the data level.

03 — Labelling

Manual annotation with LabelImg.

Every receipt image was labelled by hand using LabelImg, an open-source graphical annotation tool. I drew tight bounding boxes in YOLO format — each annotation saved as a .txt file containing the normalised centre coordinates, width, and height of each receipt.

Manual labelling is slow, but it matters. Automated pipelines applied to noisy web-scraped data tend to produce annotations with loose or incorrect boxes. With receipts in particular, the boundary between the receipt and the background can be subtle — especially on white tablecloths or desks. Hand-labelling forces you to make that judgment call precisely for every example.

1

Load image in LabelImg

Open the receipt photo. Set label format to YOLO (produces normalised coordinates relative to image dimensions).

2

Draw tight bounding box

Drag a rectangle around the full visible extent of the receipt — top-left corner to bottom-right, including edges. Label as receipt.

3

Save annotation

LabelImg writes a .txt file alongside the image. Format: class cx cy w h (all normalised 0–1).

4

Organise with COCO negatives

COCO images get empty .txt files. Both positive and negative images are placed in the same directory structure expected by Ultralytics.

5

Train/val split

Dataset shuffled and split: ~90% training, ~10% validation. Split is stratified to maintain the positive/negative ratio in both sets.

The COCO negative images receive empty annotation files — a YOLO convention meaning "no objects of interest in this image." This is important: if you simply omit the annotation file, Ultralytics may skip the image during training. An empty file is an explicit instruction.

04 — Model & Training

Why YOLO.

The YOLO (You Only Look Once) family has become the default choice for real-time object detection. Version 8, released by Ultralytics, brings a cleaner codebase, improved anchor-free detection heads, and excellent transfer learning support. I chose the small variant deliberately.

This is a single-class detection problem with relatively consistent object morphology — receipts are rectangular, white or off-white, with dense text. There is no need for a large backbone. YOLO offers a strong accuracy/speed tradeoff: it's fast enough for real-time inference on a Fargate container with no GPU, yet capable enough to handle the geometric variation in the dataset.

Training config
  • FrameworkUltralytics
  • Pretrained weightsYOLOv8s
  • OptimiserSGD / AdamW
  • AugmentationMosaic, Mixup, HSV
  • HardwareT4 GPU
  • Epocs50

Transfer learning is key here. Starting from pretrained weights means the backbone already understands low-level visual features: edges, textures, corners, and gradients. Fine-tuning then redirects this prior knowledge toward the specific task of receipt localisation, converging faster and with less data than training from scratch.

Ultralytics' built-in augmentation pipeline — mosaic composition, random flipping, HSV colour jitter — provides additional regularisation without requiring a separate preprocessing step, keeping the training code compact and reproducible.

05 — System Architecture

From inference to browser.

The model is one piece of a larger system. The full production stack handles authentication, secure image upload, model inference, and result delivery — all serverless or containerised, with no infrastructure to manage directly.

Full System Architecture AWS Production
Full AWS architecture diagram for the Receipt Detection System

The architecture follows a clean separation of concerns across three layers: authentication, inference, and content delivery.

1

Frontend — CloudFront + S3

Static HTML/CSS/JS assets are hosted on S3 and served globally via CloudFront. The frontend handles image selection, canvas rendering of bounding boxes, and result display.

2

Authentication — Amazon Cognito

Users authenticate via a Cognito User Pool using the OAuth 2.0 authorization code flow. A Lambda proxy exchanges the code for tokens server-side, avoiding CORS issues and keeping secrets off the client.

3

API Gateway

API Gateway validates the Bearer token on every request and routes authenticated inference calls to the ECS service. Unauthenticated requests are rejected at the gateway level — the model container never sees them.

4

Inference — FastAPI on ECS Fargate

A FastAPI application runs in a Docker container on ECS Fargate. It receives the image, runs the YOLO model, and returns bounding box coordinates and confidence scores as JSON. Fargate eliminates server management entirely.

5

Result rendering

The frontend receives the JSON response, overlays bounding boxes on the image using an HTML Canvas element, and displays confidence-coded badges for each detection.

Security note

The token exchange happens via a Lambda function rather than directly from the browser to Cognito. This prevents the client secret from ever being exposed in frontend code — a common mistake in OAuth implementations. The Lambda holds the secret securely via environment variables and performs the exchange server-side.

06 — Results

Model performance.

After 50 epochs of fine-tuning on a 3,000-image dataset, the model achieves strong, production-ready detection performance. Evaluated on the held-out validation set, it correctly localises receipts with high accuracy across the full range of IoU thresholds — not just the lenient 50% cutoff.

90.5%

mAP@50

89.5%

mAP@50–95

88.1%

Precision

96.6%

Recall

92.1%

F1 Score

MetricValueNotes
mAP@0.590.5%Strong single-class localisation performance
mAP@0.5:0.9589.5%Only 1% drop vs mAP@50 — very tight box predictions
Precision88.1%~12% of detections are false positives; tunable via conf threshold
Recall96.6%Misses fewer than 1 in 30 real receipts
F1 Score92.1%Best harmonic balance of precision and recall
Inference time~80–120 msCPU-only, ECS Fargate — acceptable for interactive use
False positives on COCOVery lowHard negative training confirmed effective
Metric Breakdown Validation Set · receipt_v3_50e
Confusion Matrix 231 validation samples
Confusion matrix showing 199 true positives, 26 false negatives, 6 false positives

199

True Positives

26

False Negatives

6

False Positives

199 receipts correctly detected  ·  26 missed  ·  6 spurious background fires

The confusion matrix tells the clearest story. Of 225 ground-truth receipt instances, 199 were correctly detected. Only 26 were missed (false negatives), and just 6 background images triggered a spurious detection — a false positive rate low enough to be essentially invisible in real-world use.

The asymmetry between false negatives (26) and false positives (6) directly explains the recall-precision gap seen in the metrics. The model has learned to be conservative about background — a direct consequence of the 800 COCO hard negatives introduced during training. The missed receipts are concentrated in heavily occluded or extreme-angle cases, which rotational augmentation improved but did not fully resolve.

The near-identical mAP@50 (90.5%) and mAP@50–95 (89.5%) is particularly significant. Most detectors lose 15–20 points across that IoU sweep; a 1-point drop indicates the model is predicting very tight, accurate bounding boxes — not just approximately correct regions.

Threshold note

Reported metrics use the default conf=0.25. Because recall (96.6%) outpaces precision (88.1%), raising the inference threshold to conf=0.40–0.45 will reduce false positives at the cost of a small number of true detections — a trade-off tunable without retraining.

What's next

The receipt detector is the first piece of a larger system. Detected receipts feed a forthcoming RAG pipeline that extracts line items and enables natural language queries over personal expense history — combined with an agentic layer for autonomous budget analysis.