Spaces:
Runtime error
Runtime error
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| import gradio as gr | |
| from huggingface_hub import snapshot_download | |
| import onnxruntime as ort | |
| import os | |
| import time | |
| MODEL_REPO = "fredcallagan/uvdoc-grid-onnx" | |
| print("Downloading ONNX model files...") | |
| model_dir = snapshot_download( | |
| repo_id=MODEL_REPO, | |
| local_dir="./uvdoc_model", | |
| local_dir_use_symlinks=False, | |
| ) | |
| onnx_path = os.path.join(model_dir, "UVDoc_grid.onnx") | |
| print(f"Model path: {onnx_path}") | |
| print("Creating ONNX Runtime session...") | |
| session = ort.InferenceSession( | |
| onnx_path, providers=['CPUExecutionProvider'] | |
| ) | |
| input_name = session.get_inputs()[0].name | |
| print("Session ready.") | |
| def _preprocess(image: Image.Image, target_size=(496, 720)): | |
| img_rgb = np.array(image) | |
| h_orig, w_orig = img_rgb.shape[:2] | |
| resized = cv2.resize(img_rgb, target_size) | |
| normalized = resized.astype(np.float32) / 255.0 | |
| transposed = np.transpose(normalized, (2, 0, 1)) | |
| batched = np.expand_dims(transposed, axis=0) | |
| return batched, h_orig, w_orig | |
| def unwarp(image, interpolation="cubic", border_mode="replicate"): | |
| if image is None: | |
| return None, None, "No image provided" | |
| start_time = time.time() | |
| try: | |
| interp_map = { | |
| "nearest": cv2.INTER_NEAREST, | |
| "linear": cv2.INTER_LINEAR, | |
| "cubic": cv2.INTER_CUBIC, | |
| "lanczos": cv2.INTER_LANCZOS4, | |
| } | |
| border_map = { | |
| "replicate": cv2.BORDER_REPLICATE, | |
| "constant": cv2.BORDER_CONSTANT, | |
| "reflect": cv2.BORDER_REFLECT, | |
| } | |
| interp = interp_map.get(interpolation, cv2.INTER_CUBIC) | |
| border = border_map.get(border_mode, cv2.BORDER_REPLICATE) | |
| img_bgr = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) | |
| h_orig, w_orig = img_bgr.shape[:2] | |
| input_tensor, _, _ = _preprocess(image) | |
| result = session.run(None, {input_name: input_tensor})[0] | |
| grid = np.transpose(result[0], (1, 2, 0)) # (45, 31, 2) | |
| grid_up = cv2.resize(grid, (w_orig, h_orig), interpolation=cv2.INTER_LINEAR) | |
| map_x = ((grid_up[..., 0] + 1) / 2) * (w_orig - 1) | |
| map_y = ((grid_up[..., 1] + 1) / 2) * (h_orig - 1) | |
| unwarped = cv2.remap( | |
| img_bgr, | |
| map_x.astype(np.float32), | |
| map_y.astype(np.float32), | |
| interpolation=interp, | |
| borderMode=border, | |
| ) | |
| unwarped_rgb = cv2.cvtColor(unwarped, cv2.COLOR_BGR2RGB) | |
| # Draw corners on original image using the 45x31 grid | |
| corners_grid = [ | |
| (0, 0), # top-left | |
| (0, 30), # top-right | |
| (44, 30), # bottom-right | |
| (44, 0), # bottom-left | |
| ] | |
| img_corners = img_bgr.copy() | |
| corner_colors = [(0, 255, 0), (0, 0, 255), (255, 0, 0), (255, 255, 0)] # BGR | |
| pixel_corners = [] | |
| for idx, (gy, gx) in enumerate(corners_grid): | |
| norm_x, norm_y = grid[gy, gx] | |
| px = int(((norm_x + 1) / 2) * (w_orig - 1)) | |
| py = int(((norm_y + 1) / 2) * (h_orig - 1)) | |
| pixel_corners.append((px, py)) | |
| radius = max(8, min(w_orig, h_orig) // 60) | |
| cv2.circle(img_corners, (px, py), radius=radius, color=corner_colors[idx], thickness=-1) | |
| cv2.putText(img_corners, str(idx + 1), (px - 5, py + 5), | |
| cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) | |
| # Connect corners with lines | |
| for i in range(len(pixel_corners)): | |
| pt1 = pixel_corners[i] | |
| pt2 = pixel_corners[(i + 1) % len(pixel_corners)] | |
| cv2.line(img_corners, pt1, pt2, (0, 255, 255), 3) | |
| corners_rgb = cv2.cvtColor(img_corners, cv2.COLOR_BGR2RGB) | |
| elapsed = time.time() - start_time | |
| timer_text = f"Processing completed in {elapsed:.2f} seconds" | |
| return Image.fromarray(unwarped_rgb), Image.fromarray(corners_rgb), timer_text | |
| except Exception as e: | |
| import traceback | |
| print(traceback.format_exc()) | |
| raise gr.Error(f"Inference failed: {e}") | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# UVDoc Document Unwarping (High-Resolution)") | |
| gr.Markdown( | |
| "Upload a warped or curved document photo and UVDoc will geometrically " | |
| "rectify it at your **original image resolution**." | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| input_image = gr.Image(type="pil", label="Warped Document") | |
| interpolation = gr.Dropdown( | |
| choices=["nearest", "linear", "cubic", "lanczos"], | |
| value="cubic", | |
| label="Remap Interpolation", | |
| ) | |
| border_mode = gr.Dropdown( | |
| choices=["replicate", "constant", "reflect"], | |
| value="replicate", | |
| label="Border Mode", | |
| ) | |
| btn = gr.Button("Unwarp") | |
| with gr.Column(): | |
| with gr.Row(): | |
| output_image = gr.Image(type="pil", label="Rectified Document") | |
| with gr.Row(): | |
| corners_image = gr.Image(type="pil", label="Detected Corners") | |
| with gr.Row(): | |
| timer_text = gr.Textbox(label="Timer", interactive=False) | |
| btn.click( | |
| fn=unwarp, | |
| inputs=[input_image, interpolation, border_mode], | |
| outputs=[output_image, corners_image, timer_text], | |
| ) | |
| demo.launch() | |