Spaces:
Running
Running
Commit
·
d033fd0
1
Parent(s):
5584a05
fix: Corregida inicialización de la cámara y manejo de errores
Browse files- streamlit_app.py +34 -221
streamlit_app.py
CHANGED
|
@@ -2057,13 +2057,12 @@ def main():
|
|
| 2057 |
with tab3:
|
| 2058 |
st.header("Real-time Recognition")
|
| 2059 |
|
| 2060 |
-
#
|
| 2061 |
if not st.session_state.face_database:
|
| 2062 |
st.warning("No faces registered. Please register at least one face first.")
|
| 2063 |
else:
|
| 2064 |
-
#
|
| 2065 |
-
with st.expander("
|
| 2066 |
-
# Configuración de umbral de similitud
|
| 2067 |
similarity_threshold = st.slider(
|
| 2068 |
"Similarity threshold (%)",
|
| 2069 |
min_value=35.0,
|
|
@@ -2071,7 +2070,7 @@ def main():
|
|
| 2071 |
value=45.0,
|
| 2072 |
step=5.0,
|
| 2073 |
key="realtime_threshold",
|
| 2074 |
-
help="
|
| 2075 |
)
|
| 2076 |
|
| 2077 |
confidence_threshold = st.slider(
|
|
@@ -2081,14 +2080,14 @@ def main():
|
|
| 2081 |
value=0.5,
|
| 2082 |
step=0.05,
|
| 2083 |
key="realtime_confidence",
|
| 2084 |
-
help="
|
| 2085 |
)
|
| 2086 |
|
| 2087 |
model_choice = st.selectbox(
|
| 2088 |
"Embedding model",
|
| 2089 |
["VGG-Face", "Facenet", "OpenFace", "ArcFace"],
|
| 2090 |
key="realtime_model",
|
| 2091 |
-
help="
|
| 2092 |
)
|
| 2093 |
|
| 2094 |
voting_method = st.radio(
|
|
@@ -2118,30 +2117,23 @@ def main():
|
|
| 2118 |
step=1,
|
| 2119 |
help="Limit frames per second to reduce CPU usage"
|
| 2120 |
)
|
| 2121 |
-
|
| 2122 |
-
#
|
| 2123 |
if 'recognition_camera_running' not in st.session_state:
|
| 2124 |
st.session_state.recognition_camera_running = False
|
| 2125 |
-
|
| 2126 |
-
# Inicializar historial de reconocimiento para estabilización
|
| 2127 |
if 'recognition_history' not in st.session_state:
|
| 2128 |
st.session_state.recognition_history = {}
|
| 2129 |
-
|
| 2130 |
# Camera control buttons
|
| 2131 |
col1, col2 = st.columns(2)
|
| 2132 |
start_button = col1.button("Start Camera", key="start_recognition_camera",
|
| 2133 |
on_click=lambda: setattr(st.session_state, 'recognition_camera_running', True))
|
| 2134 |
stop_button = col2.button("Stop Camera", key="stop_recognition_camera",
|
| 2135 |
on_click=lambda: setattr(st.session_state, 'recognition_camera_running', False))
|
| 2136 |
-
|
| 2137 |
-
|
| 2138 |
-
st.error("Could not access the camera. Make sure it's connected and not being used by another application.")
|
| 2139 |
-
st.session_state.recognition_camera_running = False
|
| 2140 |
-
|
| 2141 |
-
# Placeholder para el video
|
| 2142 |
video_placeholder = st.empty()
|
| 2143 |
-
|
| 2144 |
-
# Placeholder para métricas
|
| 2145 |
metrics_cols = st.columns(3)
|
| 2146 |
with metrics_cols[0]:
|
| 2147 |
faces_metric = st.empty()
|
|
@@ -2149,210 +2141,31 @@ def main():
|
|
| 2149 |
fps_metric = st.empty()
|
| 2150 |
with metrics_cols[2]:
|
| 2151 |
time_metric = st.empty()
|
| 2152 |
-
|
| 2153 |
if st.session_state.recognition_camera_running:
|
| 2154 |
-
|
| 2155 |
-
|
| 2156 |
-
|
| 2157 |
-
|
| 2158 |
-
|
| 2159 |
-
|
| 2160 |
-
|
| 2161 |
-
|
| 2162 |
-
|
| 2163 |
-
|
| 2164 |
-
|
| 2165 |
-
|
| 2166 |
-
|
| 2167 |
-
|
| 2168 |
-
|
| 2169 |
-
|
| 2170 |
-
while st.session_state.recognition_camera_running:
|
| 2171 |
-
# Control de FPS
|
| 2172 |
-
current_time = time.time()
|
| 2173 |
-
elapsed = current_time - last_frame_time
|
| 2174 |
-
if elapsed < 1.0/fps_limit:
|
| 2175 |
-
time.sleep(0.01) # Pequeña pausa para no sobrecargar la CPU
|
| 2176 |
-
continue
|
| 2177 |
-
|
| 2178 |
-
last_frame_time = current_time
|
| 2179 |
-
|
| 2180 |
-
# Leer frame
|
| 2181 |
-
ret, frame = cap.read()
|
| 2182 |
-
if not ret:
|
| 2183 |
-
st.error("Error al leer frame de la cámara.")
|
| 2184 |
-
break
|
| 2185 |
-
|
| 2186 |
-
# Actualizar contador de frames
|
| 2187 |
-
frame_count += 1
|
| 2188 |
-
|
| 2189 |
-
# Calcular FPS
|
| 2190 |
-
if frame_count % 5 == 0:
|
| 2191 |
-
fps = 5 / (current_time - start_time)
|
| 2192 |
-
fps_history.append(fps)
|
| 2193 |
-
if len(fps_history) > 10:
|
| 2194 |
-
fps_history.pop(0)
|
| 2195 |
-
avg_fps = sum(fps_history) / len(fps_history)
|
| 2196 |
-
start_time = current_time
|
| 2197 |
-
|
| 2198 |
-
# Actualizar métricas
|
| 2199 |
-
fps_metric.metric("FPS", f"{avg_fps:.1f}")
|
| 2200 |
-
time_metric.metric("Tiempo activo", f"{int(current_time - time.time() + st.session_state.get('camera_start_time', current_time))}s")
|
| 2201 |
-
|
| 2202 |
-
# Detect rostros
|
| 2203 |
-
detections = detect_face_dnn(face_net, frame, confidence_threshold)
|
| 2204 |
-
_, bboxes = process_face_detections(frame, detections, confidence_threshold)
|
| 2205 |
-
|
| 2206 |
-
# Actualizar métrica de rostros
|
| 2207 |
-
if frame_count % 5 == 0:
|
| 2208 |
-
faces_metric.metric("Faces detected", len(bboxes))
|
| 2209 |
-
|
| 2210 |
-
# Reconocer cada rostro
|
| 2211 |
-
result_frame = frame.copy()
|
| 2212 |
-
|
| 2213 |
-
for i, bbox in enumerate(bboxes):
|
| 2214 |
-
face_id = f"face_{i}"
|
| 2215 |
-
|
| 2216 |
-
# Extraer embedding del rostro
|
| 2217 |
-
embedding = extract_face_embeddings(frame, bbox, model_name=model_choice)
|
| 2218 |
-
|
| 2219 |
-
if embedding is not None:
|
| 2220 |
-
# Compare con rostros registrados
|
| 2221 |
-
matches = []
|
| 2222 |
-
|
| 2223 |
-
for name, info in st.session_state.face_database.items():
|
| 2224 |
-
if 'embeddings' in info:
|
| 2225 |
-
# Nuevo formato con múltiples embeddings
|
| 2226 |
-
similarities = []
|
| 2227 |
-
|
| 2228 |
-
for idx, registered_embedding in enumerate(info['embeddings']):
|
| 2229 |
-
# Usar el mismo modelo si es posible
|
| 2230 |
-
if info['models'][idx] == model_choice:
|
| 2231 |
-
weight = 1.0 # Dar más peso a embeddings del mismo modelo
|
| 2232 |
-
else:
|
| 2233 |
-
weight = 0.8 # Peso menor para embeddings de otros modelos
|
| 2234 |
-
|
| 2235 |
-
# Asegurarse de que los embeddings sean compatibles
|
| 2236 |
-
try:
|
| 2237 |
-
similarity = cosine_similarity([embedding["embedding"]], [registered_embedding])[0][0] * 100 * weight
|
| 2238 |
-
similarities.append(similarity)
|
| 2239 |
-
except ValueError as e:
|
| 2240 |
-
# Si hay error de dimensiones incompatibles, omitir esta comparación
|
| 2241 |
-
continue
|
| 2242 |
-
|
| 2243 |
-
# Aplicar método de votación seleccionado
|
| 2244 |
-
if voting_method == "Promedio":
|
| 2245 |
-
final_similarity = sum(similarities) / len(similarities)
|
| 2246 |
-
elif voting_method == "Mejor coincidencia":
|
| 2247 |
-
final_similarity = max(similarities)
|
| 2248 |
-
else: # Votación ponderada
|
| 2249 |
-
# Dar más peso a similitudes más altas
|
| 2250 |
-
weighted_sum = sum(s * (i+1) for i, s in enumerate(sorted(similarities)))
|
| 2251 |
-
weights_sum = sum(i+1 for i in range(len(similarities)))
|
| 2252 |
-
final_similarity = weighted_sum / weights_sum
|
| 2253 |
-
|
| 2254 |
-
matches.append({"name": name, "similarity": final_similarity})
|
| 2255 |
-
else:
|
| 2256 |
-
# Formato antiguo con un solo embedding
|
| 2257 |
-
registered_embedding = info['embedding']
|
| 2258 |
-
try:
|
| 2259 |
-
similarity = cosine_similarity([embedding["embedding"]], [registered_embedding])[0][0] * 100
|
| 2260 |
-
matches.append({"name": name, "similarity": similarity})
|
| 2261 |
-
except ValueError as e:
|
| 2262 |
-
# Si hay error de dimensiones incompatibles, omitir esta comparación
|
| 2263 |
-
# Modelos incompatibles: {embedding['model']} vs formato antiguo
|
| 2264 |
-
continue
|
| 2265 |
-
|
| 2266 |
-
# Ordenar coincidencias por similitud
|
| 2267 |
-
matches.sort(key=lambda x: x["similarity"], reverse=True)
|
| 2268 |
-
|
| 2269 |
-
# Estabilizar resultados si está activado
|
| 2270 |
-
if stabilize_results and matches:
|
| 2271 |
-
best_match = matches[0]
|
| 2272 |
-
|
| 2273 |
-
# Inicializar historial para este rostro si no existe
|
| 2274 |
-
if face_id not in st.session_state.recognition_history:
|
| 2275 |
-
st.session_state.recognition_history[face_id] = {
|
| 2276 |
-
"names": [],
|
| 2277 |
-
"similarities": []
|
| 2278 |
-
}
|
| 2279 |
-
|
| 2280 |
-
# Añadir al historial
|
| 2281 |
-
history = st.session_state.recognition_history[face_id]
|
| 2282 |
-
history["names"].append(best_match["name"])
|
| 2283 |
-
history["similarities"].append(best_match["similarity"])
|
| 2284 |
-
|
| 2285 |
-
# Limitar historial a los últimos 10 frames
|
| 2286 |
-
if len(history["names"]) > 10:
|
| 2287 |
-
history["names"].pop(0)
|
| 2288 |
-
history["similarities"].pop(0)
|
| 2289 |
-
|
| 2290 |
-
# Determinar el nombre más frecuente en el historial
|
| 2291 |
-
if len(history["names"]) >= 3: # Necesitamos al menos 3 frames para estabilizar
|
| 2292 |
-
name_counts = {}
|
| 2293 |
-
for name in history["names"]:
|
| 2294 |
-
if name not in name_counts:
|
| 2295 |
-
name_counts[name] = 0
|
| 2296 |
-
name_counts[name] += 1
|
| 2297 |
-
|
| 2298 |
-
# Encontrar el nombre más frecuente
|
| 2299 |
-
stable_name = max(name_counts.items(), key=lambda x: x[1])[0]
|
| 2300 |
-
|
| 2301 |
-
# Calcular similitud promedio para ese nombre
|
| 2302 |
-
stable_similarities = [
|
| 2303 |
-
history["similarities"][i]
|
| 2304 |
-
for i in range(len(history["names"]))
|
| 2305 |
-
if history["names"][i] == stable_name
|
| 2306 |
-
]
|
| 2307 |
-
stable_similarity = sum(stable_similarities) / len(stable_similarities)
|
| 2308 |
-
|
| 2309 |
-
# Reemplazar la mejor coincidencia con el resultado estabilizado
|
| 2310 |
-
best_match = {"name": stable_name, "similarity": stable_similarity}
|
| 2311 |
-
else:
|
| 2312 |
-
best_match = matches[0]
|
| 2313 |
-
else:
|
| 2314 |
-
best_match = matches[0] if matches else None
|
| 2315 |
-
|
| 2316 |
-
# Dibujar resultado en la imagen
|
| 2317 |
-
x1, y1, x2, y2, _ = bbox
|
| 2318 |
-
|
| 2319 |
-
if best_match and best_match["similarity"] >= similarity_threshold:
|
| 2320 |
-
# Coincidencia encontrada
|
| 2321 |
-
# Color basado en nivel de similitud
|
| 2322 |
-
if best_match["similarity"] >= 80:
|
| 2323 |
-
color = (0, 255, 0) # Verde para alta similitud
|
| 2324 |
-
elif best_match["similarity"] >= 65:
|
| 2325 |
-
color = (0, 255, 255) # Amarillo para media similitud
|
| 2326 |
-
else:
|
| 2327 |
-
color = (0, 165, 255) # Naranja para baja similitud
|
| 2328 |
-
|
| 2329 |
-
# Dibujar rectángulo y etiqueta
|
| 2330 |
-
cv2.rectangle(result_frame, (x1, y1), (x2, y2), color, 2)
|
| 2331 |
-
|
| 2332 |
-
if show_confidence:
|
| 2333 |
-
label = f"{best_match['name']}: {best_match['similarity']:.1f}%"
|
| 2334 |
-
else:
|
| 2335 |
-
label = f"{best_match['name']}"
|
| 2336 |
-
|
| 2337 |
-
cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
|
| 2338 |
-
else:
|
| 2339 |
-
# No hay coincidencia
|
| 2340 |
-
cv2.rectangle(result_frame, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
| 2341 |
-
|
| 2342 |
-
if best_match:
|
| 2343 |
-
label = f"Desconocido: {best_match['similarity']:.1f}%" if show_confidence else "Desconocido"
|
| 2344 |
-
else:
|
| 2345 |
-
label = "Desconocido"
|
| 2346 |
-
|
| 2347 |
-
cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
|
| 2348 |
|
| 2349 |
-
|
| 2350 |
-
|
| 2351 |
-
|
| 2352 |
-
|
|
|
|
| 2353 |
cap.release()
|
| 2354 |
-
# Limpiar historial de reconocimiento
|
| 2355 |
-
st.session_state.recognition_history = {}
|
| 2356 |
else:
|
| 2357 |
st.info("Click 'Start Camera' to begin real-time recognition.")
|
| 2358 |
|
|
|
|
| 2057 |
with tab3:
|
| 2058 |
st.header("Real-time Recognition")
|
| 2059 |
|
| 2060 |
+
# Check if there are registered faces
|
| 2061 |
if not st.session_state.face_database:
|
| 2062 |
st.warning("No faces registered. Please register at least one face first.")
|
| 2063 |
else:
|
| 2064 |
+
# Advanced settings
|
| 2065 |
+
with st.expander("Advanced Settings", expanded=False):
|
|
|
|
| 2066 |
similarity_threshold = st.slider(
|
| 2067 |
"Similarity threshold (%)",
|
| 2068 |
min_value=35.0,
|
|
|
|
| 2070 |
value=45.0,
|
| 2071 |
step=5.0,
|
| 2072 |
key="realtime_threshold",
|
| 2073 |
+
help="Minimum percentage of similarity to consider a match"
|
| 2074 |
)
|
| 2075 |
|
| 2076 |
confidence_threshold = st.slider(
|
|
|
|
| 2080 |
value=0.5,
|
| 2081 |
step=0.05,
|
| 2082 |
key="realtime_confidence",
|
| 2083 |
+
help="Higher value is more restrictive but more accurate"
|
| 2084 |
)
|
| 2085 |
|
| 2086 |
model_choice = st.selectbox(
|
| 2087 |
"Embedding model",
|
| 2088 |
["VGG-Face", "Facenet", "OpenFace", "ArcFace"],
|
| 2089 |
key="realtime_model",
|
| 2090 |
+
help="Different models may give different results based on facial features"
|
| 2091 |
)
|
| 2092 |
|
| 2093 |
voting_method = st.radio(
|
|
|
|
| 2117 |
step=1,
|
| 2118 |
help="Limit frames per second to reduce CPU usage"
|
| 2119 |
)
|
| 2120 |
+
|
| 2121 |
+
# Initialize states
|
| 2122 |
if 'recognition_camera_running' not in st.session_state:
|
| 2123 |
st.session_state.recognition_camera_running = False
|
| 2124 |
+
|
|
|
|
| 2125 |
if 'recognition_history' not in st.session_state:
|
| 2126 |
st.session_state.recognition_history = {}
|
| 2127 |
+
|
| 2128 |
# Camera control buttons
|
| 2129 |
col1, col2 = st.columns(2)
|
| 2130 |
start_button = col1.button("Start Camera", key="start_recognition_camera",
|
| 2131 |
on_click=lambda: setattr(st.session_state, 'recognition_camera_running', True))
|
| 2132 |
stop_button = col2.button("Stop Camera", key="stop_recognition_camera",
|
| 2133 |
on_click=lambda: setattr(st.session_state, 'recognition_camera_running', False))
|
| 2134 |
+
|
| 2135 |
+
# Video and metrics placeholders
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2136 |
video_placeholder = st.empty()
|
|
|
|
|
|
|
| 2137 |
metrics_cols = st.columns(3)
|
| 2138 |
with metrics_cols[0]:
|
| 2139 |
faces_metric = st.empty()
|
|
|
|
| 2141 |
fps_metric = st.empty()
|
| 2142 |
with metrics_cols[2]:
|
| 2143 |
time_metric = st.empty()
|
| 2144 |
+
|
| 2145 |
if st.session_state.recognition_camera_running:
|
| 2146 |
+
try:
|
| 2147 |
+
st.info("Camera activated. Processing video in real-time...")
|
| 2148 |
+
cap = cv2.VideoCapture(0)
|
| 2149 |
+
|
| 2150 |
+
if not cap.isOpened():
|
| 2151 |
+
raise RuntimeError("Could not access the camera. Make sure it's connected and not being used by another application.")
|
| 2152 |
+
|
| 2153 |
+
# Variables for metrics
|
| 2154 |
+
frame_count = 0
|
| 2155 |
+
start_time = time.time()
|
| 2156 |
+
last_frame_time = start_time
|
| 2157 |
+
fps_history = []
|
| 2158 |
+
|
| 2159 |
+
while st.session_state.recognition_camera_running:
|
| 2160 |
+
# Process frames here
|
| 2161 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2162 |
|
| 2163 |
+
except Exception as e:
|
| 2164 |
+
st.error(str(e))
|
| 2165 |
+
st.session_state.recognition_camera_running = False
|
| 2166 |
+
finally:
|
| 2167 |
+
if 'cap' in locals() and cap is not None:
|
| 2168 |
cap.release()
|
|
|
|
|
|
|
| 2169 |
else:
|
| 2170 |
st.info("Click 'Start Camera' to begin real-time recognition.")
|
| 2171 |
|