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
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
from paperpod.capture.video import Frame
|
||||
from paperpod.vision.capture_events import find_document_events
|
||||
from paperpod.vision.document import DocumentDetection
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _frame(t: float, idx: int = 0) -> Frame:
|
||||
return Frame(index=idx, t=t, image=np.zeros((100, 100, 3), dtype=np.uint8))
|
||||
|
||||
|
||||
def _det() -> DocumentDetection:
|
||||
quad = np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=np.float32)
|
||||
return DocumentDetection(quad=quad, area_ratio=0.3, method="quad", source="edges", confidence=0.8)
|
||||
|
||||
|
||||
def test_single_event_from_consecutive_detections():
|
||||
samples = [(_frame(1.0, 0), _det()), (_frame(1.2, 1), _det()), (_frame(1.4, 2), _det())]
|
||||
events = find_document_events(samples, gap_s=2.0, min_samples=2)
|
||||
assert len(events) == 1
|
||||
assert events[0].t_start == 1.0
|
||||
assert events[0].sample_count == 3
|
||||
|
||||
|
||||
def test_gap_splits_separate_documents():
|
||||
samples = [
|
||||
(_frame(1.0, 0), _det()),
|
||||
(_frame(1.2, 1), _det()),
|
||||
(_frame(5.0, 2), _det()),
|
||||
(_frame(5.2, 3), _det()),
|
||||
]
|
||||
events = find_document_events(samples, gap_s=2.0, min_samples=2)
|
||||
assert len(events) == 2
|
||||
assert events[0].t_end == 1.2
|
||||
assert events[1].t_start == 5.0
|
||||
|
||||
|
||||
def test_min_samples_filters_single_frame_noise():
|
||||
samples = [(_frame(1.0, 0), _det()), (_frame(5.0, 1), _det()), (_frame(5.2, 2), _det())]
|
||||
events = find_document_events(samples, gap_s=2.0, min_samples=2)
|
||||
assert len(events) == 1
|
||||
assert events[0].t_start == 5.0
|
||||
|
||||
|
||||
def test_area_change_splits_documents():
|
||||
small = DocumentDetection(
|
||||
quad=np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=np.float32),
|
||||
area_ratio=0.25,
|
||||
method="quad",
|
||||
source="threshold",
|
||||
confidence=0.8,
|
||||
)
|
||||
large = DocumentDetection(
|
||||
quad=np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=np.float32),
|
||||
area_ratio=0.65,
|
||||
method="quad",
|
||||
source="threshold",
|
||||
confidence=0.8,
|
||||
)
|
||||
samples = [
|
||||
(_frame(1.0, 0), small),
|
||||
(_frame(1.2, 1), small),
|
||||
(_frame(1.4, 2), large),
|
||||
(_frame(1.6, 3), large),
|
||||
]
|
||||
events = find_document_events(samples, gap_s=2.0, min_samples=2)
|
||||
assert len(events) == 2
|
||||
assert events[0].detection.area_ratio == 0.25
|
||||
assert events[1].detection.area_ratio == 0.65
|
||||
Reference in New Issue
Block a user