167 lines
5.3 KiB
Python
167 lines
5.3 KiB
Python
import os
|
|
import time
|
|
from datetime import datetime
|
|
|
|
import cv2
|
|
|
|
import config
|
|
from camara import CamaraRTSP
|
|
from detector_movimiento import DetectorMovimiento
|
|
from notificador import disparar_webhook_alexa, enviar_telegram
|
|
|
|
USAR_YOLO = True
|
|
identificador = None
|
|
|
|
|
|
def cargar_identificador():
|
|
global identificador
|
|
if identificador is None:
|
|
try:
|
|
from reconocedor import CONOCIDOS, Reconocedor, os as _os
|
|
if _os.path.isdir(CONOCIDOS) and any(
|
|
_os.path.isdir(_os.path.join(CONOCIDOS, n))
|
|
for n in _os.listdir(CONOCIDOS)
|
|
):
|
|
identificador = Reconocedor()
|
|
print(f"Identificacion activa. Conocidos: "
|
|
f"{list(identificador.conocidos.keys())}")
|
|
else:
|
|
print("Sin conocidos registrados (ejecuta enroll.py). "
|
|
"Modo deteccion simple.")
|
|
except Exception as e:
|
|
print(f"No se pudo cargar identificador: {e}")
|
|
return identificador
|
|
|
|
|
|
def componer_alerta(resultados, cam_nombre, ts):
|
|
conocidas = sorted({r["nombre"] for r in resultados if "nombre" in r})
|
|
desconocidas = sum(1 for r in resultados if r.get("desconocido"))
|
|
zona = "autorizada" if cam_nombre in config.ZONAS_AUTORIZADAS else "NO autorizada"
|
|
sufijo = f" (zona {zona})"
|
|
|
|
if conocidas and desconocidas:
|
|
return (f"👧 {', '.join(conocidas)} | "
|
|
f"⚠️ {desconocidas} persona(s) desconocida(s) "
|
|
f"en {cam_nombre}{sufijo} - {ts}")
|
|
if conocidas:
|
|
return (f"👧 {', '.join(conocidas)} detectada(s) "
|
|
f"en {cam_nombre}{sufijo} - {ts}")
|
|
if desconocidas:
|
|
return (f"🚨 {desconocidas} persona(s) en {cam_nombre}"
|
|
f"{sufijo} - {ts}")
|
|
return f"🚨 Presencia en {cam_nombre}{sufijo} - {ts}"
|
|
|
|
|
|
def detectar_presencia(modelo, frame):
|
|
"""True si hay al menos una persona en el frame (YOLO)."""
|
|
if modelo is None:
|
|
return True
|
|
try:
|
|
r = modelo.predict(frame, verbose=False, conf=0.4)
|
|
return any(int(b.cls) == 0 for b in r[0].boxes)
|
|
except Exception:
|
|
return True
|
|
|
|
|
|
def dentro_de_franja_horaria(hora_actual=None) -> bool:
|
|
if hora_actual is None:
|
|
hora_actual = datetime.now().hour
|
|
inicio = config.HORA_INICIO
|
|
fin = config.HORA_FIN
|
|
if inicio < fin:
|
|
return inicio <= hora_actual < fin
|
|
return hora_actual >= inicio or hora_actual < fin
|
|
|
|
|
|
def main():
|
|
if not config.CAMARAS:
|
|
print("No hay cámaras configuradas en .env")
|
|
return
|
|
|
|
os.makedirs(config.SNAPSHOT_DIR, exist_ok=True)
|
|
|
|
camaras = [CamaraRTSP(c["rtsp"], c["nombre"]) for c in config.CAMARAS]
|
|
detectores = []
|
|
ultima_alerta = {}
|
|
|
|
for i, c in enumerate(config.CAMARAS):
|
|
detectores.append(DetectorMovimiento())
|
|
ultima_alerta[i] = 0
|
|
|
|
ident = cargar_identificador()
|
|
|
|
modelo_presencia = None
|
|
try:
|
|
from ultralytics import YOLO
|
|
ruta_yolo = os.path.join(os.path.dirname(os.path.abspath(__file__)), "yolov8n.pt")
|
|
modelo_presencia = YOLO(ruta_yolo)
|
|
except Exception as e:
|
|
print(f"No se pudo cargar YOLO para presencia: {e}")
|
|
|
|
print("Sistema de vigilancia iniciado. Franja activa: "
|
|
f"{config.HORA_INICIO}:00 - {config.HORA_FIN}:00")
|
|
|
|
while True:
|
|
if not dentro_de_franja_horaria():
|
|
time.sleep(30)
|
|
continue
|
|
|
|
for i, cam in enumerate(camaras):
|
|
frame = cam.leer_frame()
|
|
if frame is None:
|
|
continue
|
|
|
|
detector = detectores[i]
|
|
movido = detector.hay_movimiento(frame)
|
|
|
|
ahora = time.time()
|
|
if not movido:
|
|
continue
|
|
if (ahora - ultima_alerta[i]) <= config.COOLDOWN:
|
|
continue
|
|
|
|
if not detectar_presencia(modelo_presencia, frame):
|
|
continue
|
|
|
|
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
ruta = os.path.join(
|
|
config.SNAPSHOT_DIR, f"snapshot_{cam.nombre}_{ts}.jpg"
|
|
)
|
|
|
|
texto = f"🚨 Presencia en {cam.nombre} - {ts}"
|
|
if ident is not None:
|
|
try:
|
|
resultados = []
|
|
for _ in range(3):
|
|
f2 = cam.leer_frame()
|
|
if f2 is None:
|
|
continue
|
|
resultados += ident.analizar_frame(f2)
|
|
time.sleep(0.1)
|
|
if resultados:
|
|
texto = componer_alerta(resultados, cam.nombre, ts)
|
|
except Exception as e:
|
|
print(f"[{cam.nombre}] Error identificando: {e}")
|
|
else:
|
|
zona = "autorizada" if cam.nombre in config.ZONAS_AUTORIZADAS else "NO autorizada"
|
|
texto = f"🚨 Presencia en {cam.nombre} (zona {zona}) - {ts}"
|
|
|
|
cv2_ok = True
|
|
try:
|
|
cv2.imwrite(ruta, frame)
|
|
except Exception as e:
|
|
cv2_ok = False
|
|
print(f"[{cam.nombre}] Error guardando snapshot: {e}")
|
|
|
|
if cv2_ok:
|
|
enviar_telegram(ruta, texto)
|
|
disparar_webhook_alexa(texto)
|
|
ultima_alerta[i] = ahora
|
|
print(texto)
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|