mustafa2ak commited on
Commit
3415c7c
·
verified ·
1 Parent(s): e96f282

Update detection.py

Browse files
Files changed (1) hide show
  1. detection.py +182 -42
detection.py CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
  import cv2
2
  import numpy as np
3
  import torch
@@ -7,89 +10,226 @@ from dataclasses import dataclass
7
 
8
  @dataclass
9
  class Detection:
10
- """Simple detection data structure"""
11
  bbox: List[float] # [x1, y1, x2, y2]
12
  confidence: float
13
- image_crop: Optional[np.ndarray] = None # Cropped dog image
14
-
15
- class DogDetector:
16
  """
17
- Simplified YOLOv8 detector optimized for dogs
18
- Uses standard pretrained model - no custom training needed
19
  """
20
-
21
- def __init__(self,
22
- confidence_threshold: float = 0.45,
23
  device: str = 'cuda'):
24
  """
25
- Initialize detector
26
-
27
  Args:
28
- confidence_threshold: Min confidence for detections (0.45 works well)
29
- device: 'cuda' for GPU, 'cpu' for CPU
 
30
  """
31
  self.confidence_threshold = confidence_threshold
 
32
  self.device = device if torch.cuda.is_available() else 'cpu'
33
 
34
- # Load YOLOv8 medium model (good balance of speed/accuracy)
35
  self.model = YOLO('yolov8m.pt')
36
  self.model.to(self.device)
37
 
38
  # COCO class ID for dog
39
  self.dog_class_id = 16
40
 
41
- def detect(self, frame: np.ndarray) -> List[Detection]:
 
 
 
 
 
 
 
 
 
 
 
42
  """
43
- Detect dogs in frame
 
44
 
45
- Args:
46
- frame: BGR image from OpenCV
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- Returns:
49
- List of Detection objects with bounding boxes and crops
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  """
51
  # Run YOLO inference
52
- results = self.model(frame,
53
- conf=self.confidence_threshold,
54
- classes=[self.dog_class_id], # Only detect dogs
55
- verbose=False)
56
 
57
- detections = []
58
 
59
  if results and len(results) > 0:
60
  result = results[0]
 
61
  if result.boxes is not None:
62
  boxes = result.boxes
63
 
 
 
 
 
64
  for i in range(len(boxes)):
65
- # Get bbox coordinates
66
  x1, y1, x2, y2 = boxes.xyxy[i].cpu().numpy()
67
  x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
68
 
69
  # Ensure valid coordinates
70
  h, w = frame.shape[:2]
71
- x1 = max(0, x1)
72
- y1 = max(0, y1)
73
- x2 = min(w, x2)
74
- y2 = min(h, y2)
75
 
76
- # Skip invalid boxes
77
  if x2 <= x1 or y2 <= y1:
78
  continue
79
 
80
- # Crop dog image
81
- dog_crop = frame[y1:y2, x1:x2].copy()
82
-
83
- # Create detection
84
- detection = Detection(
85
- bbox=[x1, y1, x2, y2],
86
- confidence=float(boxes.conf[i]),
87
- image_crop=dog_crop
 
88
  )
89
- detections.append(detection)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
- return detections
92
 
93
  def set_confidence(self, threshold: float):
94
  """Update detection confidence threshold"""
95
- self.confidence_threshold = max(0.1, min(1.0, threshold))
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Enhanced Detection with Better Confidence Filtering and NMS (Enhancement 7)
3
+ """
4
  import cv2
5
  import numpy as np
6
  import torch
 
10
 
11
  @dataclass
12
  class Detection:
13
+ """Detection data structure"""
14
  bbox: List[float] # [x1, y1, x2, y2]
15
  confidence: float
16
+ image_crop: Optional[np.ndarray] = None
17
+
18
+ class EnhancedDogDetector:
19
  """
20
+ Enhanced YOLOv8 detector with improved filtering (Enhancement 7)
 
21
  """
22
+ def __init__(self,
23
+ confidence_threshold: float = 0.50, # Increased from 0.45
24
+ nms_threshold: float = 0.4, # Non-maximum suppression
25
  device: str = 'cuda'):
26
  """
