import gradio as gr import cv2 import numpy as np import os from typing import Tuple, List def detect_bends_and_angles( image, blur_kernel_size: int = 7, canny_threshold1: int = 30, canny_threshold2: int = 150, dilation_kernel_size: int = 2, hough_threshold: int = 50, min_line_length: int = 10, max_line_gap: int = 60, bend_threshold: int = 15, debug: bool = True ) -> Tuple[List[Tuple[int, int]], List[Tuple[Tuple[int, int], float]]]: """ Detect bends and calculate angles relative to horizontal with configurable parameters. """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if blur_kernel_size is None: blur_kernel_size = 3 blur_kernel_size = max(3, blur_kernel_size | 1) # Ensure it's odd blurred = cv2.GaussianBlur(gray, (blur_kernel_size, blur_kernel_size), 0) edges = cv2.Canny(blurred, canny_threshold1, canny_threshold2) kernel = np.ones((dilation_kernel_size, dilation_kernel_size), np.uint8) dilated = cv2.dilate(edges, kernel, iterations=1) height, width = dilated.shape lines = cv2.HoughLinesP( dilated, rho=1, theta=np.pi/180, threshold=hough_threshold, minLineLength=min_line_length, maxLineGap=max_line_gap ) bend_points = [] if lines is not None: segments = [] for line in lines: x1, y1, x2, y2 = line[0] if x1 > x2: x1, x2 = x2, x1 y1, y2 = y2, y1 segments.append((x1, y1, x2, y2)) segments.sort(key=lambda seg: seg[0], reverse=True) for i in range(len(segments) - 1): x1, y1, x2, y2 = segments[i] x1_next, y1_next, x2_next, y2_next = segments[i + 1] if abs(x1 - x1_next) < bend_threshold and abs(y1 - y1_next) < bend_threshold: bend_points.append((x1, y1)) angles = [] for i in range(len(bend_points) - 1): x1, y1 = bend_points[i] x2, y2 = bend_points[i + 1] dx, dy = x2 - x1, y2 - y1 angle = np.arctan2(dy, dx) * 180 / np.pi angle = angle if angle >= 0 else angle + 180 angles.append((bend_points[i], angle)) return bend_points, angles def process_image( image, blur_kernel_size: int, canny_threshold1: int, canny_threshold2: int, dilation_kernel_size: int, hough_threshold: int, min_line_length: int, max_line_gap: int, bend_threshold: int ) -> Tuple[np.ndarray, str]: """ Process the image and return the visualization and angle measurements. """ if image is None: return None, "Please upload an image." if isinstance(image, dict): image = image['image'] if isinstance(image, str): image = cv2.imread(image) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) bend_points, angles = detect_bends_and_angles( image, blur_kernel_size, canny_threshold1, canny_threshold2, dilation_kernel_size, hough_threshold, min_line_length, max_line_gap, bend_threshold ) result_img = image.copy() for i, (x, y) in enumerate(bend_points): cv2.circle(result_img, (x, y), 5, (0, 0, 255), -1) cv2.putText( result_img, f"Bend {chr(65 + i)}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1 ) for (x, y), angle in angles: cv2.putText( result_img, f"{angle:.1f}°", (x, y + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1 ) measurements = "Angle Measurements:\n" for i, ((x, y), angle) in enumerate(angles): measurements += f"Bend {chr(65 + i)} at ({x}, {y}): {angle:.1f}°\n" return result_img, measurements def create_gradio_interface(): interface = gr.Interface( fn=process_image, inputs=[ gr.Image(label="Input Image", type="numpy"), gr.Slider(minimum=3, maximum=15, step=2, value=7, label="Blur Kernel Size"), gr.Slider(minimum=0, maximum=100, step=10, value=30, label="Canny Threshold 1"), gr.Slider(minimum=100, maximum=300, step=10, value=150, label="Canny Threshold 2"), gr.Slider(minimum=1, maximum=5, step=1, value=2, label="Dilation Kernel Size"), gr.Slider(minimum=10, maximum=100, step=10, value=50, label="Hough Threshold"), gr.Slider(minimum=5, maximum=50, step=5, value=10, label="Minimum Line Length"), gr.Slider(minimum=10, maximum=100, step=10, value=60, label="Maximum Line Gap"), gr.Slider(minimum=5, maximum=30, step=5, value=15, label="Bend Threshold") ], outputs=[ gr.Image(label="Result"), gr.Textbox(label="Measurements", lines=10) ], title="Angle Detection App", description="Upload an image to detect bends and measure angles. Adjust parameters as needed." ) return interface if __name__ == "__main__": interface = create_gradio_interface() interface.launch(share=True)