import os, json, glob, tempfile import gradio as gr from PIL import Image from receipt_extractor import ReceiptExtractor MODEL_ID = os.environ.get("MODEL_ID", "Tanishq71/sroie-layoutlmv3") MAX_SIDE = 1600 # downscale cap so OCR stays fast on free CPU print("Loading pipeline from", MODEL_ID) extractor = ReceiptExtractor(model_dir=MODEL_ID) print("Pipeline ready.") EMPTY = [["Company", ""], ["Date", ""], ["Address", ""], ["Total", ""]] # Pick up any receipt images placed in the examples/ folder (no hardcoded names) _example_files = sorted(glob.glob("examples/*.jpg") + glob.glob("examples/*.png")) EXAMPLES = [[f] for f in _example_files] def _downscale(img): w, h = img.size m = max(w, h) if m > MAX_SIDE: s = MAX_SIDE / m img = img.resize((int(w * s), int(h * s))) return img def predict(image): if image is None: return EMPTY, "Please upload a receipt image first.", None image = _downscale(image.convert("RGB")) with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp: image.save(tmp.name, format="JPEG") tmp_path = tmp.name try: entities, timings = extractor.extract_with_timing(tmp_path) finally: os.unlink(tmp_path) table = [ ["Company", entities["company"] or "(not detected)"], ["Date", entities["date"] or "(not detected)"], ["Address", entities["address"] or "(not detected)"], ["Total", entities["total"] or "(not detected)"], ] timing = "OCR %.1fs . Model %.2fs . Total %.1fs" % ( timings["ocr_s"], timings["model_s"], timings["total_s"]) json_path = os.path.join(tempfile.gettempdir(), "receipt_result.json") with open(json_path, "w") as f: json.dump(entities, f, indent=2) return table, timing, json_path def clear_all(): return None, EMPTY, "", None DESCRIPTION = """
Extracts Company, Date, Address, and Total from receipts using PaddleOCR + LayoutLMv3, fine-tuned on the SROIE dataset (Malaysian receipts).
SROIE test (347 receipts): macro F1 0.81 fuzzy · 0.42 exact
Date recall is boosted by a regex fallback when the model abstains · address fuzzy-F1 is 0.91
Runs on free CPU — large images are downscaled to 1600px (~5–20s). First load after inactivity takes 1–2 min (cold start).