27
+ Initialize detector with enhanced filtering
 
28
  Args:
29
+ confidence_threshold: Higher threshold reduces false positives
30
+ nms_threshold: Lower = stricter NMS, removes more overlapping boxes
31
+ device: 'cuda' or 'cpu'
32
  """
33
  self.confidence_threshold = confidence_threshold
34
+ self.nms_threshold = nms_threshold
35
  self.device = device if torch.cuda.is_available() else 'cpu'
36
 
37
+ # Load YOLOv8 medium model
38
  self.model = YOLO('yolov8m.pt')
39
  self.model.to(self.device)
40
 
41
  # COCO class ID for dog
42
  self.dog_class_id = 16
43
 
44
+ # ENHANCEMENT 7: Size constraints
45
+ self.min_detection_area = 900 # 30x30 pixels minimum
46
+ self.max_detection_area = 640000 # 800x800 pixels maximum
47
+
48
+ print(f"✅ Enhanced Detector initialized")
49
+ print(f" Confidence: {self.confidence_threshold:.2f}")
50
+ print(f" NMS threshold: {self.nms_threshold:.2f}")
51
+ print(f" Min area: {self.min_detection_area}px²")
52
+
53
+ def _apply_custom_nms(self, boxes, scores, iou_threshold=0.4):
54
+ """
55
+ ENHANCEMENT 7: Custom NMS for better duplicate removal
56
  """
57
+ if len(boxes) == 0:
58
+ return []
59
 
60
+ # Convert to numpy arrays
61
+ boxes = np.array(boxes)
62
+ scores = np.array(scores)
63
+
64
+ # Get coordinates
65
+ x1 = boxes[:, 0]
66
+ y1 = boxes[:, 1]
67
+ x2 = boxes[:, 2]
68
+ y2 = boxes[:, 3]
69
+
70
+ # Compute areas
71
+ areas = (x2 - x1) * (y2 - y1)
72
+
73
+ # Sort by score
74
+ order = scores.argsort()[::-1]
75
+
76
+ keep = []
77
+ while order.size > 0:
78
+ i = order[0]
79
+ keep.append(i)
80
+
81
+ # Compute IoU with remaining boxes
82
+ xx1 = np.maximum(x1[i], x1[order[1:]])
83
+ yy1 = np.maximum(y1[i], y1[order[1:]])
84
+ xx2 = np.minimum(x2[i], x2[order[1:]])
85
+ yy2 = np.minimum(y2[i], y2[order[1:]])
86
+
87
+ w = np.maximum(0.0, xx2 - xx1)
88
+ h = np.maximum(0.0, yy2 - yy1)
89
+ inter = w * h
90
 
91
+ iou = inter / (areas[i] + areas[order[1:]] - inter + 1e-6)
92
+
93
+ # Keep boxes with IoU less than threshold
94
+ inds = np.where(iou <= iou_threshold)[0]
95
+ order = order[inds + 1]
96
+
97
+ return keep
98
+
99
+ def _filter_by_size(self, detections: List[Detection]) -> List[Detection]:
100
+ """
101
+ ENHANCEMENT 7: Size-based filtering to remove false positives
102
+ """
103
+ filtered = []
104
+
105
+ for det in detections:
106
+ width = det.bbox[2] - det.bbox[0]
107
+ height = det.bbox[3] - det.bbox[1]
108
+ area = width * height
109
+
110
+ # Check area constraints
111
+ if area < self.min_detection_area or area > self.max_detection_area:
112
+ continue
113
+
114
+ # Check aspect ratio (dogs shouldn't be extreme shapes)
115
+ if width > 0 and height > 0:
116
+ aspect_ratio = width / height
117
+ if aspect_ratio < 0.2 or aspect_ratio > 5.0:
118
+ continue
119
+
120
+ filtered.append(det)
121
+
122
+ return filtered
123
+
124
+ def _filter_by_confidence_quality(self, detections: List[Detection]) -> List[Detection]:
125
+ """
126
+ ENHANCEMENT 7: Advanced confidence filtering
127
+ """
128
+ if not detections:
129
+ return []
130
+
131
+ # Calculate confidence statistics
132
+ confidences = [d.confidence for d in detections]
133
+ mean_conf = np.mean(confidences)
134
+ std_conf = np.std(confidences)
135
+
136
+ filtered = []
137
+ for det in detections:
138
+ # Base threshold
139
+ if det.confidence < self.confidence_threshold:
140
+ continue
141
+
142
+ # Adaptive threshold: if confidence is much lower than mean, reject
143
+ if len(detections) > 3:
144
+ if det.confidence < mean_conf - std_conf:
145
+ continue
146
+
147
+ filtered.append(det)
148
+
149
+ return filtered
150
+
151
+ def detect(self, frame: np.ndarray) -> List[Detection]:
152
+ """
153
+ Detect dogs with enhanced filtering
154
  """
155
  # Run YOLO inference
156
+ results = self.model(frame,
157
+ conf=self.confidence_threshold * 0.9, # Slightly lower for YOLO
158
+ classes=[self.dog_class_id],
159
+ verbose=False)
160
 
161
+ initial_detections = []
162
 
163
  if results and len(results) > 0:
164
  result = results[0]
165
+
166
  if result.boxes is not None:
167
  boxes = result.boxes
168
 
169
+ # Collect all boxes first
170
+ all_boxes = []
171
+ all_scores = []
172
+
173
  for i in range(len(boxes)):
 
174
  x1, y1, x2, y2 = boxes.xyxy[i].cpu().numpy()
175
  x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
176
 
177
  # Ensure valid coordinates
178
  h, w = frame.shape[:2]
179
+ x1 = max(0, min(w-1, x1))
180
+ y1 = max(0, min(h-1, y1))
181
+ x2 = max(0, min(w, x2))
182
+ y2 = max(0, min(h, y2))
183
 
 
184
  if x2 <= x1 or y2 <= y1:
185
  continue
186
 
187
+ all_boxes.append([x1, y1, x2, y2])
188
+ all_scores.append(float(boxes.conf[i]))
189
+
190
+ # ENHANCEMENT 7: Apply custom NMS
191
+ if all_boxes:
192
+ keep_indices = self._apply_custom_nms(
193
+ all_boxes,
194
+ all_scores,
195
+ iou_threshold=self.nms_threshold
196
  )
197
+
198
+ # Create detections for kept boxes
199
+ for idx in keep_indices:
200
+ bbox = all_boxes[idx]
201
+ conf = all_scores[idx]
202
+
203
+ # Crop dog image
204
+ x1, y1, x2, y2 = bbox
205
+ dog_crop = frame[y1:y2, x1:x2].copy()
206
+
207
+ detection = Detection(
208
+ bbox=bbox,
209
+ confidence=conf,
210
+ image_crop=dog_crop
211
+ )
212
+ initial_detections.append(detection)
213
+
214
+ # ENHANCEMENT 7: Apply additional filters
215
+ filtered_detections = self._filter_by_size(initial_detections)
216
+ filtered_detections = self._filter_by_confidence_quality(filtered_detections)
217
+
218
+ # Debug info
219
+ if len(initial_detections) != len(filtered_detections):
220
+ print(f" 🔍 Detection filter: {len(initial_detections)} → {len(filtered_detections)}")
221
 
222
+ return filtered_detections
223
 
224
  def set_confidence(self, threshold: float):
225
  """Update detection confidence threshold"""
226
+ self.confidence_threshold = max(0.1, min(1.0, threshold))
227
+ print(f"Detection confidence updated: {self.confidence_threshold:.2f}")
228
+
229
+ def set_nms_threshold(self, threshold: float):
230
+ """Update NMS threshold"""
231
+ self.nms_threshold = max(0.1, min(0.9, threshold))
232
+ print(f"NMS threshold updated: {self.nms_threshold:.2f}")
233
+
234
+ # Compatibility alias
235
+ DogDetector = EnhancedDogDetector