PaperPod/tests/test_orient.py
ilia a43ab20df6 Add crop refinement, finger removal, and local LLM naming
- vision/refine.py: tighten crops to the paper band (removes mat
  margins and hands beside receipts) and inpaint border-connected
  skin regions so fingers disappear from output
- llm/vision.py: identify documents with a local Ollama vision model
  (qwen2.5vl); extracts vendor/date/total/form code and flags quality
  issues (fingers, blur, glare); falls back to Tesseract when down
- pipeline: drop blank pages, dedupe consecutive captures of the same
  document, record refine/LLM fields in report and export summary
2026-07-08 18:23:50 -04:00

78 lines
2.4 KiB
Python

"""End-to-end orientation-detection tests against synthetic text pages.
Skipped automatically if the tesseract binary isn't installed.
"""
from __future__ import annotations
import shutil
import cv2
import numpy as np
import pytest
from PIL import Image, ImageDraw, ImageFont
from paperpod.vision.orient import apply_rotation, auto_rotate
pytestmark = pytest.mark.skipif(
shutil.which("tesseract") is None, reason="tesseract binary not installed"
)
_FONT_CANDIDATES = [
"/System/Library/Fonts/Helvetica.ttc",
"/System/Library/Fonts/Supplemental/Arial.ttf",
]
def _font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
for path in _FONT_CANDIDATES:
try:
return ImageFont.truetype(path, size)
except OSError:
continue
return ImageFont.load_default()
def _text_page() -> np.ndarray:
"""A tall white page with a few lines of real words (upright, 0 degrees).
Rendered with a real TrueType font (not cv2.putText's blocky stroke
font), which produces normal glyph shapes; cv2.putText text fragments
into dozens of spurious single-character "words" when rotated sideways,
which isn't representative of real documents/photos.
"""
img = Image.new("RGB", (400, 600), (255, 255, 255))
draw = ImageDraw.Draw(img)
font = _font(22)
lines = [
"INVOICE FROM ACME CORP",
"Date: January 5, 2024",
"Description of services rendered",
"Thank you for your business",
"Please remit payment within 30 days",
]
for i, line in enumerate(lines):
draw.text((20, 80 + i * 60), line, fill=(0, 0, 0), font=font)
return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
@pytest.mark.parametrize("applied_rotation", [0, 90, 180, 270])
def test_auto_rotate_corrects_known_rotation(applied_rotation):
"""Rotating upright text by N clockwise needs (360-N) clockwise to undo."""
upright = _text_page()
rotated_input = apply_rotation(upright, applied_rotation)
corrected, result = auto_rotate(rotated_input)
assert result.rotation == (360 - applied_rotation) % 360
assert corrected.shape == upright.shape
def test_apply_rotation_identity_at_zero():
page = _text_page()
assert np.array_equal(apply_rotation(page, 0), page)
def test_apply_rotation_180_is_involutive():
page = _text_page()
twice = apply_rotation(apply_rotation(page, 180), 180)
assert np.array_equal(twice, page)