diff --git a/vigilancia/enroll.py b/vigilancia/enroll.py index c3c1f95..286d66f 100644 --- a/vigilancia/enroll.py +++ b/vigilancia/enroll.py @@ -35,9 +35,9 @@ def capturar_cuerpo(r, frame): def main(): - nombres = sys.argv[1:3] - while len(nombres) < 2: - nombres.append(f"Hija{len(nombres) + 1}") + nombres = sys.argv[1:] + if not nombres: + nombres = ["Hija1", "Hija2"] print(f"Registrando: {nombres}") r = rec.cargar_reconocedor() @@ -102,6 +102,9 @@ def main(): cv2.imwrite(os.path.join(carpeta, "cara_ref.jpg"), frame[max(0, y - 20):y + h + 20, max(0, x - 20):x + w + 20]) print(f" Cara de {nombre} registrada (score {caja[-1]:.2f})") + if os.path.exists(os.path.join(carpeta, "body.npy")): + print(f" {nombre} ya tenia cuerpo; registro completado.") + break fase = "cuerpo" elif fase == "cuerpo" and tecla == ord("b"): diff --git a/vigilancia/main.py b/vigilancia/main.py index a8c6cd6..67ae00f 100644 --- a/vigilancia/main.py +++ b/vigilancia/main.py @@ -131,7 +131,13 @@ def main(): texto = f"🚨 Presencia en {cam.nombre} - {ts}" if ident is not None: try: - resultados = ident.analizar_frame(frame) + 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: diff --git a/vigilancia/reconocedor.py b/vigilancia/reconocedor.py index 0ebe201..00efd74 100644 --- a/vigilancia/reconocedor.py +++ b/vigilancia/reconocedor.py @@ -69,19 +69,48 @@ class Reconocedor: return [c for c in caras if c[-1] >= 0.6] def embedding_cara(self, frame, caja): - x, y, w, h = (int(v) for v in caja[:4]) - x, y = max(0, x), max(0, y) - recorte = frame[y:y + h, x:x + w] - if recorte.size == 0: + alineada = self._alinear_cara(frame, caja) + if alineada is None: return None blob = cv2.dnn.blobFromImage( - recorte, 1.0 / 128, (112, 112), (127.5, 127.5, 127.5), swapRB=True + alineada, 1.0 / 128, (112, 112), (127.5, 127.5, 127.5), swapRB=True ) self.arcface.setInput(blob) emb = self.arcface.forward().flatten() norm = np.linalg.norm(emb) return emb / norm if norm > 0 else None + @staticmethod + def _alinear_cara(frame, caja): + """Alinea el rostro con los landmarks (ArcFace espera 112x112 alineado).""" + if len(caja) < 15: + return None + pts = caja[4:14].reshape(5, 2) + if pts.min() < 0: + return None + # Template estandar de ArcFace (112x112) + dst = np.array([ + [38.2946, 51.6963], + [73.5318, 51.5014], + [56.0252, 71.7366], + [41.5493, 92.3655], + [70.7299, 92.2041], + ], dtype=np.float32) + # Orden YuNet: ojo der, ojo izq, nariz, comisura der, comisura izq + src = np.array([ + pts[1], pts[0], pts[2], pts[4], pts[3], + ], dtype=np.float32) + try: + M, _ = cv2.estimateAffinePartial2D(src, dst, method=cv2.LMEDS) + except Exception: + return None + if M is None: + return None + alineada = cv2.warpAffine( + frame, M, (112, 112), borderValue=0.0 + ) + return alineada + # ------------------------------------------------------------------ # Cuerpo # ------------------------------------------------------------------