rba28 commited on
Commit
2ebd3aa
·
verified ·
1 Parent(s): fdf5ad5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -52
app.py CHANGED
@@ -12,33 +12,8 @@ REPO_ID = "mshamrai/yolov8s-visdrone"
12
  FILENAME = "weights/best.pt"
13
 
14
  SAMPLES_DIR = "samples"
15
- # Embedded samples (auto-downloaded on start)
16
- TEST_IMAGE = os.path.join(SAMPLES_DIR, "test_image.jpg")
17
- TEST_VIDEO = os.path.join(SAMPLES_DIR, "test_video.mp4")
18
-
19
- SAMPLE_URLS = {
20
- TEST_IMAGE: "https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg", # small image
21
- TEST_VIDEO: "https://github.com/ultralytics/assets/releases/download/v0.0.0/drone.mp4", # short drone clip
22
- }
23
-
24
- def ensure_samples():
25
- os.makedirs(SAMPLES_DIR, exist_ok=True)
26
- try:
27
- import requests
28
- except Exception:
29
- return
30
- for local_path, url in SAMPLE_URLS.items():
31
- if os.path.exists(local_path):
32
- continue
33
- try:
34
- r = requests.get(url, timeout=30)
35
- r.raise_for_status()
36
- with open(local_path, "wb") as f:
37
- f.write(r.content)
38
- except Exception:
39
- pass
40
-
41
- ensure_samples()
42
 
43
  # -------------------
44
  # Lazy state
@@ -177,7 +152,7 @@ def _save_pdf(title: str, summary: str, counts: Dict[str, int], annotated_image_
177
  # -------------------
178
  def detect_image(image, conf: float, iou: float):
179
  if image is None:
180
- return None, None, "No image provided.", None, None
181
  cv2 = _lazy_cv2()
182
  model = _get_model(conf, iou)
183
  results = model.predict(image, imgsz=960, verbose=False)
@@ -185,7 +160,7 @@ def detect_image(image, conf: float, iou: float):
185
  rows = _results_to_rows(results)
186
  annotated = r.plot() # BGR ndarray
187
  counts = _count_by_class(rows)
188
- summary = "Detections: " + ", ".join(f"{k}: {v}" for k, v in counts.items()) if rows else "No objects detected."
189
  tmp_img = os.path.join(tempfile.gettempdir(), f"annotated_{int(time.time())}.jpg")
190
  try:
191
  cv2.imwrite(tmp_img, annotated)
@@ -197,19 +172,19 @@ def detect_image(image, conf: float, iou: float):
197
 
198
  def detect_video(video_path: str, conf: float, iou: float, max_frames: int = 300):
199
  if not video_path:
200
- return None, None, "No video provided.", None
201
  cv2 = _lazy_cv2()
202
  model = _get_model(conf, iou)
203
  cap = cv2.VideoCapture(video_path)
204
  if not cap.isOpened():
205
- return None, None, "Failed to open video.", None
206
  fps = cap.get(cv2.CAP_PROP_FPS) or 24.0
207
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) or 1280)
208
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 720)
209
  writer, out_path = _write_video(os.path.join(tempfile.gettempdir(), f"annotated_{int(time.time())}"), fps, w, h)
210
  if writer is None or (hasattr(writer, "isOpened") and not writer.isOpened()):
211
  cap.release()
212
- return None, None, "Video writer could not open. Try another format/resolution.", None
213
  totals: Dict[str, int] = {}
214
  frames = 0
215
  try:
@@ -229,7 +204,7 @@ def detect_video(video_path: str, conf: float, iou: float, max_frames: int = 300
229
  finally:
230
  cap.release()
231
  writer.release()
232
- summary = "Detections (frame-wise tallies): " + ", ".join(f"{k}: {v}" for k, v in totals.items()) if totals else "No objects detected."
233
  tally_rows = [{"class": k, "count": v} for k, v in sorted(totals.items())]
234
  csv_path = _save_csv(tally_rows)
235
  return out_path, totals, summary, csv_path
@@ -243,38 +218,37 @@ def export_pdf_vid(summary: str, counts: dict):
243
  return _save_pdf("Airspace Drone Detector — Video Report", summary or "No summary.", counts or {}, None)
244
 
245
  # -------------------
246
- # UI (preloaded samples)
247
  # -------------------
248
- EXAMPLE_NOTE = (
249
- "Tip: This model is trained on VisDrone-style aerial objects (small targets). "
250
- "Use 640–1080p aerial footage where drones are visible."
251
  )
252
 
253
- with gr.Blocks(title="Airspace Drone Detector (YOLOv8 VisDrone)") as demo:
254
  gr.Markdown(
255
  """
256
- # Airspace Drone Detector (Pretrained YOLOv8 - VisDrone)
257
- The sample **image** and **video** are already loaded below — just click **Run**.
258
-
259
- **Exports:** CSV + PDF reports.
260
- **Note:** On CPU Spaces, long videos are truncated via **Max frames**.
261
  """
262
  )
263
 
264
  with gr.Tabs():
265
- # IMAGE (preloaded)
266
  with gr.TabItem("Image"):
267
  with gr.Row():
268
  image_in = gr.Image(
269
- value=TEST_IMAGE if os.path.exists(TEST_IMAGE) else None,
270
  type="numpy",
271
- label="Input Image (preloaded)"
272
  )
273
  with gr.Column():
274
  conf_img = gr.Slider(0.05, 0.8, 0.35, step=0.05, label="Confidence")
275
  iou_img = gr.Slider(0.1, 0.9, 0.45, step=0.05, label="NMS IoU")
 
276
  run_img = gr.Button("Run Detection")
277
- gr.Markdown(EXAMPLE_NOTE)
278
 
279
  image_out = gr.Image(label="Annotated Image")
280
  table_out = gr.Dataframe(headers=["class","confidence","x1","y1","x2","y2","width","height"], wrap=True)
@@ -287,6 +261,13 @@ The sample **image** and **video** are already loaded below — just click **Run
287
 
288
  annotated_tmp_img_path = gr.State(value=None)
289
 
 
 
 
 
 
 
 
290
  def _run_img(image, conf, iou):
291
  return detect_image(image, conf, iou)
292
 
@@ -302,19 +283,20 @@ The sample **image** and **video** are already loaded below — just click **Run
302
  outputs=[pdf_img_path],
303
  )
