Mateo's picture
put back 8 frames
7ce3f7e
Raw
History Blame
5.78 kB
import os
import cv2
import gradio as gr
import imageio.v2 as imageio
import numpy as np
from PIL import Image, ImageDraw
from vision import Classifier
from utils import box_iou, nms
def _sample_indices(total, n):
if total <= 0:
return []
if total <= n:
return list(range(total))
return np.linspace(0, total - 1, n).astype(int).tolist()
def _select_from_list(frames, n):
if not frames:
return []
indices = _sample_indices(len(frames), n)
return [frames[i] for i in indices]
def _extract_with_cv2(video_path, n):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError("Could not open video file.")
frames = []
try:
total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if total > 0:
for idx in _sample_indices(total, n):
cap.set(cv2.CAP_PROP_POS_FRAMES, int(idx))
ok, frame = cap.read()
if not ok:
continue
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(Image.fromarray(frame))
return frames
all_frames = []
while True:
ok, frame = cap.read()
if not ok:
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
all_frames.append(Image.fromarray(frame))
return _select_from_list(all_frames, n)
finally:
cap.release()
def _get_imageio_count(reader):
try:
return reader.count_frames()
except Exception:
pass
try:
meta = reader.get_meta_data()
count = meta.get("nframes")
if count and count != float("inf"):
return int(count)
except Exception:
pass
return None
def _extract_with_imageio(video_path, n):
reader = imageio.get_reader(video_path)
try:
total = _get_imageio_count(reader)
if total:
frames = []
for idx in _sample_indices(total, n):
frame = reader.get_data(idx)
frames.append(Image.fromarray(frame))
return frames
all_frames = [Image.fromarray(frame) for frame in reader]
return _select_from_list(all_frames, n)
finally:
reader.close()
def split_video(video_path, n=8):
if not video_path or not os.path.exists(video_path):
return []
# Prefer OpenCV for random access; use imageio as a fallback path if needed.
try:
return _extract_with_cv2(video_path, n)
except Exception:
return _extract_with_imageio(video_path, n)
model = Classifier(format="onnx")
def _resolve_video_path(video_input):
if not video_input:
return None
if isinstance(video_input, str):
return video_input
if isinstance(video_input, dict):
for key in ("name", "path", "data", "video"):
value = video_input.get(key)
if isinstance(value, str) and os.path.exists(value):
return value
if isinstance(video_input, (list, tuple)):
for value in video_input:
if isinstance(value, str) and os.path.exists(value):
return value
return None
def _draw_detections(pil_img, preds):
img = pil_img.copy()
draw = ImageDraw.Draw(img)
width, height = img.size
color = (255, 80, 0)
preds = np.asarray(preds)
for x1, y1, x2, y2, conf in preds:
x1 = int(max(0.0, min(1.0, float(x1))) * width)
y1 = int(max(0.0, min(1.0, float(y1))) * height)
x2 = int(max(0.0, min(1.0, float(x2))) * width)
y2 = int(max(0.0, min(1.0, float(y2))) * height)
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
draw.text((x1 + 4, y1 + 4), f"{conf:.2f}", fill=color)
draw.text((6, 6), f"detections: {len(preds)}", fill=color)
return img
def infer(video_file):
video_path = _resolve_video_path(video_file)
frames = split_video(video_path, n=8)
if not frames:
return []
n_frames = len(frames)
boxes = np.zeros((0, 5), dtype=np.float64)
frame_preds = []
for frame in frames:
bbox = np.asarray(model(frame), dtype=np.float64)
frame_preds.append(bbox)
if bbox.size > 0:
boxes = np.vstack([boxes, bbox])
if boxes.size == 0:
return []
main_bboxes = np.asarray(nms(boxes), dtype=np.float64)
if main_bboxes.size == 0:
return []
# Keep main boxes that appear in enough frames.
matches_per_main = np.zeros(len(main_bboxes), dtype=int)
for bbox in frame_preds:
if bbox.size == 0:
continue
ious = box_iou(bbox[:, :4], main_bboxes[:, :4])
matches_per_main += (ious > 0).any(axis=1).astype(int)
keep_main = matches_per_main > n_frames // 2
kept_main = main_bboxes[keep_main] if np.any(keep_main) else np.zeros((0, 5), dtype=np.float64)
if kept_main.size == 0:
return []
outputs = []
for main_box in kept_main:
for frame, bbox in zip(frames, frame_preds):
if bbox.size == 0:
continue
ious = box_iou(bbox[:, :4], main_box[:4].reshape(1, 4))
if (ious > 0).any():
match_idx = int(np.argmax(ious[0]))
outputs.append(_draw_detections(frame, bbox[match_idx : match_idx + 1]))
break
return outputs
with gr.Blocks() as demo:
gr.Markdown("## Pyronear Wildfire Detection")
with gr.Row():
video_in = gr.Video(label="Upload MP4")
gallery_out = gr.Gallery(label="Wildfire detected", columns=2, height=360)
run_btn = gr.Button("Detect")
run_btn.click(fn=infer, inputs=video_in, outputs=gallery_out)
if __name__ == "__main__":
demo.launch()