- 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
74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
import cv2
|
|
import numpy as np
|
|
|
|
from paperpod.vision.refine import _density_band, refine_crop, skin_mask
|
|
|
|
|
|
def _crop_with_margin() -> np.ndarray:
|
|
"""White paper occupying the left 60% of a dark-mat crop."""
|
|
img = np.full((600, 500, 3), (60, 60, 60), dtype=np.uint8)
|
|
img[20:580, 20:300] = 245
|
|
return img
|
|
|
|
|
|
def _skin_patch(img: np.ndarray, y0: int, y1: int, x0: int, x1: int) -> None:
|
|
# BGR value squarely inside the YCrCb skin range.
|
|
img[y0:y1, x0:x1] = (120, 160, 210)
|
|
|
|
|
|
def test_tightens_to_paper_band():
|
|
result = refine_crop(_crop_with_margin(), remove_fingers=False)
|
|
assert result.tightened
|
|
h, w = result.image.shape[:2]
|
|
assert w < 350 # dark right margin removed
|
|
assert result.image.mean() > 150 # mostly paper now
|
|
|
|
|
|
def test_no_tighten_when_paper_fills_crop():
|
|
img = np.full((600, 500, 3), 245, dtype=np.uint8)
|
|
result = refine_crop(img, remove_fingers=False)
|
|
assert result.image.shape == img.shape
|
|
|
|
|
|
def test_skin_mask_detects_skin_tone():
|
|
img = _crop_with_margin()
|
|
_skin_patch(img, 0, 150, 350, 500)
|
|
mask = skin_mask(img)
|
|
assert (mask[50, 400] > 0) and (mask[300, 100] == 0)
|
|
|
|
|
|
def test_fingers_inpainted_when_touching_border():
|
|
img = _crop_with_margin()
|
|
_skin_patch(img, 0, 120, 100, 220) # finger reaching in from the top edge
|
|
result = refine_crop(img, tighten=False, remove_fingers=True)
|
|
assert result.fingers_removed
|
|
# Skin tone should be gone from the finger region.
|
|
assert skin_mask(result.image).sum() < skin_mask(img).sum() * 0.2
|
|
|
|
|
|
def test_interior_skin_tone_not_inpainted():
|
|
"""A skin-colored logo in the middle of the page must not be smeared."""
|
|
img = np.full((600, 500, 3), 245, dtype=np.uint8)
|
|
_skin_patch(img, 250, 300, 200, 260)
|
|
result = refine_crop(img, tighten=False, remove_fingers=True)
|
|
assert not result.fingers_removed
|
|
|
|
|
|
def test_density_band_ignores_far_edge_strip():
|
|
# Paper columns 0-59, gap, thin bright strip at the far edge.
|
|
density = np.zeros(100)
|
|
density[0:60] = 0.9
|
|
density[97:100] = 0.9
|
|
band = _density_band(density, threshold=0.25, gap_fraction=0.1)
|
|
assert band is not None
|
|
x0, x1 = band
|
|
assert x0 == 0 and x1 < 80
|
|
|
|
|
|
def test_density_band_bridges_dark_print_on_paper():
|
|
# Paper band with a dark printed banner (low density) in the middle.
|
|
density = np.full(100, 0.9)
|
|
density[40:48] = 0.05
|
|
band = _density_band(density, threshold=0.25, gap_fraction=0.15)
|
|
assert band == (0, 99)
|