rba28 commited on
Commit
cd9b1ce
·
verified ·
1 Parent(s): aa0d411

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -17
app.py CHANGED
@@ -22,12 +22,46 @@ MODEL_CANDIDATES = []
22
  if HF_MODEL_REPO and HF_MODEL_FILE:
23
  MODEL_CANDIDATES.append((HF_MODEL_REPO, HF_MODEL_FILE))
24
 
25
- # ✅ Public repos that work without auth:
26
  MODEL_CANDIDATES += [
27
  ("Javvanny/yolov8m_flying_objects_detection", "yolov8m/weights/best.pt"), # drones/planes/helis/birds
28
  ("doguilmak/Drone-Detection-YOLOv8x", "weight/best.pt"), # drone-focused (larger)
29
  ]
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  # =========================
32
  # LAZY STATE
33
  # =========================
@@ -101,7 +135,8 @@ def _results_to_rows(results) -> List[dict]:
101
  r = results[0]
102
  if getattr(r, "boxes", None) is None:
103
  return rows
104
- names = getattr(r, "names", {}) or _model_names or {}
 
105
  import numpy as np
106
  xyxy = r.boxes.xyxy.cpu().numpy() if hasattr(r.boxes, "xyxy") else np.zeros((0, 4))
107
  confs = r.boxes.conf.cpu().numpy() if hasattr(r.boxes, "conf") else np.zeros((0,))
@@ -109,9 +144,9 @@ def _results_to_rows(results) -> List[dict]:
109
  for i, box in enumerate(xyxy):
110
  x1, y1, x2, y2 = [float(v) for v in box.tolist()]
111
  cls_idx = int(clss[i]) if i < len(clss) else -1
