Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| def flatten_image(img, points): | |
| if img is None or not points or len(points) != 4: | |
| return None, "Please click exactly 4 points (TL, TR, BR, BL)." | |
| image_np = np.array(img) | |
| h, w = image_np.shape[:2] | |
| src_pts = np.array(points, dtype=np.float32) | |
| width_top = np.linalg.norm(src_pts[0] - src_pts[1]) | |
| width_bottom = np.linalg.norm(src_pts[3] - src_pts[2]) | |
| height_left = np.linalg.norm(src_pts[0] - src_pts[3]) | |
| height_right = np.linalg.norm(src_pts[1] - src_pts[2]) | |
| max_width = int(max(width_top, width_bottom)) | |
| max_height = int(max(height_left, height_right)) | |
| dst_pts = np.array([ | |
| [0, 0], | |
| [max_width - 1, 0], | |
| [max_width - 1, max_height - 1], | |
| [0, max_height - 1] | |
| ], dtype=np.float32) | |
| M = cv2.getPerspectiveTransform(src_pts, dst_pts) | |
| warped = cv2.warpPerspective(image_np, M, (max_width, max_height), flags=cv2.INTER_CUBIC) | |
| return Image.fromarray(warped), None | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## ๐ธ Perspective Flatten Tool\nUpload an image, click 4 corners (Top-Left โ Top-Right โ Bottom-Right โ Bottom-Left), then flatten!") | |
| with gr.Row(): | |
| input_image = gr.Image(label="Upload Image", type="pil") | |
| output_image = gr.Image(label="Flattened Output") | |
| coords = gr.State([]) | |
| def collect_points(evt: gr.SelectData, points): | |
| if points is None: | |
| points = [] | |
| points.append(evt.index) # evt.index = (x, y) | |
| if len(points) > 4: | |
| points = points[-4:] | |
| return points, f"Selected {len(points)}/4 points: {points}" | |
| points_output = gr.Textbox(label="Selected Points", interactive=False) | |
| # The .select event still works; we just donโt declare tool="select" | |
| input_image.select(fn=collect_points, inputs=coords, outputs=[coords, points_output]) | |
| flatten_btn = gr.Button("๐ Flatten Image") | |
| error_box = gr.Textbox(label="Messages", interactive=False) | |
| flatten_btn.click(fn=flatten_image, inputs=[input_image, coords], outputs=[output_image, error_box]) | |
| gr.Markdown("Tip: Re-upload image to reset point selection.") | |
| demo.launch() | |