firgiawann commited on
Commit
c9aa351
·
verified ·
1 Parent(s): db9f71c

Update 40 restore person count labels

Browse files
Files changed (2) hide show
  1. CHANGELOG.md +5 -0
  2. app.py +65 -48
CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
  # Changelog
2
 
 
 
 
 
 
3
  ## Update ke-39 - 2026-05-02
4
  - Tambah repo artefak training terpisah melalui `HF_ARTIFACT_REPO`.
5
  - Panel training sekarang mengambil ulang grafik, confusion matrix, `results.csv`, log, dan summary terakhir dari repo artefak saat Space rebuild.
 
1
  # Changelog
2
 
3
+ ## Update ke-40 - 2026-05-02
4
+ - Perbaiki anotasi mode `YOLOv8n` semua kelas agar objek person kembali diberi label berurutan `person 1`, `person 2`, dan seterusnya.
5
+ - Non-person tetap ditampilkan sebagai kelas objek masing-masing dengan nomor terpisah.
6
+ - Minor update dinaikkan ke-40.
7
+
8
  ## Update ke-39 - 2026-05-02
9
  - Tambah repo artefak training terpisah melalui `HF_ARTIFACT_REPO`.
10
  - Panel training sekarang mengambil ulang grafik, confusion matrix, `results.csv`, log, dan summary terakhir dari repo artefak saat Space rebuild.
app.py CHANGED
@@ -24,7 +24,7 @@ from ultralytics import YOLO
24
  CM_INIT = {"tp": 0, "fp": 0, "fn": 0, "tn": 0, "person": 0, "not_person": 0}
25
  EVAL_CLASSES = ["Person", "Not Person/Background"]
26
  APP_VERSION = os.getenv("APP_VERSION", "v0.9.0")
27
- APP_MINOR_UPDATE = os.getenv("APP_MINOR_UPDATE", "39")
28
 
29
 
30
  # Gradio/Spaces can trigger noisy asyncio cleanup traces ("Invalid file descriptor: -1")
