File size: 6,206 Bytes
65a0c52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
140
141
142
import cv2
import numpy as np
import gradio as gr

def image_inference(image_path, mode, epsilon_thresh):
    # Read the image
    img = cv2.cvtColor(image_path, cv2.COLOR_RGB2BGR)
    if img is None:
        raise FileNotFoundError(f"Image at path '{image_path}' not found.")
    
    # Create a copy of the original image
    img_copy = img.copy()
    
    # Convert the image to grayscale
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Apply thresholding
    _, img_thresh = cv2.threshold(img_gray, 200, 255, cv2.THRESH_BINARY_INV)

    # Find the contours
    contours, _ = cv2.findContours(img_thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    contour_info = f"Number of contours found: {len(contours)}"  # Text output for Gradio
    # print(f"Number of contours found = {len(contours)}")
    
    def convert_color(hsv):
        # Utility to convert a single HSV color tuple into BGR
        pixel_img = np.uint8([[hsv]])
        return tuple(int(i) for i in cv2.cvtColor(pixel_img, cv2.COLOR_HSV2BGR).flatten())

    if mode == "contour":
        if len(contours) > 0:
            for i, single_contour in enumerate(contours):
                hsv = (int(i/len(contours) * 180), 255, 255)
                color = convert_color(hsv)
                # Draw the contour
                img_final = cv2.drawContours(img_copy, contours, i, color, 4)
                # Calculate and display the area
                # area = cv2.contourArea(single_contour)
                # x, y, w, h = cv2.boundingRect(single_contour)
                # img_final = cv2.putText(img_final, f"Area: {area}", (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)
        else:
            img_final = img_copy  # No contours found, return the original image
    elif mode == "rotated rectangle":
        for cnt in contours:
            # Rotated bounding box
            box = cv2.minAreaRect(cnt)
            box_pts = np.intp(cv2.boxPoints(box))
            img_final = cv2.drawContours(img_copy, [box_pts], -1, (0, 255, 0), 4)
            
            # Calculate and display the area
            area = cv2.contourArea(cnt)
            x, y, w, h = cv2.boundingRect(cnt)
            img_final = cv2.putText(img_final, f"Area: {area}", (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)
            
    elif mode == "rectangle":
        for cnt in contours:
            # Bounding rectangle
            x, y, w, h = cv2.boundingRect(cnt)
            img_final = cv2.rectangle(img_copy, (x, y), (x + w, y + h), (0, 255, 0), 4)
            
            # Calculate and display the area
            area = cv2.contourArea(cnt)
            img_final = cv2.putText(img_final, f"Area: {area}", (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)
            
    elif mode == "circle":
        for cnt in contours:
            # Enclosing circle
            ((x, y), radius) = cv2.minEnclosingCircle(cnt)
            img_final = cv2.circle(img_copy, (int(x), int(y)), int(round(radius)), (0, 255, 0), 4)
            
            # Calculate and display the perimeter
            perimeter = cv2.arcLength(cnt, True)
            x, y, w, h = cv2.boundingRect(cnt)
            img_final = cv2.putText(img_final, f"Perimeter: {perimeter:.2f}", (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)
            
    elif mode == "ellipse":
        for cnt in contours:
            if len(cnt) >= 5:  # fitEllipse requires at least 5 points
                ellipse = cv2.fitEllipse(cnt)
                img_final = cv2.ellipse(img_copy, ellipse, (0, 255, 0), 4)
                
                # Calculate and display the perimeter
                perimeter = cv2.arcLength(cnt, True)
                x, y, w, h = cv2.boundingRect(cnt)
                img_final = cv2.putText(img_final, f"Perimeter: {perimeter:.2f}", (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 0), 1)
            else:
                img_final = img_copy
    elif mode == "centroid":
        for cnt in contours:
            M = cv2.moments(cnt)
            if M["m00"] != 0:
                x = int(round(M["m10"] / M["m00"]))
                y = int(round(M["m01"] / M["m00"]))
                img_final = cv2.circle(img_copy, (x, y), 10, (255, 0, 0), -1)
            else:
                img_final = img_copy
    elif mode == "contour approx":
        if len(contours) > 0:
            c = max(contours, key=cv2.contourArea)
            peri = cv2.arcLength(c, True)
            approx = cv2.approxPolyDP(c, epsilon_thresh * peri, True)
            img_final = cv2.drawContours(img_copy, [approx], -1, (0, 255, 0), 3)
            x, y, w, h = cv2.boundingRect(c)
            text = f"eps={epsilon_thresh:.4f}, num_pts={len(approx)}"
            img_final = cv2.putText(img_copy, text, (x, y - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
        else:
            img_final = img_copy
    else:
        img_final = img_copy
        print(f"Mode '{mode}' not recognized. Please choose a valid mode.")
    
    return contour_info, cv2.cvtColor(img_final, cv2.COLOR_BGR2RGB)

# Gradio interface
input_image = gr.Image(type="numpy", label="Input Image")
epsilon_thresh = gr.Slider(
    0.01,
    0.1,
    step=0.01,
    value=0.05,
    label="Epsilon Threshold",
    info="Adjust the Contour Threshold according to the object size that you want to detect. Only Applicable for Contour Approx",
)
mode = gr.Radio(
    ["contour", "rectangle", "rotated rectangle", "circle", "ellipse", "centroid", "contour approx"],
    label="Contour Type",
    info="Choose the MODE",
)
output_text = gr.Textbox(label="Contour Information")
output_image = gr.Image(label="Output Image")

app = gr.Interface(
    fn=image_inference,
    inputs=[input_image, mode, epsilon_thresh],
    outputs=[output_text, output_image],
    title="Contour Detection using OpenCV",
    description="A Gradio app for dynamic image analysis using Contour detection techniques.",
    allow_flagging="never",
    examples=[["./sample/tictactoe.png", "contour", float(0.01)], ["./sample/tetris_blocks.png", "rectangle", float(0.00)], ["./sample/shape.png", "contour approx", float(0.05)]],
    cache_examples=False,
)
app.queue().launch()