The problem
Passport images captured in the field are skewed, badly lit, low-contrast and noisy. OCR on a raw capture either fails outright or, more dangerously, returns a plausible but wrong field — which means a human has to check every document, including the ones that were fine.
What I built
A quality-first OCR pipeline. Restoration happens before recognition: OpenCV applies CLAHE contrast equalisation, denoising, and a perspective de-skew that squares the document to the frame. EasyOCR then reads the restored image, and PassportEye validates the machine-readable zone using its own check digits.
The check-digit step is what makes the pipeline trustworthy: the MRZ carries arithmetic that a corrupted read fails, so most bad extractions are rejected by the format itself rather than by a reviewer noticing later.
How it works
Each document is scored on three axes — MRZ confidence, image clarity and alignment — and the combination drives an automated QA gate. Documents that pass are accepted without human involvement; only genuinely ambiguous captures are flagged. That gate cut the manual photo-review step for flagged passports.
I also benchmarked classical restoration against deep enhancers — DE-GAN, GFPGAN and NAFNet — on the same document set, rather than assuming a heavier model would win. The benchmark is part of the repository, so the choice of pipeline is reproducible instead of asserted.
Evidence
MRZ check-digit validation catches corrupted reads by construction; the three-axis QA gate (MRZ confidence + clarity + alignment) removed the manual photo-review step for flagged passports; classical restoration was benchmarked head-to-head against DE-GAN, GFPGAN and NAFNet.
What I owned
I built the restoration pipeline, the MRZ validation path, the benchmark and the QA gate.
Stack
Python · OpenCV (CLAHE, denoise, perspective de-skew) · EasyOCR · PassportEye · NumPy