import gradio as gr import cv2 import numpy as np import matplotlib.pyplot as plt from typing import Tuple, List import tempfile import os 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. """ # Convert image to grayscale gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Step 2: Apply Gaussian blur blurred = cv2.GaussianBlur(gray, (blur_kernel_size, blur_kernel_size), 0) # Step 3: Perform edge detection edges = cv2.Canny(blurred, canny_threshold1, canny_threshold2) # Step 4: Dilate edges kernel = np.ones((dilation_kernel_size, dilation_kernel_size), np.uint8) dilated = cv2.dilate(edges, kernel, iterations=1) # Step 5: Detect parallel lines and identify bends 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)) # Step 6: Calculate angles between bends 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. """ 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 ) # Create visualization 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 ) # Create angle measurements text 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 # Create Gradio interface def create_gradio_interface(): with gr.Blocks(title="Angle Detection App", theme=gr.themes.Soft()) as interface: gr.Markdown("# Angle Detection App") gr.Markdown("Upload an image to detect bends and measure angles.") with gr.Row(): with gr.Column(): input_image = gr.Image(label="Input Image") with gr.Accordion("Algorithm Parameters", open=False): blur_kernel_size = gr.Slider( minimum=3, maximum=15, step=2, value=7, label="Blur Kernel Size" ) canny_threshold1 = gr.Slider( minimum=0, maximum=100, step=10, value=30, label="Canny Threshold 1" ) canny_threshold2 = gr.Slider( minimum=100, maximum=300, step=10, value=150, label="Canny Threshold 2" ) dilation_kernel_size = gr.Slider( minimum=1, maximum=5, step=1, value=2, label="Dilation Kernel Size" ) hough_threshold = gr.Slider( minimum=10, maximum=100, step=10, value=50, label="Hough Threshold" ) min_line_length = gr.Slider( minimum=5, maximum=50, step=5, value=10, label="Minimum Line Length" ) max_line_gap = gr.Slider( minimum=10, maximum=100, step=10, value=60, label="Maximum Line Gap" ) bend_threshold = gr.Slider( minimum=5, maximum=30, step=5, value=15, label="Bend Threshold" ) process_btn = gr.Button("Process Image", variant="primary") with gr.Column(): output_image = gr.Image(label="Result") output_text = gr.Textbox(label="Measurements", lines=10) process_btn.click( fn=process_image, inputs=[ input_image, blur_kernel_size, canny_threshold1, canny_threshold2, dilation_kernel_size, hough_threshold, min_line_length, max_line_gap, bend_threshold ], outputs=[output_image, output_text] ) return interface if __name__ == "__main__": interface = create_gradio_interface() # Get port from environment variable or use default port = int(os.environ.get("PORT", 7860)) # Get host from environment variable or use default host = os.environ.get("HOST", "0.0.0.0") # Launch the interface interface.launch( server_name=host, server_port=port, share=False # Set to True if you want to create a public URL )