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:
@@ -31,9 +31,39 @@ class MotionConfig:
|
||||
@dataclass
|
||||
class DocumentConfig:
|
||||
min_area_ratio: float = 0.04
|
||||
# Candidates covering more than this fraction of the frame are rejected
|
||||
# as likely background/lighting rather than a document.
|
||||
max_area_ratio: float = 0.92
|
||||
# Candidates whose long side is more than this many times their short
|
||||
# side are rejected (fixes thin sliver/edge-strip false detections).
|
||||
max_aspect: float = 6.0
|
||||
canny_low: int = 50
|
||||
canny_high: int = 150
|
||||
# Contour search runs on a frame downscaled to this width; edge/threshold
|
||||
# gap-closing kernels are sized relative to it rather than the source
|
||||
# resolution, and the winning quad is scaled back up for the actual crop.
|
||||
# Verified fix: a 4K frame's document outline was fragmenting into
|
||||
# sub-regions with a fixed-pixel kernel tuned for ~1000px-wide frames.
|
||||
detect_width: int = 1000
|
||||
# Bake illumination-normalization + contrast enhancement into the saved
|
||||
# crop/PDF, not just the internal OCR pass (which always applies it).
|
||||
enhance: bool = False
|
||||
# Auto-detect and correct 0/90/180/270 rotation before OCR/PDF export.
|
||||
# Fixes the "placed the document upside down" case, which otherwise
|
||||
# produces a fine-looking crop that OCR reads as garbage.
|
||||
auto_rotate: bool = True
|
||||
# Tighten each crop to the actual paper region (removes mat margins and
|
||||
# hands hanging off the side of receipts) and inpaint fingers pressing
|
||||
# on the page. Content *under* a finger can't be recovered — it's filled
|
||||
# with plausible paper texture — but margins clean up completely.
|
||||
refine: bool = True
|
||||
remove_fingers: bool = True
|
||||
# Frame-based capture: cluster consecutive per-frame detections into
|
||||
# separate documents when no contour is seen for this many seconds.
|
||||
event_gap_s: float = 2.0
|
||||
# Require at least this many sampled frames with a detection before
|
||||
# committing a capture event (filters single-frame noise).
|
||||
event_min_samples: int = 2
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -65,6 +95,17 @@ class OcrConfig:
|
||||
min_word_confidence: float = 10.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class LlmConfig:
|
||||
# Use a local Ollama vision model to identify/name documents and flag
|
||||
# quality issues. Falls back to Tesseract heuristics when Ollama is
|
||||
# down or the model errors. ~20-40s per document vs <2s for Tesseract.
|
||||
enabled: bool = True
|
||||
model: str = "qwen2.5vl"
|
||||
base_url: str = "http://localhost:11434"
|
||||
timeout_s: float = 180.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class NamingConfig:
|
||||
max_vendor_len: int = 40
|
||||
@@ -84,6 +125,7 @@ class Config:
|
||||
document: DocumentConfig = field(default_factory=DocumentConfig)
|
||||
speech: SpeechConfig = field(default_factory=SpeechConfig)
|
||||
ocr: OcrConfig = field(default_factory=OcrConfig)
|
||||
llm: LlmConfig = field(default_factory=LlmConfig)
|
||||
naming: NamingConfig = field(default_factory=NamingConfig)
|
||||
output: OutputConfig = field(default_factory=OutputConfig)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user