version estable del seguimiento
This commit is contained in:
parent
0d58931665
commit
e10bfb7bf4
432
seguimiento2.py
432
seguimiento2.py
@ -27,30 +27,15 @@ ASPECT_RATIO_MIN = 0.5
|
|||||||
ASPECT_RATIO_MAX = 4.0
|
ASPECT_RATIO_MAX = 4.0
|
||||||
AREA_MIN_CALIDAD = 1200
|
AREA_MIN_CALIDAD = 1200
|
||||||
FRAMES_CALIDAD = 2
|
FRAMES_CALIDAD = 2
|
||||||
|
TIEMPO_MIN_TRANSITO_NO_VECINO = 10.0
|
||||||
TIEMPO_MAX_AUSENCIA = 800.0
|
TIEMPO_MAX_AUSENCIA = 800.0
|
||||||
|
|
||||||
# ─── TIEMPOS DE VIDA DE TRACKERS
|
# ─── UMBRALES DE RE-ID (VERSIÓN ESTABLE)
|
||||||
TIEMPO_PACIENCIA_BORDE_CON_ID = 5.0 # Segundos antes de matar un ID conocido en borde
|
|
||||||
TIEMPO_PACIENCIA_BORDE_SIN_ID = 1.0 # Segundos antes de matar un candidato en borde
|
|
||||||
TIEMPO_PACIENCIA_INTERIOR = 8.0 # Vida para ID conocido en el interior
|
|
||||||
TIEMPO_PACIENCIA_INTERIOR_NUEVO = 1.5 # Vida para candidato sin ID en interior
|
|
||||||
|
|
||||||
# ─── RE-ADQUISICIÓN RÁPIDA (persona que sale y entra por la misma puerta)
|
|
||||||
RADIO_REENTRADA = 80 # Píxeles: si reaparece a menos de esta distancia, re-adquirir
|
|
||||||
TIEMPO_REENTRADA = 15.0 # Segundos máximos para considerar una reentrada
|
|
||||||
|
|
||||||
# ─── UMBRALES DE RE-ID
|
|
||||||
# ⚡ UMBRAL_REID_MISMA_CAM ajustado a 0.65 para recuperar estabilidad en sombras y giros
|
|
||||||
UMBRAL_REID_MISMA_CAM = 0.65
|
UMBRAL_REID_MISMA_CAM = 0.65
|
||||||
UMBRAL_REID_VECINO = 0.62
|
UMBRAL_REID_VECINO = 0.55
|
||||||
UMBRAL_REID_NO_VECINO = 0.80
|
UMBRAL_REID_NO_VECINO = 0.72
|
||||||
MARGEN_MINIMO_REID = 0.07
|
|
||||||
MAX_FIRMAS_MEMORIA = 15
|
MAX_FIRMAS_MEMORIA = 15
|
||||||
|
|
||||||
# ─── TIEMPO ENTRE TURNOS ACTIVOS (para el anti-fantasma dinámico)
|
|
||||||
TIEMPO_TURNO_ROTATIVO = len(SECUENCIA) * 0.035
|
|
||||||
|
|
||||||
# ─── COLORES Y FUENTE
|
|
||||||
C_CANDIDATO = (150, 150, 150)
|
C_CANDIDATO = (150, 150, 150)
|
||||||
C_LOCAL = (0, 255, 0)
|
C_LOCAL = (0, 255, 0)
|
||||||
C_GLOBAL = (0, 165, 255)
|
C_GLOBAL = (0, 165, 255)
|
||||||
@ -61,26 +46,25 @@ FUENTE = cv2.FONT_HERSHEY_SIMPLEX
|
|||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# INICIALIZACIÓN DEL MOTOR DEEP LEARNING (ONNX)
|
# INICIALIZACIÓN DEL MOTOR DEEP LEARNING (ONNX)
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
print("Cargando motor de Re-Identificación (OSNet ONNX en CPU)...")
|
print("Cargando cerebro de Re-Identificación (OSNet)...")
|
||||||
try:
|
try:
|
||||||
ort_session = ort.InferenceSession(ONNX_MODEL_PATH, providers=['CPUExecutionProvider'])
|
ort_session = ort.InferenceSession(ONNX_MODEL_PATH, providers=['CPUExecutionProvider'])
|
||||||
input_name = ort_session.get_inputs()[0].name
|
input_name = ort_session.get_inputs()[0].name
|
||||||
print("OSNet cargado.")
|
print("Modelo OSNet cargado exitosamente.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR FATAL: No se pudo cargar {ONNX_MODEL_PATH}. {e}")
|
print(f"ERROR FATAL: No se pudo cargar {ONNX_MODEL_PATH}.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32).reshape(1, 3, 1, 1)
|
MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32).reshape(1, 3, 1, 1)
|
||||||
STD = np.array([0.229, 0.224, 0.225], dtype=np.float32).reshape(1, 3, 1, 1)
|
STD = np.array([0.229, 0.224, 0.225], dtype=np.float32).reshape(1, 3, 1, 1)
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# 1. MOTOR HÍBRIDO DE FIRMA
|
# 1. MOTOR HÍBRIDO ESTABLE (Sin filtros destructivos)
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
def analizar_calidad(box):
|
def analizar_calidad(box):
|
||||||
x1, y1, x2, y2 = box
|
x1, y1, x2, y2 = box
|
||||||
w, h = x2 - x1, y2 - y1
|
w, h = x2 - x1, y2 - y1
|
||||||
if w <= 0 or h <= 0:
|
if w <= 0 or h <= 0: return False
|
||||||
return False
|
|
||||||
return (ASPECT_RATIO_MIN < (h / w) < ASPECT_RATIO_MAX) and ((w * h) > AREA_MIN_CALIDAD)
|
return (ASPECT_RATIO_MIN < (h / w) < ASPECT_RATIO_MAX) and ((w * h) > AREA_MIN_CALIDAD)
|
||||||
|
|
||||||
def preprocess_onnx(roi):
|
def preprocess_onnx(roi):
|
||||||
@ -91,15 +75,14 @@ def preprocess_onnx(roi):
|
|||||||
img = (img - MEAN) / STD
|
img = (img - MEAN) / STD
|
||||||
return img
|
return img
|
||||||
|
|
||||||
def extraer_color_zonas(roi):
|
def extraer_color_zonas(img):
|
||||||
h_roi = roi.shape[0]
|
h_roi = img.shape[0]
|
||||||
t1 = int(h_roi * 0.15)
|
t1 = int(h_roi * 0.15)
|
||||||
t2 = int(h_roi * 0.55)
|
t2 = int(h_roi * 0.55)
|
||||||
zonas = [roi[:t1, :], roi[t1:t2, :], roi[t2:, :]]
|
zonas = [img[:t1, :], img[t1:t2, :], img[t2:, :]]
|
||||||
|
|
||||||
def hist_zona(z):
|
def hist_zona(z):
|
||||||
if z.size == 0:
|
if z.size == 0: return np.zeros(16 * 8)
|
||||||
return np.zeros(16 * 8)
|
|
||||||
hsv = cv2.cvtColor(z, cv2.COLOR_BGR2HSV)
|
hsv = cv2.cvtColor(z, cv2.COLOR_BGR2HSV)
|
||||||
hist = cv2.calcHist([hsv], [0, 1], None, [16, 8], [0, 180, 0, 256])
|
hist = cv2.calcHist([hsv], [0, 1], None, [16, 8], [0, 180, 0, 256])
|
||||||
cv2.normalize(hist, hist)
|
cv2.normalize(hist, hist)
|
||||||
@ -111,109 +94,85 @@ def extraer_firma_hibrida(frame, box):
|
|||||||
try:
|
try:
|
||||||
x1, y1, x2, y2 = map(int, box)
|
x1, y1, x2, y2 = map(int, box)
|
||||||
fh, fw = frame.shape[:2]
|
fh, fw = frame.shape[:2]
|
||||||
x1_c = max(0, x1); y1_c = max(0, y1)
|
x1_c, y1_c = max(0, x1), max(0, y1)
|
||||||
x2_c = min(fw, x2); y2_c = min(fh, y2)
|
x2_c, y2_c = min(fw, x2), min(fh, y2)
|
||||||
|
|
||||||
roi = frame[y1_c:y2_c, x1_c:x2_c]
|
roi = frame[y1_c:y2_c, x1_c:x2_c]
|
||||||
|
|
||||||
if roi.size == 0 or roi.shape[0] < 20 or roi.shape[1] < 10:
|
if roi.size == 0 or roi.shape[0] < 20 or roi.shape[1] < 10: return None
|
||||||
return None
|
|
||||||
|
|
||||||
calidad_area = (x2_c - x1_c) * (y2_c - y1_c)
|
calidad_area = (x2_c - x1_c) * (y2_c - y1_c)
|
||||||
|
|
||||||
# ⚡ ROI CRUDA: Pasamos la imagen tal cual, sin alterar su contraste artificialmente
|
|
||||||
blob = preprocess_onnx(roi)
|
blob = preprocess_onnx(roi)
|
||||||
blob_16 = np.zeros((16, 3, 256, 128), dtype=np.float32)
|
blob_16 = np.zeros((16, 3, 256, 128), dtype=np.float32)
|
||||||
blob_16[0] = blob[0]
|
blob_16[0] = blob[0]
|
||||||
deep_feat = ort_session.run(None, {input_name: blob_16})[0][0].flatten()
|
deep_feat = ort_session.run(None, {input_name: blob_16})[0][0].flatten()
|
||||||
norma = np.linalg.norm(deep_feat)
|
norma = np.linalg.norm(deep_feat)
|
||||||
if norma > 0:
|
if norma > 0: deep_feat = deep_feat / norma
|
||||||
deep_feat = deep_feat / norma
|
|
||||||
|
|
||||||
color_feat = extraer_color_zonas(roi)
|
color_feat = extraer_color_zonas(roi)
|
||||||
|
|
||||||
return {'deep': deep_feat, 'color': color_feat, 'calidad': calidad_area}
|
return {'deep': deep_feat, 'color': color_feat, 'calidad': calidad_area}
|
||||||
|
except Exception as e:
|
||||||
except Exception:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def similitud_hibrida(f1, f2, cross_cam=False):
|
def similitud_hibrida(f1, f2):
|
||||||
if f1 is None or f2 is None:
|
if f1 is None or f2 is None: return 0.0
|
||||||
return 0.0
|
|
||||||
|
|
||||||
sim_deep = max(0.0, 1.0 - cosine(f1['deep'], f2['deep']))
|
sim_deep = max(0.0, 1.0 - cosine(f1['deep'], f2['deep']))
|
||||||
|
|
||||||
if f1['color'].shape == f2['color'].shape and f1['color'].size > 1:
|
if f1['color'].shape == f2['color'].shape and f1['color'].size > 1:
|
||||||
L = len(f1['color']) // 3
|
L = len(f1['color']) // 3
|
||||||
sim_head = max(0.0, float(cv2.compareHist(
|
sim_head = max(0.0, float(cv2.compareHist(f1['color'][:L].astype(np.float32), f2['color'][:L].astype(np.float32), cv2.HISTCMP_CORREL)))
|
||||||
f1['color'][:L].astype(np.float32),
|
sim_torso = max(0.0, float(cv2.compareHist(f1['color'][L:2*L].astype(np.float32), f2['color'][L:2*L].astype(np.float32), cv2.HISTCMP_CORREL)))
|
||||||
f2['color'][:L].astype(np.float32), cv2.HISTCMP_CORREL)))
|
sim_legs = max(0.0, float(cv2.compareHist(f1['color'][2*L:].astype(np.float32), f2['color'][2*L:].astype(np.float32), cv2.HISTCMP_CORREL)))
|
||||||
sim_torso = max(0.0, float(cv2.compareHist(
|
|
||||||
f1['color'][L:2*L].astype(np.float32),
|
|
||||||
f2['color'][L:2*L].astype(np.float32), cv2.HISTCMP_CORREL)))
|
|
||||||
sim_legs = max(0.0, float(cv2.compareHist(
|
|
||||||
f1['color'][2*L:].astype(np.float32),
|
|
||||||
f2['color'][2*L:].astype(np.float32), cv2.HISTCMP_CORREL)))
|
|
||||||
sim_color = (0.10 * sim_head) + (0.60 * sim_torso) + (0.30 * sim_legs)
|
sim_color = (0.10 * sim_head) + (0.60 * sim_torso) + (0.30 * sim_legs)
|
||||||
else:
|
else: sim_color = 0.0
|
||||||
sim_color = 0.0
|
|
||||||
|
|
||||||
if cross_cam:
|
return (sim_deep * 0.90) + (sim_color * 0.10)
|
||||||
return (sim_deep * 0.75) + (sim_color * 0.25)
|
|
||||||
else:
|
|
||||||
return (sim_deep * 0.85) + (sim_color * 0.15)
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# 2. KALMAN TRACKER
|
# 2. KALMAN TRACKER
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
class KalmanTrack:
|
class KalmanTrack:
|
||||||
_count = 0
|
_count = 0
|
||||||
|
|
||||||
def __init__(self, box, now):
|
def __init__(self, box, now):
|
||||||
self.kf = cv2.KalmanFilter(7, 4)
|
self.kf = cv2.KalmanFilter(7, 4)
|
||||||
self.kf.measurementMatrix = np.array([
|
self.kf.measurementMatrix = np.array([
|
||||||
[1,0,0,0,0,0,0], [0,1,0,0,0,0,0],
|
[1,0,0,0,0,0,0], [0,1,0,0,0,0,0], [0,0,1,0,0,0,0], [0,0,0,1,0,0,0]], np.float32)
|
||||||
[0,0,1,0,0,0,0], [0,0,0,1,0,0,0]], np.float32)
|
|
||||||
self.kf.transitionMatrix = np.eye(7, dtype=np.float32)
|
self.kf.transitionMatrix = np.eye(7, dtype=np.float32)
|
||||||
self.kf.transitionMatrix[0,4] = 1
|
self.kf.transitionMatrix[0,4] = 1; self.kf.transitionMatrix[1,5] = 1; self.kf.transitionMatrix[2,6] = 1
|
||||||
self.kf.transitionMatrix[1,5] = 1
|
|
||||||
self.kf.transitionMatrix[2,6] = 1
|
|
||||||
self.kf.processNoiseCov *= 0.03
|
self.kf.processNoiseCov *= 0.03
|
||||||
self.kf.statePost = np.zeros((7, 1), np.float32)
|
self.kf.statePost = np.zeros((7, 1), np.float32)
|
||||||
self.kf.statePost[:4] = self._convert_bbox_to_z(box)
|
self.kf.statePost[:4] = self._convert_bbox_to_z(box)
|
||||||
|
|
||||||
self.local_id = KalmanTrack._count
|
self.local_id = KalmanTrack._count
|
||||||
KalmanTrack._count += 1
|
KalmanTrack._count += 1
|
||||||
|
|
||||||
self.gid = None
|
self.gid = None
|
||||||
self.origen_global = False
|
self.origen_global = False
|
||||||
self.aprendiendo = False
|
self.aprendiendo = False
|
||||||
self.box = list(box)
|
self.box = list(box)
|
||||||
|
|
||||||
self.ts_creacion = now
|
self.ts_creacion = now
|
||||||
self.ts_ultima_deteccion = now
|
self.ts_ultima_deteccion = now
|
||||||
|
|
||||||
self.time_since_update = 0
|
self.time_since_update = 0
|
||||||
self.en_grupo = False
|
self.en_grupo = False
|
||||||
self.frames_buena_calidad = 0
|
self.frames_buena_calidad = 0
|
||||||
self.listo_para_id = False
|
self.listo_para_id = False
|
||||||
self.area_referencia = 0.0
|
self.area_referencia = 0.0
|
||||||
self.ultimo_aprendizaje = 0.0
|
|
||||||
|
|
||||||
def _convert_bbox_to_z(self, bbox):
|
def _convert_bbox_to_z(self, bbox):
|
||||||
w = bbox[2] - bbox[0]; h = bbox[3] - bbox[1]
|
w = bbox[2] - bbox[0]; h = bbox[3] - bbox[1]; x = bbox[0] + w/2.; y = bbox[1] + h/2.
|
||||||
x = bbox[0] + w / 2.; y = bbox[1] + h / 2.
|
|
||||||
return np.array([[x],[y],[w*h],[w/float(h+1e-6)]]).astype(np.float32)
|
return np.array([[x],[y],[w*h],[w/float(h+1e-6)]]).astype(np.float32)
|
||||||
|
|
||||||
def _convert_x_to_bbox(self, x):
|
def _convert_x_to_bbox(self, x):
|
||||||
cx = float(x[0].item()); cy = float(x[1].item())
|
cx, cy, s, r = float(x[0].item()), float(x[1].item()), float(x[2].item()), float(x[3].item())
|
||||||
s = float(x[2].item()); r = float(x[3].item())
|
|
||||||
w = np.sqrt(s * r); h = s / (w + 1e-6)
|
w = np.sqrt(s * r); h = s / (w + 1e-6)
|
||||||
return [cx-w/2., cy-h/2., cx+w/2., cy+h/2.]
|
return [cx-w/2., cy-h/2., cx+w/2., cy+h/2.]
|
||||||
|
|
||||||
def predict(self, turno_activo=True):
|
def predict(self, turno_activo=True):
|
||||||
if (self.kf.statePost[6] + self.kf.statePost[2]) <= 0:
|
if (self.kf.statePost[6] + self.kf.statePost[2]) <= 0: self.kf.statePost[6] *= 0.0
|
||||||
self.kf.statePost[6] *= 0.0
|
|
||||||
self.kf.predict()
|
self.kf.predict()
|
||||||
if turno_activo:
|
if turno_activo: self.time_since_update += 1
|
||||||
self.time_since_update += 1
|
|
||||||
self.aprendiendo = False
|
self.aprendiendo = False
|
||||||
self.box = self._convert_x_to_bbox(self.kf.statePre)
|
self.box = self._convert_x_to_bbox(self.kf.statePre)
|
||||||
return self.box
|
return self.box
|
||||||
@ -233,7 +192,7 @@ class KalmanTrack:
|
|||||||
self.frames_buena_calidad = max(0, self.frames_buena_calidad - 1)
|
self.frames_buena_calidad = max(0, self.frames_buena_calidad - 1)
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# 3. MEMORIA GLOBAL
|
# 3. MEMORIA GLOBAL (Top-3 Ponderado y Anti-Robo)
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
class GlobalMemory:
|
class GlobalMemory:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -245,84 +204,61 @@ class GlobalMemory:
|
|||||||
ultima_cam = str(data['last_cam'])
|
ultima_cam = str(data['last_cam'])
|
||||||
cam_destino = str(cam_destino)
|
cam_destino = str(cam_destino)
|
||||||
dt = now - data['ts']
|
dt = now - data['ts']
|
||||||
if ultima_cam == cam_destino:
|
|
||||||
return True
|
if ultima_cam == cam_destino: return True
|
||||||
vecinos = VECINOS.get(ultima_cam, [])
|
vecinos = VECINOS.get(ultima_cam, [])
|
||||||
if cam_destino in vecinos:
|
if cam_destino in vecinos: return dt >= -0.5
|
||||||
return dt >= -0.5
|
|
||||||
return dt >= 4.0
|
return dt >= 4.0
|
||||||
|
|
||||||
def _sim_robusta(self, firma_nueva, firmas_guardadas, cross_cam=False):
|
def _sim_robusta(self, firma_nueva, firmas_guardadas):
|
||||||
if not firmas_guardadas:
|
if not firmas_guardadas: return 0.0
|
||||||
return 0.0
|
|
||||||
sims = sorted(
|
sims = sorted(
|
||||||
[similitud_hibrida(firma_nueva, f, cross_cam=cross_cam)
|
[similitud_hibrida(firma_nueva, f) for f in firmas_guardadas],
|
||||||
for f in firmas_guardadas],
|
|
||||||
reverse=True
|
reverse=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(sims) == 1:
|
if len(sims) == 1:
|
||||||
return sims[0]
|
return sims[0]
|
||||||
elif len(sims) <= 3:
|
elif len(sims) <= 4:
|
||||||
return (sims[0] * 0.60) + (sims[1] * 0.40)
|
return (sims[0] * 0.6) + (sims[1] * 0.4)
|
||||||
else:
|
else:
|
||||||
return (sims[0] * 0.50) + (sims[1] * 0.30) + (sims[2] * 0.20)
|
return (sims[0] * 0.50) + (sims[1] * 0.30) + (sims[2] * 0.20)
|
||||||
|
|
||||||
def identificar_candidato(self, firma_hibrida, cam_id, now, active_gids):
|
def identificar_candidato(self, firma_hibrida, cam_id, now, active_gids):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
candidatos = []
|
best_gid, best_score = None, -1.0
|
||||||
vecinos = VECINOS.get(str(cam_id), [])
|
vecinos = VECINOS.get(str(cam_id), [])
|
||||||
|
|
||||||
for gid, data in self.db.items():
|
for gid, data in self.db.items():
|
||||||
if gid in active_gids:
|
if gid in active_gids: continue
|
||||||
continue
|
|
||||||
dt = now - data['ts']
|
dt = now - data['ts']
|
||||||
if dt > TIEMPO_MAX_AUSENCIA:
|
if dt > TIEMPO_MAX_AUSENCIA or not self._es_transito_posible(data, cam_id, now): continue
|
||||||
continue
|
if not data['firmas']: continue
|
||||||
if not self._es_transito_posible(data, cam_id, now):
|
|
||||||
continue
|
|
||||||
if not data['firmas']:
|
|
||||||
continue
|
|
||||||
|
|
||||||
cross_cam = str(data['last_cam']) != str(cam_id)
|
sim = self._sim_robusta(firma_hibrida, data['firmas'])
|
||||||
sim = self._sim_robusta(firma_hibrida, data['firmas'], cross_cam=cross_cam)
|
|
||||||
|
|
||||||
misma_cam = str(data['last_cam']) == str(cam_id)
|
misma_cam = str(data['last_cam']) == str(cam_id)
|
||||||
es_vecino = str(data['last_cam']) in vecinos
|
es_vecino = str(data['last_cam']) in vecinos
|
||||||
|
|
||||||
if misma_cam:
|
if misma_cam: umbral = UMBRAL_REID_MISMA_CAM
|
||||||
umbral = UMBRAL_REID_MISMA_CAM
|
elif es_vecino: umbral = UMBRAL_REID_VECINO
|
||||||
elif es_vecino:
|
else: umbral = UMBRAL_REID_NO_VECINO
|
||||||
umbral = UMBRAL_REID_VECINO
|
|
||||||
else:
|
|
||||||
umbral = UMBRAL_REID_NO_VECINO
|
|
||||||
|
|
||||||
if sim > umbral:
|
if sim > best_score and sim > umbral:
|
||||||
candidatos.append((sim, gid))
|
best_score = sim
|
||||||
|
best_gid = gid
|
||||||
if not candidatos:
|
|
||||||
nid = self.next_gid
|
|
||||||
self.next_gid += 1
|
|
||||||
self._actualizar_sin_lock(nid, firma_hibrida, cam_id, now)
|
|
||||||
return nid, False
|
|
||||||
|
|
||||||
candidatos.sort(reverse=True)
|
|
||||||
sim_1, best_gid = candidatos[0]
|
|
||||||
|
|
||||||
if len(candidatos) >= 2:
|
|
||||||
sim_2 = candidatos[1][0]
|
|
||||||
margen = sim_1 - sim_2
|
|
||||||
if margen < MARGEN_MINIMO_REID:
|
|
||||||
nid = self.next_gid
|
|
||||||
self.next_gid += 1
|
|
||||||
self._actualizar_sin_lock(nid, firma_hibrida, cam_id, now)
|
|
||||||
return nid, False
|
|
||||||
|
|
||||||
|
if best_gid is not None:
|
||||||
self._actualizar_sin_lock(best_gid, firma_hibrida, cam_id, now)
|
self._actualizar_sin_lock(best_gid, firma_hibrida, cam_id, now)
|
||||||
return best_gid, True
|
return best_gid, True
|
||||||
|
else:
|
||||||
|
nid = self.next_gid; self.next_gid += 1
|
||||||
|
self._actualizar_sin_lock(nid, firma_hibrida, cam_id, now)
|
||||||
|
return nid, False
|
||||||
|
|
||||||
def _actualizar_sin_lock(self, gid, firma_dict, cam_id, now):
|
def _actualizar_sin_lock(self, gid, firma_dict, cam_id, now):
|
||||||
if gid not in self.db:
|
if gid not in self.db: self.db[gid] = {'firmas': [], 'last_cam': cam_id, 'ts': now}
|
||||||
self.db[gid] = {'firmas': [], 'last_cam': cam_id, 'ts': now}
|
|
||||||
|
|
||||||
if firma_dict is not None:
|
if firma_dict is not None:
|
||||||
firmas_list = self.db[gid]['firmas']
|
firmas_list = self.db[gid]['firmas']
|
||||||
@ -338,15 +274,18 @@ class GlobalMemory:
|
|||||||
if len(firmas_list) >= MAX_FIRMAS_MEMORIA:
|
if len(firmas_list) >= MAX_FIRMAS_MEMORIA:
|
||||||
max_sim_interna = -1.0
|
max_sim_interna = -1.0
|
||||||
idx_redundante = 1
|
idx_redundante = 1
|
||||||
|
|
||||||
for i in range(1, len(firmas_list)):
|
for i in range(1, len(firmas_list)):
|
||||||
sims_con_otras = [
|
sims_con_otras = [
|
||||||
similitud_hibrida(firmas_list[i], firmas_list[j])
|
similitud_hibrida(firmas_list[i], firmas_list[j])
|
||||||
for j in range(1, len(firmas_list)) if j != i
|
for j in range(1, len(firmas_list)) if j != i
|
||||||
]
|
]
|
||||||
sim_promedio = float(np.mean(sims_con_otras)) if sims_con_otras else 0.0
|
sim_promedio = np.mean(sims_con_otras) if sims_con_otras else 0.0
|
||||||
|
|
||||||
if sim_promedio > max_sim_interna:
|
if sim_promedio > max_sim_interna:
|
||||||
max_sim_interna = sim_promedio
|
max_sim_interna = sim_promedio
|
||||||
idx_redundante = i
|
idx_redundante = i
|
||||||
|
|
||||||
firmas_list[idx_redundante] = firma_dict
|
firmas_list[idx_redundante] = firma_dict
|
||||||
else:
|
else:
|
||||||
firmas_list.append(firma_dict)
|
firmas_list.append(firma_dict)
|
||||||
@ -355,15 +294,13 @@ class GlobalMemory:
|
|||||||
self.db[gid]['ts'] = now
|
self.db[gid]['ts'] = now
|
||||||
|
|
||||||
def actualizar(self, gid, firma, cam_id, now):
|
def actualizar(self, gid, firma, cam_id, now):
|
||||||
with self.lock:
|
with self.lock: self._actualizar_sin_lock(gid, firma, cam_id, now)
|
||||||
self._actualizar_sin_lock(gid, firma, cam_id, now)
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# 4. GESTOR LOCAL
|
# 4. GESTOR LOCAL
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
def iou_overlap(boxA, boxB):
|
def iou_overlap(boxA, boxB):
|
||||||
xA = max(boxA[0], boxB[0]); yA = max(boxA[1], boxB[1])
|
xA, yA, xB, yB = max(boxA[0], boxB[0]), max(boxA[1], boxB[1]), min(boxA[2], boxB[2]), min(boxA[3], boxB[3])
|
||||||
xB = min(boxA[2], boxB[2]); yB = min(boxA[3], boxB[3])
|
|
||||||
inter = max(0, xB-xA) * max(0, yB-yA)
|
inter = max(0, xB-xA) * max(0, yB-yA)
|
||||||
areaA = (boxA[2]-boxA[0]) * (boxA[3]-boxA[1])
|
areaA = (boxA[2]-boxA[0]) * (boxA[3]-boxA[1])
|
||||||
areaB = (boxB[2]-boxB[0]) * (boxB[3]-boxB[1])
|
areaB = (boxB[2]-boxB[0]) * (boxB[3]-boxB[1])
|
||||||
@ -371,135 +308,74 @@ def iou_overlap(boxA, boxB):
|
|||||||
|
|
||||||
class CamManager:
|
class CamManager:
|
||||||
def __init__(self, cam_id, global_mem):
|
def __init__(self, cam_id, global_mem):
|
||||||
self.cam_id = cam_id
|
self.cam_id, self.global_mem, self.trackers = cam_id, global_mem, []
|
||||||
self.global_mem = global_mem
|
|
||||||
self.trackers = []
|
|
||||||
|
|
||||||
def update(self, boxes, frame, now, turno_activo):
|
def update(self, boxes, frame, now, turno_activo):
|
||||||
for trk in self.trackers:
|
for trk in self.trackers: trk.predict(turno_activo=turno_activo)
|
||||||
trk.predict(turno_activo=turno_activo)
|
if not turno_activo: return self.trackers
|
||||||
if not turno_activo:
|
|
||||||
return self.trackers
|
|
||||||
|
|
||||||
matched, unmatched_dets, _ = self._asignar(boxes, now)
|
matched, unmatched_dets, unmatched_trks = self._asignar(boxes, now)
|
||||||
fh, fw = frame.shape[:2]
|
|
||||||
|
|
||||||
for t_idx, d_idx in matched:
|
for t_idx, d_idx in matched:
|
||||||
trk = self.trackers[t_idx]
|
trk = self.trackers[t_idx]; box = boxes[d_idx]
|
||||||
box = boxes[d_idx]
|
|
||||||
|
|
||||||
en_grupo = any(
|
en_grupo = any(other is not trk and iou_overlap(box, other.box) > 0.10 for other in self.trackers)
|
||||||
other is not trk and iou_overlap(box, other.box) > 0.10
|
|
||||||
for other in self.trackers
|
|
||||||
)
|
|
||||||
trk.update(box, en_grupo, now)
|
trk.update(box, en_grupo, now)
|
||||||
|
|
||||||
active_gids_local = {t.gid for t in self.trackers if t.gid is not None}
|
active_gids = {t.gid for t in self.trackers if t.gid is not None}
|
||||||
area_actual = (box[2] - box[0]) * (box[3] - box[1])
|
area_actual = (box[2] - box[0]) * (box[3] - box[1])
|
||||||
|
|
||||||
if trk.gid is None and trk.listo_para_id:
|
if trk.gid is None and trk.listo_para_id:
|
||||||
firma = extraer_firma_hibrida(frame, box)
|
firma = extraer_firma_hibrida(frame, box)
|
||||||
if firma is not None:
|
if firma is not None:
|
||||||
gid, es_reid = self.global_mem.identificar_candidato(
|
gid, es_reid = self.global_mem.identificar_candidato(firma, self.cam_id, now, active_gids)
|
||||||
firma, self.cam_id, now, active_gids_local
|
trk.gid, trk.origen_global, trk.area_referencia = gid, es_reid, area_actual
|
||||||
)
|
|
||||||
trk.gid = gid
|
|
||||||
trk.origen_global = es_reid
|
|
||||||
trk.area_referencia = area_actual
|
|
||||||
|
|
||||||
elif trk.gid is None and not trk.listo_para_id:
|
|
||||||
firma_rapida = extraer_firma_hibrida(frame, box)
|
|
||||||
if firma_rapida is not None:
|
|
||||||
cx_nuevo = (box[0] + box[2]) / 2
|
|
||||||
cy_nuevo = (box[1] + box[3]) / 2
|
|
||||||
|
|
||||||
with self.global_mem.lock:
|
|
||||||
for gid_cand, data_cand in self.global_mem.db.items():
|
|
||||||
if gid_cand in active_gids_local:
|
|
||||||
continue
|
|
||||||
if str(data_cand.get('last_cam', '')) != str(self.cam_id):
|
|
||||||
continue
|
|
||||||
if (now - data_cand['ts']) > TIEMPO_REENTRADA:
|
|
||||||
continue
|
|
||||||
if not data_cand['firmas']:
|
|
||||||
continue
|
|
||||||
|
|
||||||
ultima_box = data_cand.get('last_box')
|
|
||||||
if ultima_box is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
cx_ult = (ultima_box[0] + ultima_box[2]) / 2
|
|
||||||
cy_ult = (ultima_box[1] + ultima_box[3]) / 2
|
|
||||||
distancia = np.sqrt((cx_nuevo-cx_ult)**2 + (cy_nuevo-cy_ult)**2)
|
|
||||||
|
|
||||||
if distancia < RADIO_REENTRADA:
|
|
||||||
sim = similitud_hibrida(firma_rapida, data_cand['firmas'][0])
|
|
||||||
if sim > UMBRAL_REID_MISMA_CAM:
|
|
||||||
trk.gid = gid_cand
|
|
||||||
trk.origen_global = True
|
|
||||||
trk.area_referencia = area_actual
|
|
||||||
trk.listo_para_id = True
|
|
||||||
self.global_mem._actualizar_sin_lock(
|
|
||||||
gid_cand, firma_rapida, self.cam_id, now
|
|
||||||
)
|
|
||||||
break
|
|
||||||
|
|
||||||
elif trk.gid is not None and not trk.en_grupo:
|
elif trk.gid is not None and not trk.en_grupo:
|
||||||
tiempo_ultima_firma = trk.ultimo_aprendizaje
|
tiempo_ultima_firma = getattr(trk, 'ultimo_aprendizaje', 0)
|
||||||
|
|
||||||
if (now - tiempo_ultima_firma) > 1.5 and analizar_calidad(box):
|
if (now - tiempo_ultima_firma) > 1.5 and analizar_calidad(box):
|
||||||
x1b, y1b, x2b, y2b = map(int, box)
|
fh, fw = frame.shape[:2]
|
||||||
en_borde = (x1b < 15 or y1b < 15 or x2b > fw-15 or y2b > fh-15)
|
x1, y1, x2, y2 = map(int, box)
|
||||||
|
en_borde = (x1 < 15 or y1 < 15 or x2 > fw - 15 or y2 > fh - 15)
|
||||||
|
|
||||||
if not en_borde:
|
if not en_borde:
|
||||||
firma_nueva = extraer_firma_hibrida(frame, box)
|
firma_nueva = extraer_firma_hibrida(frame, box)
|
||||||
if firma_nueva is not None:
|
if firma_nueva is not None:
|
||||||
with self.global_mem.lock:
|
with self.global_mem.lock:
|
||||||
if (trk.gid in self.global_mem.db
|
if trk.gid in self.global_mem.db and self.global_mem.db[trk.gid]['firmas']:
|
||||||
and self.global_mem.db[trk.gid]['firmas']):
|
|
||||||
|
|
||||||
firma_ancla = self.global_mem.db[trk.gid]['firmas'][0]
|
firma_ancla = self.global_mem.db[trk.gid]['firmas'][0]
|
||||||
sim_coherencia = similitud_hibrida(firma_nueva, firma_ancla)
|
sim_coherencia = similitud_hibrida(firma_nueva, firma_ancla)
|
||||||
|
|
||||||
if sim_coherencia > 0.55:
|
if sim_coherencia > 0.55:
|
||||||
es_coherente = True
|
es_coherente = True
|
||||||
|
|
||||||
for otro_gid, otro_data in self.global_mem.db.items():
|
for otro_gid, otro_data in self.global_mem.db.items():
|
||||||
if otro_gid == trk.gid or not otro_data['firmas']:
|
if otro_gid == trk.gid or not otro_data['firmas']: continue
|
||||||
continue
|
sim_intruso = similitud_hibrida(firma_nueva, otro_data['firmas'][0])
|
||||||
sim_intruso = similitud_hibrida(
|
if sim_intruso > sim_coherencia:
|
||||||
firma_nueva, otro_data['firmas'][0]
|
|
||||||
)
|
|
||||||
if sim_intruso > 0.55:
|
|
||||||
es_coherente = False
|
es_coherente = False
|
||||||
break
|
break
|
||||||
|
|
||||||
if es_coherente:
|
if es_coherente:
|
||||||
self.global_mem._actualizar_sin_lock(
|
self.global_mem._actualizar_sin_lock(trk.gid, firma_nueva, self.cam_id, now)
|
||||||
trk.gid, firma_nueva, self.cam_id, now
|
|
||||||
)
|
|
||||||
trk.ultimo_aprendizaje = now
|
trk.ultimo_aprendizaje = now
|
||||||
trk.aprendiendo = True
|
trk.aprendiendo = True
|
||||||
|
|
||||||
for trk in self.trackers:
|
for d_idx in unmatched_dets: self.trackers.append(KalmanTrack(boxes[d_idx], now))
|
||||||
if trk.time_since_update == 0 and trk.gid is not None:
|
|
||||||
with self.global_mem.lock:
|
|
||||||
if trk.gid in self.global_mem.db:
|
|
||||||
self.global_mem.db[trk.gid]['last_box'] = list(trk.box)
|
|
||||||
|
|
||||||
for d_idx in unmatched_dets:
|
|
||||||
self.trackers.append(KalmanTrack(boxes[d_idx], now))
|
|
||||||
|
|
||||||
vivos = []
|
vivos = []
|
||||||
|
fh, fw = frame.shape[:2]
|
||||||
for t in self.trackers:
|
for t in self.trackers:
|
||||||
x1, y1, x2, y2 = t.box
|
x1, y1, x2, y2 = t.box
|
||||||
toca_borde = (x1 < 5 or y1 < 5 or x2 > fw - 5 or y2 > fh - 5)
|
toca_borde = (x1 < 5 or y1 < 5 or x2 > fw - 5 or y2 > fh - 5)
|
||||||
tiempo_oculto = now - t.ts_ultima_deteccion
|
tiempo_oculto = now - t.ts_ultima_deteccion
|
||||||
|
|
||||||
if toca_borde:
|
if toca_borde and tiempo_oculto > 1.0:
|
||||||
limite = TIEMPO_PACIENCIA_BORDE_CON_ID if t.gid else TIEMPO_PACIENCIA_BORDE_SIN_ID
|
continue
|
||||||
else:
|
|
||||||
limite = TIEMPO_PACIENCIA_INTERIOR if t.gid else TIEMPO_PACIENCIA_INTERIOR_NUEVO
|
|
||||||
|
|
||||||
if tiempo_oculto < limite:
|
limite_vida = 5.0 if t.gid else 1.0
|
||||||
|
if tiempo_oculto < limite_vida:
|
||||||
vivos.append(t)
|
vivos.append(t)
|
||||||
|
|
||||||
self.trackers = vivos
|
self.trackers = vivos
|
||||||
@ -517,28 +393,28 @@ class CamManager:
|
|||||||
for t, trk in enumerate(self.trackers):
|
for t, trk in enumerate(self.trackers):
|
||||||
for d, det in enumerate(boxes):
|
for d, det in enumerate(boxes):
|
||||||
iou = iou_overlap(trk.box, det)
|
iou = iou_overlap(trk.box, det)
|
||||||
cx_t = (trk.box[0]+trk.box[2]) / 2
|
cx_t, cy_t = (trk.box[0]+trk.box[2])/2, (trk.box[1]+trk.box[3])/2
|
||||||
cy_t = (trk.box[1]+trk.box[3]) / 2
|
cx_d, cy_d = (det[0]+det[2])/2, (det[1]+det[3])/2
|
||||||
cx_d = (det[0]+det[2]) / 2
|
|
||||||
cy_d = (det[1]+det[3]) / 2
|
|
||||||
dist_norm = np.sqrt((cx_t-cx_d)**2 + (cy_t-cy_d)**2) / 550.0
|
dist_norm = np.sqrt((cx_t-cx_d)**2 + (cy_t-cy_d)**2) / 550.0
|
||||||
|
|
||||||
area_trk = (trk.box[2] - trk.box[0]) * (trk.box[3] - trk.box[1])
|
area_trk = (trk.box[2] - trk.box[0]) * (trk.box[3] - trk.box[1])
|
||||||
area_det = (det[2] - det[0]) * (det[3] - det[1])
|
area_det = (det[2] - det[0]) * (det[3] - det[1])
|
||||||
ratio_area = max(area_trk, area_det) / (min(area_trk, area_det) + 1e-6)
|
ratio_area = max(area_trk, area_det) / (min(area_trk, area_det) + 1e-6)
|
||||||
castigo_tam = (ratio_area - 1.0) * 0.4
|
|
||||||
|
castigo_tamano = (ratio_area - 1.0) * 0.4
|
||||||
|
|
||||||
tiempo_oculto = now - trk.ts_ultima_deteccion
|
tiempo_oculto = now - trk.ts_ultima_deteccion
|
||||||
|
|
||||||
|
TIEMPO_TURNO_ROTATIVO = len(SECUENCIA) * 0.035
|
||||||
|
|
||||||
if tiempo_oculto > (TIEMPO_TURNO_ROTATIVO * 2) and iou < 0.10:
|
if tiempo_oculto > (TIEMPO_TURNO_ROTATIVO * 2) and iou < 0.10:
|
||||||
fantasma_penalty = 5.0
|
fantasma_penalty = 5.0
|
||||||
else:
|
else:
|
||||||
fantasma_penalty = 0.0
|
fantasma_penalty = 0.0
|
||||||
|
|
||||||
if iou >= 0.05 or dist_norm < 0.50:
|
if iou >= 0.05 or dist_norm < 0.50:
|
||||||
cost_mat[t, d] = ((1.0 - iou)
|
cost_mat[t, d] = (1.0 - iou) + (dist_norm * 2.0) + fantasma_penalty + castigo_tamano
|
||||||
+ (dist_norm * 2.0)
|
|
||||||
+ fantasma_penalty
|
|
||||||
+ castigo_tam)
|
|
||||||
else:
|
else:
|
||||||
cost_mat[t, d] = 100.0
|
cost_mat[t, d] = 100.0
|
||||||
|
|
||||||
@ -547,29 +423,23 @@ class CamManager:
|
|||||||
|
|
||||||
for r, c in zip(row_ind, col_ind):
|
for r, c in zip(row_ind, col_ind):
|
||||||
if cost_mat[r, c] > 4.0:
|
if cost_mat[r, c] > 4.0:
|
||||||
unmatched_trks.append(r)
|
unmatched_trks.append(r); unmatched_dets.append(c)
|
||||||
unmatched_dets.append(c)
|
else: matched.append((r, c))
|
||||||
else:
|
|
||||||
matched.append((r, c))
|
|
||||||
|
|
||||||
matched_t = {m[0] for m in matched}
|
|
||||||
matched_d = {m[1] for m in matched}
|
|
||||||
for t in range(n_trk):
|
for t in range(n_trk):
|
||||||
if t not in matched_t: unmatched_trks.append(t)
|
if t not in [m[0] for m in matched]: unmatched_trks.append(t)
|
||||||
for d in range(n_det):
|
for d in range(n_det):
|
||||||
if d not in matched_d: unmatched_dets.append(d)
|
if d not in [m[1] for m in matched]: unmatched_dets.append(d)
|
||||||
|
|
||||||
return matched, unmatched_dets, unmatched_trks
|
return matched, unmatched_dets, unmatched_trks
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# 5. STREAM DE CÁMARA
|
# 5. STREAM Y MAIN LOOP (Standalone)
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
class CamStream:
|
class CamStream:
|
||||||
def __init__(self, url):
|
def __init__(self, url):
|
||||||
self.url = url
|
self.url, self.cap = url, cv2.VideoCapture(url)
|
||||||
self.cap = cv2.VideoCapture(url)
|
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1); self.frame = None
|
||||||
self.frame = None
|
|
||||||
self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
|
||||||
threading.Thread(target=self._run, daemon=True).start()
|
threading.Thread(target=self._run, daemon=True).start()
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
@ -582,25 +452,15 @@ class CamStream:
|
|||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
self.cap.open(self.url)
|
self.cap.open(self.url)
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
|
||||||
# 6. DIBUJADO
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
|
||||||
def dibujar_track(frame_show, trk):
|
def dibujar_track(frame_show, trk):
|
||||||
try:
|
try: x1, y1, x2, y2 = map(int, trk.box)
|
||||||
x1, y1, x2, y2 = map(int, trk.box)
|
except Exception: return
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
|
|
||||||
if trk.gid is None:
|
if trk.gid is None: color, label = C_CANDIDATO, f"?{trk.local_id}"
|
||||||
color, label = C_CANDIDATO, f"?{trk.local_id}"
|
elif trk.en_grupo: color, label = C_GRUPO, f"ID:{trk.gid} [grp]"
|
||||||
elif trk.en_grupo:
|
elif trk.aprendiendo: color, label = C_APRENDIZAJE, f"ID:{trk.gid} [++]"
|
||||||
color, label = C_GRUPO, f"ID:{trk.gid} [grp]"
|
elif trk.origen_global: color, label = C_GLOBAL, f"ID:{trk.gid} [re-id]"
|
||||||
elif trk.aprendiendo:
|
else: color, label = C_LOCAL, f"ID:{trk.gid}"
|
||||||
color, label = C_APRENDIZAJE, f"ID:{trk.gid} [++]"
|
|
||||||
elif trk.origen_global:
|
|
||||||
color, label = C_GLOBAL, f"ID:{trk.gid} [re-id]"
|
|
||||||
else:
|
|
||||||
color, label = C_LOCAL, f"ID:{trk.gid}"
|
|
||||||
|
|
||||||
cv2.rectangle(frame_show, (x1, y1), (x2, y2), color, 2)
|
cv2.rectangle(frame_show, (x1, y1), (x2, y2), color, 2)
|
||||||
(tw, th), _ = cv2.getTextSize(label, FUENTE, 0.55, 1)
|
(tw, th), _ = cv2.getTextSize(label, FUENTE, 0.55, 1)
|
||||||
@ -609,15 +469,11 @@ def dibujar_track(frame_show, trk):
|
|||||||
|
|
||||||
if trk.gid is None:
|
if trk.gid is None:
|
||||||
pct = min(trk.frames_buena_calidad / FRAMES_CALIDAD, 1.0)
|
pct = min(trk.frames_buena_calidad / FRAMES_CALIDAD, 1.0)
|
||||||
bw = x2 - x1
|
bw = x2 - x1; cv2.rectangle(frame_show, (x1, y2+2), (x2, y2+7), (50,50,50), -1)
|
||||||
cv2.rectangle(frame_show, (x1, y2+2), (x2, y2+7), (50, 50, 50), -1)
|
|
||||||
cv2.rectangle(frame_show, (x1, y2+2), (x1+int(bw*pct), y2+7), (0,220,220), -1)
|
cv2.rectangle(frame_show, (x1, y2+2), (x1+int(bw*pct), y2+7), (0,220,220), -1)
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
|
||||||
# 7. MAIN LOOP (para pruebas standalone)
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
|
||||||
def main():
|
def main():
|
||||||
print("SmartSoft")
|
print("Iniciando Sistema V-PRO — Tracker Resiliente (Rollback Estable)")
|
||||||
model = YOLO("yolov8n.pt")
|
model = YOLO("yolov8n.pt")
|
||||||
global_mem = GlobalMemory()
|
global_mem = GlobalMemory()
|
||||||
managers = {str(c): CamManager(c, global_mem) for c in SECUENCIA}
|
managers = {str(c): CamManager(c, global_mem) for c in SECUENCIA}
|
||||||
@ -626,53 +482,27 @@ def main():
|
|||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
while True:
|
while True:
|
||||||
now, tiles = time.time(), []
|
now = time.time()
|
||||||
|
tiles = []
|
||||||
cam_ia = idx % len(cams)
|
cam_ia = idx % len(cams)
|
||||||
|
|
||||||
for i, cam_obj in enumerate(cams):
|
for i, cam_obj in enumerate(cams):
|
||||||
frame = cam_obj.frame
|
frame = cam_obj.frame; cid = str(SECUENCIA[i])
|
||||||
cid = str(SECUENCIA[i])
|
if frame is None: tiles.append(np.zeros((270, 480, 3), np.uint8)); continue
|
||||||
|
frame_show = cv2.resize(frame.copy(), (480, 270)); boxes = []; turno_activo = (i == cam_ia)
|
||||||
if frame is None:
|
|
||||||
tiles.append(np.zeros((270, 480, 3), np.uint8))
|
|
||||||
continue
|
|
||||||
|
|
||||||
frame_show = cv2.resize(frame.copy(), (480, 270))
|
|
||||||
boxes = []
|
|
||||||
turno_activo = (i == cam_ia)
|
|
||||||
|
|
||||||
if turno_activo:
|
if turno_activo:
|
||||||
res = model.predict(
|
res = model.predict(frame_show, conf=0.50, iou=0.40, classes=[0], verbose=False, imgsz=480)
|
||||||
frame_show, conf=0.40, iou=0.50,
|
if res[0].boxes: boxes = res[0].boxes.xyxy.cpu().numpy().tolist()
|
||||||
classes=[0], verbose=False, imgsz=480
|
|
||||||
)
|
|
||||||
if res[0].boxes:
|
|
||||||
boxes = res[0].boxes.xyxy.cpu().numpy().tolist()
|
|
||||||
|
|
||||||
tracks = managers[cid].update(boxes, frame_show, now, turno_activo)
|
tracks = managers[cid].update(boxes, frame_show, now, turno_activo)
|
||||||
|
|
||||||
for trk in tracks:
|
for trk in tracks:
|
||||||
if trk.time_since_update == 0:
|
if trk.time_since_update <= 1: dibujar_track(frame_show, trk)
|
||||||
dibujar_track(frame_show, trk)
|
if turno_activo: cv2.circle(frame_show, (460, 20), 6, (0, 0, 255), -1)
|
||||||
|
|
||||||
if turno_activo:
|
|
||||||
cv2.circle(frame_show, (460, 20), 6, (0, 0, 255), -1)
|
|
||||||
|
|
||||||
con_id = sum(1 for t in tracks if t.gid and t.time_since_update==0)
|
con_id = sum(1 for t in tracks if t.gid and t.time_since_update==0)
|
||||||
cv2.putText(frame_show, f"CAM {cid} [{con_id} ID]",
|
cv2.putText(frame_show, f"CAM {cid} [{con_id} ID]", (10, 28), FUENTE, 0.7, (255, 255, 255), 2)
|
||||||
(10, 28), FUENTE, 0.7, (255, 255, 255), 2)
|
|
||||||
tiles.append(frame_show)
|
tiles.append(frame_show)
|
||||||
|
|
||||||
if len(tiles) == 6:
|
if len(tiles) == 6: cv2.imshow("SmartSoft", np.vstack([np.hstack(tiles[0:3]), np.hstack(tiles[3:6])]))
|
||||||
cv2.imshow("SmartSoft", np.vstack([
|
|
||||||
np.hstack(tiles[0:3]),
|
|
||||||
np.hstack(tiles[3:6])
|
|
||||||
]))
|
|
||||||
|
|
||||||
idx += 1
|
idx += 1
|
||||||
if cv2.waitKey(1) == ord('q'):
|
if cv2.waitKey(1) == ord('q'): break
|
||||||
break
|
|
||||||
|
|
||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user