Spaces:
Sleeping
Sleeping
Download preprocess.py from adityacodes404/doc-oc: direct link, hf CLI and curl.
- Browser
- Download file 4.06 kB
-
https://huggingface.co/spaces/adityacodes404/doc-oc/resolve/main/preprocess.py
- Command line
-
hf download hf://spaces/adityacodes404/doc-oc/preprocess.py
-
curl -L -o preprocess.py https://huggingface.co/spaces/adityacodes404/doc-oc/resolve/main/preprocess.py
4.06 kB
| """ | |
| Preprocessing utilities for marksheet images. | |
| Provides a simple pipeline: | |
| - Load image | |
| - Detect the largest page-like contour and crop so only the marksheet is visible | |
| - Boost contrast using CLAHE on the L (or V) channel | |
| - Reduce saturation slightly to stabilize OCR | |
| Returns (processed_image, original_image, crop_coords) | |
| where crop_coords = (x1, y1, x2, y2) in original image coordinates. | |
| """ | |
| from typing import Tuple, Optional | |
| import cv2 | |
| import numpy as np | |
| def _find_largest_contour_cropping_box(image_gray: np.ndarray) -> Optional[Tuple[int, int, int, int]]: | |
| """ | |
| Find a tight bounding box around the largest high-contrast region (the marksheet page). | |
| This uses Canny + contour detection and returns a bounding rectangle. | |
| """ | |
| # Edges | |
| blurred = cv2.GaussianBlur(image_gray, (5, 5), 0) | |
| edges = cv2.Canny(blurred, 50, 150) | |
| # Dilate to connect edges | |
| kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) | |
| edges = cv2.dilate(edges, kernel, iterations=1) | |
| contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
| if not contours: | |
| return None | |
| # Choose the largest contour by area | |
| largest_cnt = max(contours, key=cv2.contourArea) | |
| x, y, w, h = cv2.boundingRect(largest_cnt) | |
| # Expand slightly to avoid tight cuts | |
| pad_x = int(0.01 * image_gray.shape[1]) | |
| pad_y = int(0.01 * image_gray.shape[0]) | |
| x1 = max(0, x - pad_x) | |
| y1 = max(0, y - pad_y) | |
| x2 = min(image_gray.shape[1], x + w + pad_x) | |
| y2 = min(image_gray.shape[0], y + h + pad_y) | |
| return (x1, y1, x2, y2) | |
| def _apply_contrast_and_saturation(img_bgr: np.ndarray) -> np.ndarray: | |
| """ | |
| Boost contrast with CLAHE on the L channel (via LAB) and reduce saturation ~15% via HSV. | |
| """ | |
| # Contrast (CLAHE on LAB L channel) | |
| lab = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB) | |
| l, a, b = cv2.split(lab) | |
| clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) | |
| l_eq = clahe.apply(l) | |
| lab_eq = cv2.merge((l_eq, a, b)) | |
| bgr_eq = cv2.cvtColor(lab_eq, cv2.COLOR_LAB2BGR) | |
| # Reduce saturation in HSV | |
| hsv = cv2.cvtColor(bgr_eq, cv2.COLOR_BGR2HSV) | |
| h, s, v = cv2.split(hsv) | |
| s = (s.astype(np.float32) * 0.85).clip(0, 255).astype(np.uint8) | |
| hsv_mod = cv2.merge((h, s, v)) | |
| bgr_out = cv2.cvtColor(hsv_mod, cv2.COLOR_HSV2BGR) | |
| return bgr_out | |
| def preprocess_marksheet(image_path: str, | |
| output_path: Optional[str] = None, | |
| save_intermediate: bool = False): | |
| """ | |
| Load an image, crop to the marksheet region, enhance contrast and reduce saturation. | |
| Args: | |
| image_path: Path to the input image | |
| output_path: Optional path to save the processed image (unused when None) | |
| save_intermediate: Whether to save intermediate images (not used; kept for compatibility) | |
| Returns: | |
| processed_image (np.ndarray), original_image (np.ndarray), crop_coords (x1,y1,x2,y2) | |
| """ | |
| original = cv2.imread(image_path) | |
| if original is None: | |
| raise FileNotFoundError(f"Failed to read image: {image_path}") | |
| # Ensure portrait orientation similar to downstream expectations | |
| h, w = original.shape[:2] | |
| img = original.copy() | |
| if w > h: | |
| img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| # Find crop box | |
| crop_box = _find_largest_contour_cropping_box(gray) | |
| if crop_box is None: | |
| # Fall back to the whole image | |
| x1, y1, x2, y2 = 0, 0, img.shape[1], img.shape[0] | |
| else: | |
| x1, y1, x2, y2 = crop_box | |
| cropped = img[y1:y2, x1:x2] | |
| if cropped.size == 0: | |
| cropped = img | |
| x1, y1, x2, y2 = 0, 0, img.shape[1], img.shape[0] | |
| # Enhance | |
| enhanced = _apply_contrast_and_saturation(cropped) | |
| # Save if path provided | |
| if output_path: | |
| try: | |
| cv2.imwrite(output_path, enhanced) | |
| except Exception: | |
| pass | |
| crop_coords = (int(x1), int(y1), int(x2), int(y2)) | |
| return enhanced, original, crop_coords | |