document-ocr / ocr_studio /preprocess.py
alirezaaminzadeh's picture
Expand document OCR with PDF support, bilingual UI, and extra exports
98e1d9f verified
Raw
History Blame Contribute Delete
2.05 kB
from __future__ import annotations
from PIL import Image
from ocr_studio.config import DESKEW_MAX_ANGLE
def _rotate(image: Image.Image, angle: float) -> Image.Image:
if abs(angle) < 0.25:
return image
fill = (255, 255, 255)
return image.convert("RGB").rotate(angle, expand=True, fillcolor=fill, resample=Image.Resampling.BICUBIC)
def enhance_scan(image: Image.Image) -> Image.Image:
try:
import cv2
import numpy as np
except Exception:
return image.convert("RGB")
rgb = np.array(image.convert("RGB"))
lab = cv2.cvtColor(rgb, cv2.COLOR_RGB2LAB)
lightness, axis_a, axis_b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
lightness = clahe.apply(lightness)
merged = cv2.merge((lightness, axis_a, axis_b))
enhanced = cv2.cvtColor(merged, cv2.COLOR_LAB2RGB)
return Image.fromarray(enhanced)
def estimate_deskew_angle(image: Image.Image) -> float:
try:
import cv2
import numpy as np
except Exception:
return 0.0
gray = np.array(image.convert("L"))
if gray.size < 400:
return 0.0
inverted = cv2.bitwise_not(gray)
_, binary = cv2.threshold(inverted, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
coords = np.column_stack(np.where(binary > 0))
if coords.shape[0] < 80:
return 0.0
_rect, _size, raw_angle = cv2.minAreaRect(coords)
angle = raw_angle
if angle < -45:
angle = 90.0 + angle
if abs(angle) > DESKEW_MAX_ANGLE:
return 0.0
return float(-angle)
def deskew_image(image: Image.Image) -> tuple[Image.Image, float]:
rgb = image.convert("RGB")
angle = estimate_deskew_angle(rgb)
straightened = _rotate(rgb, angle)
return enhance_scan(straightened), angle
def prepare_page(image: Image.Image, deskew: bool) -> tuple[Image.Image, float]:
if not deskew:
return enhance_scan(image.convert("RGB")), 0.0
return deskew_image(image)