304
 
305
- # VIDEO (preloaded)
306
  with gr.TabItem("Video"):
307
  with gr.Row():
308
  video_in = gr.Video(
309
- value=TEST_VIDEO if os.path.exists(TEST_VIDEO) else None,
310
- label="Input Video (preloaded)"
311
  )
312
  with gr.Column():
313
  conf_vid = gr.Slider(0.05, 0.8, 0.35, step=0.05, label="Confidence")
314
  iou_vid = gr.Slider(0.1, 0.9, 0.45, step=0.05, label="NMS IoU")
315
  max_frames = gr.Slider(60, 2000, 300, step=10, label="Max frames to process")
 
316
  run_vid = gr.Button("Run Detection")
317
- gr.Markdown(EXAMPLE_NOTE)
318
 
319
  video_out = gr.Video(label="Annotated Video")
320
  counts_text = gr.Textbox(label="Per-class tally (JSON)", max_lines=20)
@@ -325,6 +307,13 @@ The sample **image** and **video** are already loaded below — just click **Run
325
  pdf_vid_btn = gr.Button("Generate PDF Report")
326
  pdf_vid_path = gr.File(label="PDF Report", interactive=False)
327
 
 
 
 
 
 
 
 
328
  def _run_vid(vpath, conf, iou, maxf):
329
  out_path, counts, summary, csv_path = detect_video(vpath, conf, iou, int(maxf))
330
  counts_str = json.dumps(counts or {}, ensure_ascii=False, indent=2)
@@ -352,7 +341,8 @@ The sample **image** and **video** are already loaded below — just click **Run
352
  gr.Markdown(
353
  f"""
354
  **Weights:** `{REPO_ID}/{FILENAME}` (downloaded lazily)
355
- **Diagnostics** — FFmpeg: {'Yes' if _ffmpeg_ok() else 'No'} • Python: 3.10
 
356
  """
357
  )
358
 
 
12
  FILENAME = "weights/best.pt"
13
 
14
  SAMPLES_DIR = "samples"
15
+ EMBED_IMG = os.path.join(SAMPLES_DIR, "aerial_image.jpg")
16
+ EMBED_VID = os.path.join(SAMPLES_DIR, "aerial_video.mp4")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  # -------------------
19
  # Lazy state
 
152
  # -------------------
153
  def detect_image(image, conf: float, iou: float):
154
  if image is None:
155
+ return None, [], "No image provided.", None, None
156
  cv2 = _lazy_cv2()
157
  model = _get_model(conf, iou)
158
  results = model.predict(image, imgsz=960, verbose=False)
 
160
  rows = _results_to_rows(results)
161
  annotated = r.plot() # BGR ndarray
162
  counts = _count_by_class(rows)
163
+ summary = "Detections: " + (", ".join(f"{k}: {v}" for k, v in counts.items()) if rows else "none")
164
  tmp_img = os.path.join(tempfile.gettempdir(), f"annotated_{int(time.time())}.jpg")
165
  try:
166
  cv2.imwrite(tmp_img, annotated)
 
172
 
173
  def detect_video(video_path: str, conf: float, iou: float, max_frames: int = 300):
174
  if not video_path:
175
+ return None, {}, "No video provided.", None
176
  cv2 = _lazy_cv2()
177
  model = _get_model(conf, iou)
178
  cap = cv2.VideoCapture(video_path)
179
  if not cap.isOpened():
180
+ return None, {}, "Failed to open video.", None
181
  fps = cap.get(cv2.CAP_PROP_FPS) or 24.0
182
  w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) or 1280)
183
  h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 720)
184
  writer, out_path = _write_video(os.path.join(tempfile.gettempdir(), f"annotated_{int(time.time())}"), fps, w, h)