112
- cls_name = names.get(cls_idx, str(cls_idx))
113
  rows.append({
114
- "class": cls_name,
115
  "confidence": float(confs[i]) if i < len(confs) else None,
116
  "x1": x1, "y1": y1, "x2": x2, "y2": y2,
117
  "width": x2 - x1, "height": y2 - y1,
@@ -121,7 +156,8 @@ def _results_to_rows(results) -> List[dict]:
121
  def _count_by_class(rows: List[dict]) -> Dict[str, int]:
122
  d: Dict[str, int] = {}
123
  for r in rows:
124
- d[r["class"]] = d.get(r["class"], 0) + 1
 
125
  return d
126
 
127
  def _write_video(base_path: str, fps: float, w: int, h: int):
@@ -173,30 +209,32 @@ def _save_pdf(title: str, summary: str, counts: Dict[str, int], annotated_image_
173
  c.showPage(); c.save()
174
  return out_path
175
 
176
- # ---- NEW: normalize rows to avoid DataFrame truthiness issues ----
177
  def _normalize_rows(table_rows):
178
- """
179
- Accepts pandas.DataFrame OR list[dict] OR list[list] and returns list[dict] with expected keys.
180
- """
181
  try:
182
  import pandas as pd
183
  if isinstance(table_rows, pd.DataFrame):
184
  return table_rows.to_dict(orient="records")
185
  except Exception:
186
  pass
187
-
188
  if isinstance(table_rows, list) and (not table_rows or isinstance(table_rows[0], dict)):
189
  return table_rows or []
190
-
191
  if isinstance(table_rows, list) and table_rows and isinstance(table_rows[0], list):
192
  headers = ["class","confidence","x1","y1","x2","y2","width","height"]
193
  return [dict(zip(headers, row)) for row in table_rows]
194
-
195
  return []
196
 
197
  # =========================
198
- # INFERENCE (SAFE WRAPPERS)
199
  # =========================
 
 
 
 
 
 
 
 
200
  def detect_image_safe(image, conf: float, iou: float):
201
  try:
202
  if image is None:
@@ -205,8 +243,9 @@ def detect_image_safe(image, conf: float, iou: float):
205
  model = _get_model(conf, iou)
206
  results = model.predict(image, imgsz=960, verbose=False)
207
  r = results[0]
 
208
  rows = _results_to_rows(results)
209
- annotated = r.plot() # BGR ndarray
210
  counts = _count_by_class(rows)
211
  summary = "Detections: " + (", ".join(f"{k}: {v}" for k, v in counts.items()) if rows else "none")
212
 
@@ -263,6 +302,7 @@ def detect_video_safe(video_path: str, conf: float, iou: float, max_frames: int
263
 
264
  results = model.predict(frame, imgsz=960, verbose=False)
265
  r = results[0]
 
266
 
267
  for row in _results_to_rows(results):
268
  totals[row["class"]] = totals.get(row["class"], 0) + 1
@@ -286,6 +326,9 @@ def detect_video_safe(video_path: str, conf: float, iou: float, max_frames: int
286
  def export_pdf_img(summary: str, table_rows, annotated_tmp_jpg: Optional[str]):
287
  try:
288
  rows = _normalize_rows(table_rows)
 
 
 
289
  counts = _count_by_class(rows)
290
  return _save_pdf(
291
  "UAV Detector — Image Report",
@@ -301,6 +344,8 @@ def export_pdf_vid(summary: str, counts_json: str):
301
  counts = json.loads(counts_json) if counts_json else {}
302
  except Exception:
303
  counts = {}
 
 
304
  try:
305
  return _save_pdf("UAV Detector — Video Report", summary or "No summary.", counts or {}, None)
306
  except Exception as e:
@@ -310,8 +355,8 @@ def export_pdf_vid(summary: str, counts_json: str):
310
  # UI (local embedded samples)
311
  # =========================
312
  NOTE = (
313
- "UAV model: detects drones (class names vary per checkpoint, e.g., 'drone', 'uav'). "
314
- "Ensure the drone is ≥30–40 px on the short side for reliable detection."
315
  )
316
 
317
  with gr.Blocks(title="UAV / Drone Detector (YOLO)") as demo:
@@ -330,7 +375,6 @@ If they’re missing, you can still upload your own.
330
  # ---------- IMAGE ----------
331
  with gr.TabItem("Image"):
332
  with gr.Row():
333
- # Use type="filepath" so embedded path loads directly. Uploads also pass a path.
334
  image_in = gr.Image(
335
  value=EMBED_IMG if os.path.exists(EMBED_IMG) else None,
336
  type="filepath",
 
22
  if HF_MODEL_REPO and HF_MODEL_FILE:
23
  MODEL_CANDIDATES.append((HF_MODEL_REPO, HF_MODEL_FILE))
24
 
 
25
  MODEL_CANDIDATES += [
26
  ("Javvanny/yolov8m_flying_objects_detection", "yolov8m/weights/best.pt"), # drones/planes/helis/birds
27
  ("doguilmak/Drone-Detection-YOLOv8x", "weight/best.pt"), # drone-focused (larger)
28
  ]
29
 
30
+ # =========================
31
+ # LABEL TRANSLATION (RU -> EN)
32
+ # =========================
33
+ LABEL_MAP = {
34
+ "БПЛА": "UAV",
35
+ "БПЛА коптер": "Drone",
36
+ "квадрокоптер": "Quadcopter",
37
+ "квадроcамолет": "Quadcopter",
38
+ "самолет": "Airplane",
39
+ "вертолет": "Helicopter",
40
+ "птица": "Bird",
41
+ "человек": "Person",
42
+ "машина": "Car",
43
+ "автомобиль": "Car",
44
+ # add more if your checkpoint uses different strings
45
+ }
46
+
47
+ def map_label(name: str) -> str:
48
+ if not isinstance(name, str):
49
+ return name
50
+ # exact match first
51
+ if name in LABEL_MAP:
52
+ return LABEL_MAP[name]
53
+ # try case-insensitive
54
+ low = name.lower()
55
+ for ru, en in LABEL_MAP.items():
56
+ if low == ru.lower():
57
+ return en
58
+ return name # fallback
59
+
60
+ def translate_names_dict(names_dict: Dict[int, str]) -> Dict[int, str]:
61
+ if not isinstance(names_dict, dict):
62
+ return names_dict
63
+ return {k: map_label(v) for k, v in names_dict.items()}
64
+
65
  # =========================
66
  # LAZY STATE
67
  # =========================
 
135
  r = results[0]
136
  if getattr(r, "boxes", None) is None:
137
  return rows
138
+ names_dict = getattr(r, "names", {}) or _model_names or {}
139
+ names_dict = translate_names_dict(names_dict) # translate for table/CSV
140
  import numpy as np
141
  xyxy = r.boxes.xyxy.cpu().numpy() if hasattr(r.boxes, "xyxy") else np.zeros((0, 4))
142
  confs = r.boxes.conf.cpu().numpy() if hasattr(r.boxes, "conf") else np.zeros((0,))
 
144
  for i, box in enumerate(xyxy):
145
  x1, y1, x2, y2 = [float(v) for v in box.tolist()]
146
  cls_idx = int(clss[i]) if i < len(clss) else -1
147
+ cls_name = names_dict.get(cls_idx, str(cls_idx))
148
  rows.append({
149
+ "class": map_label(cls_name),
150
  "confidence": float(confs[i]) if i < len(confs) else None,
151
  "x1": x1, "y1": y1, "x2": x2, "y2": y2,
152
  "width": x2 - x1, "height": y2 - y1,
 
156
  def _count_by_class(rows: List[dict]) -> Dict[str, int]:
157
  d: Dict[str, int] = {}
158
  for r in rows:
159
+ name = map_label(r.get("class", ""))
160
+ d[name] = d.get(name, 0) + 1
161
  return d
162
 
163
  def _write_video(base_path: str, fps: float, w: int, h: int):
 
209
  c.showPage(); c.save()
210
  return out_path
211
 
 
212
  def _normalize_rows(table_rows):
213
+ """Accept pandas.DataFrame OR list[dict] OR list[list]; return list[dict]."""
 
 
214
  try:
215
  import pandas as pd
216
  if isinstance(table_rows, pd.DataFrame):
217
  return table_rows.to_dict(orient="records")
218
  except Exception:
219
  pass
 
220
  if isinstance(table_rows, list) and (not table_rows or isinstance(table_rows[0], dict)):
221
  return table_rows or []
 
222
  if isinstance(table_rows, list) and table_rows and isinstance(table_rows[0], list):
223
  headers = ["class","confidence","x1","y1","x2","y2","width","height"]
224
  return [dict(zip(headers, row)) for row in table_rows]
 
225
  return []
226
 
227
  # =========================
228
+ # INFERENCE (SAFE WRAPPERS) + ENGLISH OVERLAY
229
  # =========================
230
+ def _apply_english_overlay(r):
231
+ """Override r.names with English mapping so r.plot draws English labels."""
232
+ try:
233
+ if hasattr(r, "names") and isinstance(r.names, dict):
234
+ r.names = translate_names_dict(r.names)
235
+ except Exception:
236
+ pass
237
+
238
  def detect_image_safe(image, conf: float, iou: float):
239
  try:
240
  if image is None:
 
243
  model = _get_model(conf, iou)
244
  results = model.predict(image, imgsz=960, verbose=False)
245
  r = results[0]
246
+ _apply_english_overlay(r) # <- ensure overlay text is English
247
  rows = _results_to_rows(results)
248
+ annotated = r.plot() # BGR ndarray with English labels
249
  counts = _count_by_class(rows)
250
  summary = "Detections: " + (", ".join(f"{k}: {v}" for k, v in counts.items()) if rows else "none")
251
 
 
302
 
303
  results = model.predict(frame, imgsz=960, verbose=False)
304
  r = results[0]
305
+ _apply_english_overlay(r) # <- English overlay per frame
306
 
307
  for row in _results_to_rows(results):
308
  totals[row["class"]] = totals.get(row["class"], 0) + 1
 
326
  def export_pdf_img(summary: str, table_rows, annotated_tmp_jpg: Optional[str]):
327
  try:
328
  rows = _normalize_rows(table_rows)
329
+ # ensure English in report
330
+ for r in rows:
331
+ r["class"] = map_label(r.get("class", ""))
332
  counts = _count_by_class(rows)
333
  return _save_pdf(
334
  "UAV Detector — Image Report",
 
344
  counts = json.loads(counts_json) if counts_json else {}
345
  except Exception:
346
  counts = {}
347
+ # map to English just in case
348
+ counts = {map_label(k): v for k, v in counts.items()}
349
  try:
350
  return _save_pdf("UAV Detector — Video Report", summary or "No summary.", counts or {}, None)
351
  except Exception as e:
 
355
  # UI (local embedded samples)
356
  # =========================
357
  NOTE = (
358
+ "UAV model: detects drones (class names vary per checkpoint). "
359
+ "All labels are translated to English in the UI and exports."
360
  )
361
 
362
  with gr.Blocks(title="UAV / Drone Detector (YOLO)") as demo:
 
375
  # ---------- IMAGE ----------
376
  with gr.TabItem("Image"):
377
  with gr.Row():
 
378
  image_in = gr.Image(
379
  value=EMBED_IMG if os.path.exists(EMBED_IMG) else None,
380
  type="filepath",