Update 40 restore person count labels
Browse files- CHANGELOG.md +5 -0
- 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", "
|
| 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 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
cls_name = f"class_{cls_id}"
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
(
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
annotated,
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
(
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|