185
  if writer is None or (hasattr(writer, "isOpened") and not writer.isOpened()):
186
  cap.release()
187
+ return None, {}, "Video writer could not open. Try another format/resolution.", None
188
  totals: Dict[str, int] = {}
189
  frames = 0
190
  try:
 
204
  finally:
205
  cap.release()
206
  writer.release()
207
+ summary = "Detections (frame-wise tallies): " + (", ".join(f"{k}: {v}" for k, v in totals.items()) if totals else "none")
208
  tally_rows = [{"class": k, "count": v} for k, v in sorted(totals.items())]
209
  csv_path = _save_csv(tally_rows)
210
  return out_path, totals, summary, csv_path
 
218
  return _save_pdf("Airspace Drone Detector — Video Report", summary or "No summary.", counts or {}, None)
219
 
220
  # -------------------
221
+ # UI (embedded-local samples + uploads)
222
  # -------------------
223
+ NOTE = (
224
+ "Model: VisDrone (aerial **cars/pedestrians/vehicles**). It does **not** include a 'drone' class. "
225
+ "Use top‑down scenes with people/traffic for best results."
226
  )
227
 
228
+ with gr.Blocks(title="Aerial Object Detector (VisDrone)") as demo:
229
  gr.Markdown(
230
  """
231
+ # Aerial Object Detector (Pretrained on VisDrone)
232
+ Use the **embedded samples** or your own uploads.
233
+ Exports: **CSV** and **PDF** reports.
 
 
234
  """
235
  )
236
 
237
  with gr.Tabs():
238
+ # IMAGE
239
  with gr.TabItem("Image"):
240
  with gr.Row():
241
  image_in = gr.Image(
242
+ value=EMBED_IMG if os.path.exists(EMBED_IMG) else None,
243
  type="numpy",
244
+ label="Input Image (embedded or upload)"
245
  )
246
  with gr.Column():
247
  conf_img = gr.Slider(0.05, 0.8, 0.35, step=0.05, label="Confidence")
248
  iou_img = gr.Slider(0.1, 0.9, 0.45, step=0.05, label="NMS IoU")
249
+ load_embed_img = gr.Button("Load Embedded Sample Image")
250
  run_img = gr.Button("Run Detection")
251
+ gr.Markdown(NOTE)
252
 
253
  image_out = gr.Image(label="Annotated Image")
254
  table_out = gr.Dataframe(headers=["class","confidence","x1","y1","x2","y2","width","height"], wrap=True)
 
261
 
262
  annotated_tmp_img_path = gr.State(value=None)
263
 
264
+ def _load_embed_img():
265
+ if os.path.exists(EMBED_IMG):
266
+ return EMBED_IMG
267
+ return None
268
+
269
+ load_embed_img.click(fn=_load_embed_img, outputs=[image_in])
270
+
271
  def _run_img(image, conf, iou):
272
  return detect_image(image, conf, iou)
273
 
 
283
  outputs=[pdf_img_path],
284
  )
285
 
286
+ # VIDEO
287
  with gr.TabItem("Video"):
288
  with gr.Row():
289
  video_in = gr.Video(
290
+ value=EMBED_VID if os.path.exists(EMBED_VID) else None,
291
+ label="Input Video (embedded or upload)"
292
  )
293
  with gr.Column():
294
  conf_vid = gr.Slider(0.05, 0.8, 0.35, step=0.05, label="Confidence")
295
  iou_vid = gr.Slider(0.1, 0.9, 0.45, step=0.05, label="NMS IoU")
296
  max_frames = gr.Slider(60, 2000, 300, step=10, label="Max frames to process")
297
+ load_embed_vid = gr.Button("Load Embedded Sample Video")
298
  run_vid = gr.Button("Run Detection")
299
+ gr.Markdown(NOTE)
300
 
301
  video_out = gr.Video(label="Annotated Video")
302
  counts_text = gr.Textbox(label="Per-class tally (JSON)", max_lines=20)
 
307
  pdf_vid_btn = gr.Button("Generate PDF Report")
308
  pdf_vid_path = gr.File(label="PDF Report", interactive=False)
309
 
310
+ def _load_embed_vid():
311
+ if os.path.exists(EMBED_VID):
312
+ return EMBED_VID
313
+ return None
314
+
315
+ load_embed_vid.click(fn=_load_embed_vid, outputs=[video_in])
316
+
317
  def _run_vid(vpath, conf, iou, maxf):
318
  out_path, counts, summary, csv_path = detect_video(vpath, conf, iou, int(maxf))
319
  counts_str = json.dumps(counts or {}, ensure_ascii=False, indent=2)
 
341
  gr.Markdown(
342
  f"""
343
  **Weights:** `{REPO_ID}/{FILENAME}` (downloaded lazily)
344
+ **Diagnostics** — FFmpeg: {'Yes' if _ffmpeg_ok() else 'No'} • Python: 3.10
345
+ **Tip:** For true *drone* detection, I can swap in a UAV‑specific model. Say the word and I’ll rewire it.
346
  """
347
  )
348