@@ -180,11 +180,11 @@ def _annotate_person_instances(
180
  return annotated, len(person_boxes)
181
 
182
 
183
- def _annotate_all_instances(
184
- image_rgb: np.ndarray,
185
- result: Any,
186
- annotation_style: str,
187
- ) -> Tuple[np.ndarray, int]:
188
  annotated = image_rgb.copy()
189
  if result.boxes is None:
190
  return annotated, 0
@@ -192,48 +192,65 @@ def _annotate_all_instances(
192
  classes = result.boxes.cls.cpu().numpy()
193
  boxes = result.boxes.xyxy.cpu().numpy()
194
  names = getattr(result, "names", {})
195
- count = 0
196
-
197
- for i, (x1, y1, x2, y2) in enumerate(boxes.tolist(), start=1):
198
- cls_id = int(classes[i - 1])
199
- if isinstance(names, dict):
200
- cls_name = str(names.get(cls_id, f"class_{cls_id}"))
201
- elif isinstance(names, list) and 0 <= cls_id < len(names):
202
- cls_name = str(names[cls_id])
203
- else:
204
- cls_name = f"class_{cls_id}"
205
-
206
- if annotation_style == "Border + Label":
207
- cv2.rectangle(annotated, (int(x1), int(y1)), (int(x2), int(y2)), (59, 130, 246), 2)
208
- label = f"{cls_name} {i}"
209
- (tw, th), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
210
- if annotation_style == "Label saja":
211
- label_x = max(0, min(int(x1), annotated.shape[1] - tw - 8))
212
- label_y = max(th + 4, min(int(y1) + th + 4, annotated.shape[0] - 6))
213
- text_y1 = max(0, label_y - th - baseline - 6)
214
- text_y2 = min(annotated.shape[0], label_y + 2)
215
- text_x1 = label_x
216
- text_x2 = min(annotated.shape[1], label_x + tw + 10)
217
- text_org = (label_x + 4, label_y - 4)
218
- else:
219
- text_y1 = max(0, int(y1) - th - baseline - 6)
220
- text_y2 = int(y1)
221
- text_x1 = int(x1)
222
- text_x2 = min(annotated.shape[1], int(x1) + tw + 10)
223
- text_org = (int(x1) + 4, max(14, int(y1) - 6))
224
- cv2.rectangle(annotated, (text_x1, text_y1), (text_x2, text_y2), (59, 130, 246), -1)
225
- cv2.putText(
226
- annotated,
227
- label,
228
- text_org,
229
- cv2.FONT_HERSHEY_SIMPLEX,
230
- 0.6,
231
- (255, 255, 255),
232
- 2,
233
- cv2.LINE_AA,
234
- )
235
- count += 1
236
- return annotated, count
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
 
239
  def get_detection_scope(model_choice: str) -> str:
 
24
  CM_INIT = {"tp": 0, "fp": 0, "fn": 0, "tn": 0, "person": 0, "not_person": 0}
25
  EVAL_CLASSES = ["Person", "Not Person/Background"]
26
  APP_VERSION = os.getenv("APP_VERSION", "v0.9.0")
27
+ APP_MINOR_UPDATE = os.getenv("APP_MINOR_UPDATE", "40")
28
 
29
 
30
  # Gradio/Spaces can trigger noisy asyncio cleanup traces ("Invalid file descriptor: -1")
 
180
  return annotated, len(person_boxes)
181
 
182
 
183
+ def _annotate_all_instances(
184
+ image_rgb: np.ndarray,
185
+ result: Any,
186
+ annotation_style: str,
187
+ ) -> Tuple[np.ndarray, int]:
188
  annotated = image_rgb.copy()
189
  if result.boxes is None:
190
  return annotated, 0
 
192
  classes = result.boxes.cls.cpu().numpy()
193
  boxes = result.boxes.xyxy.cpu().numpy()
194
  names = getattr(result, "names", {})
195
+ items = []
196
+ person_id = _resolve_person_class_id(result)
197
+ person_counter = 0
198
+ other_counter = 0
199
+
200
+ for box, cls_raw in zip(boxes.tolist(), classes.tolist()):
201
+ x1, y1, x2, y2 = box
202
+ cls_id = int(cls_raw)
203
+ if isinstance(names, dict):
204
+ cls_name = str(names.get(cls_id, f"class_{cls_id}"))
205
+ elif isinstance(names, list) and 0 <= cls_id < len(names):
206
+ cls_name = str(names[cls_id])
207
+ else:
208
+ cls_name = f"class_{cls_id}"
209
+ items.append((int(x1), int(y1), int(x2), int(y2), cls_id, cls_name))
210
+
211
+ items.sort(key=lambda item: (item[1], item[0]))
212
+
213
+ for x1, y1, x2, y2, cls_id, cls_name in items:
214
+ is_person = cls_id == person_id or cls_name.lower() == "person"
215
+ if is_person:
216
+ person_counter += 1
217
+ label = f"person {person_counter}"
218
+ color = (34, 197, 94)
219
+ text_color = (0, 0, 0)
220
+ else:
221
+ other_counter += 1
222
+ label = f"{cls_name} {other_counter}"
223
+ color = (59, 130, 246)
224
+ text_color = (255, 255, 255)
225
+ if annotation_style == "Border + Label":
226
+ cv2.rectangle(annotated, (x1, y1), (x2, y2), color, 2)
227
+ (tw, th), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
228
+ if annotation_style == "Label saja":
229
+ label_x = max(0, min(x1, annotated.shape[1] - tw - 8))
230
+ label_y = max(th + 4, min(y1 + th + 4, annotated.shape[0] - 6))
231
+ text_y1 = max(0, label_y - th - baseline - 6)
232
+ text_y2 = min(annotated.shape[0], label_y + 2)
233
+ text_x1 = label_x
234
+ text_x2 = min(annotated.shape[1], label_x + tw + 10)
235
+ text_org = (label_x + 4, label_y - 4)
236
+ else:
237
+ text_y1 = max(0, y1 - th - baseline - 6)
238
+ text_y2 = y1
239
+ text_x1 = x1
240
+ text_x2 = min(annotated.shape[1], x1 + tw + 10)
241
+ text_org = (x1 + 4, max(14, y1 - 6))
242
+ cv2.rectangle(annotated, (text_x1, text_y1), (text_x2, text_y2), color, -1)
243
+ cv2.putText(
244
+ annotated,
245
+ label,
246
+ text_org,
247
+ cv2.FONT_HERSHEY_SIMPLEX,
248
+ 0.6,
249
+ text_color,
250
+ 2,
251
+ cv2.LINE_AA,
252
+ )
253
+ return annotated, len(items)
254
 
255
 
256
  def get_detection_scope(model_choice: str) -> str: