Spaces:
Sleeping
Sleeping
| 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 | |
| ) |