Spaces:
Sleeping
Sleeping
File size: 3,675 Bytes
c66d0d6 982fb7b f0b735d c66d0d6 f0b735d c66d0d6 f0b735d 982fb7b c66d0d6 982fb7b f0b735d c66d0d6 982fb7b c66d0d6 f0b735d 982fb7b c66d0d6 f0b735d c66d0d6 982fb7b c66d0d6 f0b735d c66d0d6 f0b735d c66d0d6 f0b735d c66d0d6 f0b735d c66d0d6 982fb7b c66d0d6 f0b735d c66d0d6 f0b735d c66d0d6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
import cv2
import gradio as gr
from ultralytics import YOLO
# ── Config ─────────────────────────────────────────────
MODEL_PATH = "yolov8n.pt" # peso pre-entrenado (clase “person”)
CONF_THRES = 0.30 # confianza mínima
LINE_RATIO = 0.50 # posición de la línea virtual (50 % de la altura)
# ───────────────────────────────────────────────────────
# modelo — se carga una sola vez
model = YOLO(MODEL_PATH)
# estado global del contador
memory, in_count, out_count = {}, 0, 0
def count_people(frame_rgb):
"""
▸ Recibe un frame RGB (numpy) desde la webcam.
▸ Devuelve el frame anotado (RGB) y el texto del contador.
"""
global memory, in_count, out_count
if frame_rgb is None:
return None, ""
# ── convertir RGB ➜ BGR (OpenCV/YOLO) ──────────────
frame_bgr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)
h, w = frame_bgr.shape[:2]
line_y = int(h * LINE_RATIO)
# ── detección + tracking (ByteTrack interno) ───────
results = model.track(frame_bgr,
classes=[0], # solo “person”
conf=CONF_THRES,
persist=True,
verbose=False)
annotated = frame_bgr.copy()
cv2.line(annotated, (0, line_y), (w, line_y), (0, 255, 255), 2)
if results:
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
tid = int(box.id[0]) if box.id is not None else -1
prev_cy = memory.get(tid, cy)
if prev_cy < line_y <= cy: # cruza de arriba → abajo (ENTRA)
in_count += 1
elif prev_cy > line_y >= cy: # cruza de abajo → arriba (SALE)
out_count += 1
memory[tid] = cy
cv2.rectangle(annotated, (x1, y1), (x2, y2), (0, 255, 0), 1)
cv2.circle(annotated, (cx, cy), 3, (0, 0, 255), -1)
cv2.putText(annotated, str(tid), (x1, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
total = in_count - out_count
label = f"In: {in_count} | Out: {out_count} | Ocupación: {total}"
# ── devolver en RGB para que Gradio lo muestre bien ─
annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
return annotated_rgb, label
def reset_counts():
"""Resetea contador y memoria."""
global memory, in_count, out_count
memory, in_count, out_count = {}, 0, 0
return None, ""
# ── Interfaz Gradio ────────────────────────────────────
with gr.Blocks(title="Contador de personas (entrada única)") as demo:
gr.Markdown("# Contador de personas (entrada única)")
with gr.Row():
cam = gr.Image(sources=["webcam"], streaming=True, label="frame")
out_img = gr.Image(label="Video")
out_lbl = gr.Text(label="Contador")
btn_clear = gr.Button("Limpiar")
# conectar streaming y botón
cam.stream(fn=count_people, inputs=[cam], outputs=[out_img, out_lbl])
btn_clear.click(fn=reset_counts, outputs=[out_img, out_lbl])
# ── Cola y lanzamiento ─────────────────────────────────
demo.queue(concurrency_count=2, max_size=60) # 2 hilos, cola máx 60 frames
demo.launch()
|