OCR pipeline for document photos

OCR pipeline for document photos

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1284
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1240
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    917
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1031

OCR pipeline for document photos

A photo of a document is not a scan. Typical problems: perspective distortion (text becomes trapezoidal), shadows from fingers and spine, uneven lighting, motion blur, reflections on glossy surfaces. A high-quality recognition system must correct all these distortions before sending to an OCR engine. Without preprocessing, even the best models (PaddleOCR, GPT-4o) show CER of 20–30% on such photos. For passports and IDs, CER <5% is required — achievable only with a comprehensive pipeline.

We developed a solution that automatically detects the document, aligns perspective, removes shadows and glare, and only then runs OCR. This approach yields consistently low error even on imperfect shots. Our pipeline processes a frame in ~200 ms on GPU and has been deployed in projects for banks and government organizations — over 5 years of experience, more than 20 successful integrations.

How we align the document in the photo?

The first task is to find the document in the frame and correct perspective. Typical situation: a user photographs a passport at an angle, and the text becomes unreadable for regular OCR. We use contour detection after preprocessing: gray conversion, blur, Canny edge detection, then search for a quadrilateral that occupies >20% of the frame. If such a contour is found, we apply perspective transformation (homography). This gives a front-facing view of the document.

import cv2 import numpy as np class DocumentPhotoOCR: def __init__(self): self.ocr = PaddleOCR(use_angle_cls=True, lang='ru', use_gpu=True) def process(self, image_path: str) -> dict: image = cv2.imread(image_path) # 1. Detect document in frame doc_corners = self.detect_document(image) # 2. Perspective correction if doc_corners is not None: image = self.four_point_transform(image, doc_corners) # 3. Image enhancement image = self.enhance_document(image) # 4. OCR result = self.ocr.ocr(image, cls=True) return { 'text': self._extract_text(result), 'words': self._extract_words_with_positions(result), 'corrected': doc_corners is not None } def detect_document(self, image: np.ndarray) -> np.ndarray | None: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) edges = cv2.Canny(blur, 75, 200) dilated = cv2.dilate(edges, np.ones((3, 3)), iterations=1) contours, _ = cv2.findContours(dilated, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5] for contour in contours: peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * peri, True) if len(approx) == 4: area_ratio = cv2.contourArea(approx) / (image.shape[0] * image.shape[1]) if area_ratio > 0.2: return approx.reshape(4, 2) return None def four_point_transform(self, image: np.ndarray, pts: np.ndarray) -> np.ndarray: rect = self._order_points(pts) tl, tr, br, bl = rect width = int(max(np.linalg.norm(br - bl), np.linalg.norm(tr - tl))) height = int(max(np.linalg.norm(tr - br), np.linalg.norm(tl - bl))) dst = np.array([ [0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1] ], dtype='float32') M = cv2.getPerspectiveTransform(rect.astype('float32'), dst) return cv2.warpPerspective(image, M, (width, height)) 

Result: even at tilt up to 30°, text becomes horizontal, which radically improves recognition quality.

What about shadows and glare?

The next problem is uneven lighting. Real photos often have shadows from fingers, binding, or glare on laminated cards. For shadows, we apply CLAHE (Contrast Limited Adaptive Histogram Equalization) in LAB color space. This adaptively equalizes brightness locally without creating artifacts. For glare, we use inpainting — detect overexposed pixels (value >250 in any RGB channel) and interpolate them from neighboring areas.

def enhance_document(self, image: np.ndarray) -> np.ndarray: lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(16, 16)) l_enhanced = clahe.apply(l) enhanced = cv2.merge([l_enhanced, a, b]) enhanced = cv2.cvtColor(enhanced, cv2.COLOR_LAB2BGR) kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) sharpened = cv2.filter2D(enhanced, -1, kernel) return sharpened 

We also add a light sharpen to compensate for blur from handheld shots. Our experience shows that the combination of CLAHE + sharpen reduces CER by 3–5% compared to raw images.

Full pipeline for document photo processing

Here is our production pipeline (Python code with OpenCV and PaddleOCR). The functions are combined into one class. We use PaddleOCR with Russian language and angle classification (use_angle_cls=True). On GPU, processing one frame takes ~200 ms. For mobile devices, a lightweight model can be substituted.

Comparison of OCR engines on real photos

Engine CER (tilt/shadows) Speed on GPU
PaddleOCR 3–7% ~200 ms
Tesseract 5 8–15% ~50 ms (no preprocessing)
EasyOCR 5–10% ~300 ms

PaddleOCR offers the best balance of accuracy and speed for Russian-language documents. Without our preprocessing pipeline, any OCR engine shows 5–10% worse CER. Using the combination of OpenCV and PaddleOCR, we achieve CER <5% for passports — 1.5–3 times better than without preprocessing. Our pipeline processes a frame 2x faster than standard solutions with similar quality.

Implementation process

Turnkey implementation includes:

  1. Analysis: examining document types, shooting conditions, accuracy requirements, selecting reference images.
  2. Design: stack selection (OpenCV, PyTorch/TensorFlow, OCR engine), pipeline architecture, container configuration.
  3. Development: writing detection, correction, OCR modules; integration with backend via REST API.
  4. Testing: on your dataset (minimum 500 images), measuring CER, latency p99, debugging edge cases.
  5. Deployment: containerization (Docker), deployment on server or edge device, metric monitoring.
Example PaddleOCR configuration
ocr = PaddleOCR(use_angle_cls=True, lang='ru', use_gpu=True, det_db_thresh=0.3, det_db_box_thresh=0.5) 

Parameters are tuned to the specific document domain.

What is included in the result

  • Documented pipeline (code, configs, monitoring dashboards).
  • Integration via REST API with request examples.
  • Training webinar for your team.
  • Guarantee: we set a target CER in the contract — your business gets predictable quality.

Estimated timelines

Task Timeline
OCR with basic preprocessing 1–2 weeks
Full pipeline with document detection 3–4 weeks
Mobile app with live preview 5–7 weeks

Pricing is calculated individually after analyzing your project. Contact us for an estimate — we'll discuss the stack and scope, prepare a commercial proposal in 1–2 days. Get an engineer consultation to ensure the solution is right for you.

We have been working with computer vision and OCR for over 5 years, delivering projects for banks, insurance companies, and government organizations. Your case could be next.