-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcircle_detect.py
More file actions
139 lines (106 loc) · 3.63 KB
/
Copy pathcircle_detect.py
File metadata and controls
139 lines (106 loc) · 3.63 KB
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
"""
Circle-based coin detection using OpenCV's Hough Transform.
Used as a pre-filter / supplementary detector alongside YOLOv8.
Detects circular objects in the frame, measures their size,
and provides candidate regions for classification.
"""
import cv2
import numpy as np
def detect_circles(frame, min_radius=20, max_radius=150):
"""
Detect circular objects in a BGR frame using Hough Transform.
Args:
frame: BGR image (numpy array)
min_radius: Minimum circle radius in pixels
max_radius: Maximum circle radius in pixels
Returns:
List of (x_center, y_center, radius) tuples
"""
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (9, 9), 2)
circles = cv2.HoughCircles(
blurred,
cv2.HOUGH_GRADIENT,
dp=1.2,
minDist=40,
param1=50,
param2=30,
minRadius=min_radius,
maxRadius=max_radius,
)
if circles is None:
return []
circles = np.round(circles[0]).astype(int)
return [(int(x), int(y), int(r)) for x, y, r in circles]
def circles_to_bboxes(circles):
"""
Convert circles to bounding boxes [x1, y1, x2, y2].
Args:
circles: List of (x_center, y_center, radius)
Returns:
List of [x1, y1, x2, y2] bounding boxes
"""
bboxes = []
for x, y, r in circles:
bboxes.append([x - r, y - r, x + r, y + r])
return bboxes
def find_unmatched_circles(circles, yolo_boxes, iou_threshold=0.3):
"""
Find circles that don't overlap with any YOLO detection.
These are candidate coins that YOLO missed.
Args:
circles: List of (x, y, r) from Hough detection
yolo_boxes: List of [x1, y1, x2, y2] from YOLO
iou_threshold: Minimum IoU to consider a match
Returns:
List of (x, y, r) circles not matched by YOLO
"""
if not circles or not yolo_boxes:
return circles if not yolo_boxes else []
circle_bboxes = circles_to_bboxes(circles)
unmatched = []
for i, cbbox in enumerate(circle_bboxes):
matched = False
for ybbox in yolo_boxes:
if _iou(cbbox, ybbox) > iou_threshold:
matched = True
break
if not matched:
unmatched.append(circles[i])
return unmatched
def crop_circle_region(frame, x, y, r, padding=10):
"""
Crop a square region around a detected circle for classification.
Args:
frame: BGR image
x, y, r: Circle center and radius
padding: Extra pixels around the circle
Returns:
Cropped BGR image, or None if out of bounds
"""
h, w = frame.shape[:2]
x1 = max(0, x - r - padding)
y1 = max(0, y - r - padding)
x2 = min(w, x + r + padding)
y2 = min(h, y + r + padding)
if x2 - x1 < 10 or y2 - y1 < 10:
return None
return frame[y1:y2, x1:x2]
def draw_circles(frame, circles, color=(255, 255, 0), thickness=2):
"""Draw detected circles on a frame for visualization."""
for x, y, r in circles:
cv2.circle(frame, (x, y), r, color, thickness)
cv2.circle(frame, (x, y), 3, color, -1) # center dot
return frame
def _iou(box_a, box_b):
"""Intersection over Union for two [x1, y1, x2, y2] boxes."""
x1 = max(box_a[0], box_b[0])
y1 = max(box_a[1], box_b[1])
x2 = min(box_a[2], box_b[2])
y2 = min(box_a[3], box_b[3])
intersection = max(0, x2 - x1) * max(0, y2 - y1)
if intersection == 0:
return 0.0
area_a = (box_a[2] - box_a[0]) * (box_a[3] - box_a[1])
area_b = (box_b[2] - box_b[0]) * (box_b[3] - box_b[1])
return intersection / (area_a + area_b - intersection)