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() |