File size: 5,037 Bytes
990270d
 
 
 
291ef85
990270d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291ef85
 
990270d
 
 
 
 
 
 
291ef85
 
990270d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564b428
 
 
990270d
 
 
 
 
 
 
291ef85
 
990270d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564b428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990270d
 
 
 
564b428
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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)