Fix clipped receipt margins, add time-of-day naming, speed up rotation detection

- vision/document.py: pad_quad() expands the detected quad outward
  before warping so a straight-line contour fit doesn't clip the first
  character of every line on a slightly curled/wrinkled receipt
- ocr/extract.py + llm/vision.py: extract transaction time (HH:MM) so
  same-day repeat visits to a vendor get distinct filenames
  (2023-03-20_1525_walmart.pdf vs. a later same-day trip)
- vision/orient.py: downscale before Tesseract OSD/confidence-sweep
  rotation detection — this was the dominant cost in the detection
  phase (7x speedup: 472s -> 67s on a 36s test video)
This commit is contained in:
2026-07-08 18:55:37 -04:00
parent a43ab20df6
commit 5be9d5935d
11 changed files with 160 additions and 12 deletions
+12
View File
@@ -29,6 +29,7 @@ _PROMPT = """Look at this image of a document. Reply with ONLY a JSON object, no
"vendor": "business or organization name, or null",
"form_code": "tax form code like T4/T4A/T5008 if this is a tax form, else null",
"date": "document/transaction date as YYYY-MM-DD, or null",
"time": "transaction time as 24-hour HH:MM if printed on the document, else null",
"tax_year": "tax year like 2023 if this is a tax form, else null",
"total": "total amount like 103.60, or null",
"description": "3-6 word summary",
@@ -43,6 +44,7 @@ class LlmAnalysis:
vendor: str | None
form_code: str | None
date: str | None # normalized YYYY-MM-DD
time: str | None # normalized HH:MM (24h)
tax_year: str | None
total: str | None
description: str
@@ -87,6 +89,15 @@ def _clean_str(value) -> str | None:
return text if text and text.lower() not in ("null", "none", "n/a", "unknown") else None
def _normalize_time(raw: str | None) -> str | None:
if not raw:
return None
match = re.search(r"([01]?\d|2[0-3]):([0-5]\d)", str(raw))
if not match:
return None
return f"{int(match.group(1)):02d}:{match.group(2)}"
def analyze_document(
image_bgr: np.ndarray,
model: str = "qwen2.5vl",
@@ -128,6 +139,7 @@ def analyze_document(
vendor=_clean_str(data.get("vendor")),
form_code=_clean_str(data.get("form_code")),
date=_normalize_date(data.get("date")),
time=_normalize_time(data.get("time")),
tax_year=tax_year,
total=_clean_str(data.get("total")),
description=_clean_str(data.get("description")) or "",