9OCR / togyz /preprocess.py
ansarzeinulla's picture
Initial commit
20d7fde
Raw History Blame
1.8 kB
"""The single image -> array preprocessing used by training AND inference.
The legacy pipeline failed partly because train and inference normalized
differently. Every consumer (dataset, eval, predict, the ONNX pipeline) must
import from here.
This module is deliberately **torch-free** (numpy only) so the serving path
can import it without pulling in PyTorch. Torch consumers wrap the returned
array with ``torch.from_numpy(...)``.
"""
import numpy as np
from PIL import Image, ImageOps
TARGET_H = 64
TARGET_W = 128
MEAN = 0.5
STD = 0.5
def _estimate_background(gray: Image.Image) -> int:
"""Median of the image border, so padding blends with the paper."""
arr = np.asarray(gray)
border = np.concatenate([arr[0, :], arr[-1, :], arr[:, 0], arr[:, -1]])
return int(np.median(border))
def preprocess_pil(img: Image.Image) -> np.ndarray:
"""PIL image (any mode/size) -> float32 array [1, TARGET_H, TARGET_W]."""
img = ImageOps.exif_transpose(img).convert("L")
w, h = img.size
target_ratio = TARGET_W / TARGET_H
# Pad (never crop) to the target aspect ratio, centered on paper-colored canvas.
if w / h < target_ratio:
new_w, new_h = int(round(h * target_ratio)), h
else:
new_w, new_h = w, int(round(w / target_ratio))
if (new_w, new_h) != (w, h):
canvas = Image.new("L", (new_w, new_h), color=_estimate_background(img))
canvas.paste(img, ((new_w - w) // 2, (new_h - h) // 2))
img = canvas
img = img.resize((TARGET_W, TARGET_H), Image.BILINEAR)
arr = np.asarray(img, dtype=np.float32) / 255.0
arr = arr[None, :, :] # add channel axis -> [1, H, W]
return (arr - MEAN) / STD
def preprocess_file(path: str) -> np.ndarray:
with Image.open(path) as img:
return preprocess_pil(img)