Spaces:
Build error
Build error
Commit
·
784f8aa
1
Parent(s):
3d308e7
Add name validation and loading spinner during image processing
Browse files- streamlit_app.py +216 -283
streamlit_app.py
CHANGED
|
@@ -1469,167 +1469,98 @@ def main():
|
|
| 1469 |
# Botón de registro
|
| 1470 |
register_button = st.form_submit_button("Register Face")
|
| 1471 |
|
| 1472 |
-
if register_button
|
| 1473 |
-
#
|
| 1474 |
-
|
| 1475 |
-
|
| 1476 |
-
|
| 1477 |
-
|
| 1478 |
-
|
| 1479 |
-
|
| 1480 |
-
|
| 1481 |
-
|
| 1482 |
-
|
| 1483 |
-
|
| 1484 |
-
if not bboxes:
|
| 1485 |
-
st.error("No faces detected in the image. Please upload another image.")
|
| 1486 |
-
elif len(bboxes) > 1:
|
| 1487 |
-
st.warning("Multiple faces detected. The first one will be used.")
|
| 1488 |
-
|
| 1489 |
-
# Extraer embeddings del primer rostro
|
| 1490 |
-
if bboxes and len(bboxes) > 0 and len(bboxes[0]) == 5:
|
| 1491 |
-
embeddings_all_models = extract_face_embeddings_all_models(image, bboxes[0])
|
| 1492 |
-
|
| 1493 |
-
if embeddings_all_models:
|
| 1494 |
-
# Guardar en la base de datos
|
| 1495 |
-
if add_to_existing and person_name in st.session_state.face_database:
|
| 1496 |
-
# Añadir a persona existente
|
| 1497 |
-
if 'embeddings' in st.session_state.face_database[person_name]:
|
| 1498 |
-
# Formato nuevo con múltiples embeddings
|
| 1499 |
-
for embedding in embeddings_all_models:
|
| 1500 |
-
model_name = embedding['model']
|
| 1501 |
-
model_idx = -1
|
| 1502 |
-
|
| 1503 |
-
# Buscar si ya existe un embedding de este modelo
|
| 1504 |
-
for i, model in enumerate(st.session_state.face_database[person_name]['models']):
|
| 1505 |
-
if model == model_name:
|
| 1506 |
-
model_idx = i
|
| 1507 |
-
break
|
| 1508 |
-
|
| 1509 |
-
if model_idx >= 0:
|
| 1510 |
-
# Actualizar embedding existente
|
| 1511 |
-
st.session_state.face_database[person_name]['embeddings'][model_idx] = embedding['embedding']
|
| 1512 |
-
else:
|
| 1513 |
-
# Añadir nuevo modelo
|
| 1514 |
-
st.session_state.face_database[person_name]['models'].append(model_name)
|
| 1515 |
-
st.session_state.face_database[person_name]['embeddings'].append(embedding['embedding'])
|
| 1516 |
-
|
| 1517 |
-
# Incrementar contador
|
| 1518 |
-
st.session_state.face_database[person_name]['count'] += 1
|
| 1519 |
-
else:
|
| 1520 |
-
# Formato antiguo, convertir a nuevo formato
|
| 1521 |
-
old_embedding = st.session_state.face_database[person_name]['embedding']
|
| 1522 |
-
old_model = 'VGG-Face' # Modelo por defecto para embeddings antiguos
|
| 1523 |
-
|
| 1524 |
-
# Crear nuevo formato
|
| 1525 |
-
st.session_state.face_database[person_name] = {
|
| 1526 |
-
'embeddings': [old_embedding],
|
| 1527 |
-
'models': [old_model],
|
| 1528 |
-
'count': 1
|
| 1529 |
-
}
|
| 1530 |
-
|
| 1531 |
-
# Añadir nuevos embeddings
|
| 1532 |
-
for embedding in embeddings_all_models:
|
| 1533 |
-
model_name = embedding['model']
|
| 1534 |
-
if model_name != old_model: # Evitar duplicados
|
| 1535 |
-
st.session_state.face_database[person_name]['models'].append(model_name)
|
| 1536 |
-
st.session_state.face_database[person_name]['embeddings'].append(embedding['embedding'])
|
| 1537 |
-
|
| 1538 |
-
# Incrementar contador
|
| 1539 |
-
st.session_state.face_database[person_name]['count'] += 1
|
| 1540 |
-
else:
|
| 1541 |
-
# Crear nueva entrada
|
| 1542 |
-
models = []
|
| 1543 |
-
embeddings = []
|
| 1544 |
-
|
| 1545 |
-
for embedding in embeddings_all_models:
|
| 1546 |
-
models.append(embedding['model'])
|
| 1547 |
-
embeddings.append(embedding['embedding'])
|
| 1548 |
-
|
| 1549 |
-
st.session_state.face_database[person_name] = {
|
| 1550 |
-
'embeddings': embeddings,
|
| 1551 |
-
'models': models,
|
| 1552 |
-
'count': 1
|
| 1553 |
-
}
|
| 1554 |
|
| 1555 |
-
|
|
|
|
|
|
|
| 1556 |
|
| 1557 |
-
#
|
| 1558 |
-
processed_image,
|
| 1559 |
-
|
| 1560 |
-
|
| 1561 |
-
|
| 1562 |
-
|
| 1563 |
-
|
| 1564 |
-
|
| 1565 |
-
|
| 1566 |
-
|
| 1567 |
-
|
| 1568 |
-
|
| 1569 |
-
|
| 1570 |
-
|
| 1571 |
-
|
| 1572 |
-
|
| 1573 |
-
|
| 1574 |
-
|
| 1575 |
-
|
| 1576 |
-
|
| 1577 |
-
|
| 1578 |
-
|
| 1579 |
-
|
| 1580 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1581 |
|
| 1582 |
-
|
| 1583 |
-
|
| 1584 |
-
|
| 1585 |
-
|
| 1586 |
-
# Añadir nuevo modelo
|
| 1587 |
-
st.session_state.face_database[person_name]['models'].append(model_name)
|
| 1588 |
-
st.session_state.face_database[person_name]['embeddings'].append(embedding['embedding'])
|
| 1589 |
-
|
| 1590 |
-
# Incrementar contador
|
| 1591 |
-
st.session_state.face_database[person_name]['count'] += 1
|
| 1592 |
-
else:
|
| 1593 |
-
# Formato antiguo, convertir a nuevo formato
|
| 1594 |
-
old_embedding = st.session_state.face_database[person_name]['embedding']
|
| 1595 |
-
old_model = 'VGG-Face' # Modelo por defecto para embeddings antiguos
|
| 1596 |
-
|
| 1597 |
-
# Crear nuevo formato
|
| 1598 |
-
st.session_state.face_database[person_name] = {
|
| 1599 |
-
'embeddings': [old_embedding],
|
| 1600 |
-
'models': [old_model],
|
| 1601 |
-
'count': 1
|
| 1602 |
-
}
|
| 1603 |
-
|
| 1604 |
-
# Añadir nuevos embeddings
|
| 1605 |
-
for embedding in embeddings_all_models:
|
| 1606 |
-
model_name = embedding['model']
|
| 1607 |
-
if model_name != old_model: # Evitar duplicados
|
| 1608 |
-
st.session_state.face_database[person_name]['models'].append(model_name)
|
| 1609 |
-
st.session_state.face_database[person_name]['embeddings'].append(embedding['embedding'])
|
| 1610 |
-
|
| 1611 |
-
# Incrementar contador
|
| 1612 |
-
st.session_state.face_database[person_name]['count'] += 1
|
| 1613 |
else:
|
| 1614 |
-
#
|
| 1615 |
-
|
| 1616 |
-
embeddings = []
|
| 1617 |
-
|
| 1618 |
-
for embedding in embeddings_all_models:
|
| 1619 |
-
models.append(embedding['model'])
|
| 1620 |
-
embeddings.append(embedding['embedding'])
|
| 1621 |
|
| 1622 |
-
|
| 1623 |
-
|
| 1624 |
-
|
| 1625 |
-
|
| 1626 |
-
|
| 1627 |
-
|
| 1628 |
-
|
| 1629 |
-
|
| 1630 |
-
|
| 1631 |
-
|
| 1632 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1633 |
else:
|
| 1634 |
st.error("Failed to extract embeddings. Please try again with a clearer image.")
|
| 1635 |
|
|
@@ -1756,145 +1687,147 @@ def main():
|
|
| 1756 |
)
|
| 1757 |
|
| 1758 |
if uploaded_file is not None:
|
| 1759 |
-
#
|
| 1760 |
-
|
| 1761 |
-
|
| 1762 |
-
|
| 1763 |
-
|
| 1764 |
-
detections = detect_face_dnn(face_net, image, confidence_threshold)
|
| 1765 |
-
processed_image, bboxes = process_face_detections(image, detections, confidence_threshold)
|
| 1766 |
-
|
| 1767 |
-
if not bboxes:
|
| 1768 |
-
st.error("No se detectaron rostros en la imagen.")
|
| 1769 |
-
else:
|
| 1770 |
-
# Mostrar imagen con rostros detectados
|
| 1771 |
-
st.image(processed_image, channels='BGR', caption="Faces detected")
|
| 1772 |
|
| 1773 |
-
#
|
| 1774 |
-
|
|
|
|
| 1775 |
|
| 1776 |
-
|
| 1777 |
-
|
| 1778 |
-
|
| 1779 |
-
|
| 1780 |
-
|
| 1781 |
-
|
|
|
|
|
|
|
| 1782 |
|
| 1783 |
-
|
| 1784 |
-
|
| 1785 |
-
|
|
|
|
|
|
|
|
|
|
| 1786 |
|
| 1787 |
-
|
| 1788 |
-
|
| 1789 |
-
|
| 1790 |
-
|
| 1791 |
-
|
| 1792 |
-
|
| 1793 |
-
#
|
| 1794 |
-
|
| 1795 |
-
|
| 1796 |
-
|
| 1797 |
-
|
| 1798 |
-
|
| 1799 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1800 |
try:
|
| 1801 |
-
similarity = cosine_similarity([embedding["embedding"]], [registered_embedding])[0][0] * 100
|
| 1802 |
-
|
| 1803 |
except ValueError as e:
|
| 1804 |
# Si hay error de dimensiones incompatibles, omitir esta comparación
|
| 1805 |
-
# Modelos incompatibles: {
|
| 1806 |
continue
|
| 1807 |
-
|
| 1808 |
-
# Aplicar método de votación seleccionado
|
| 1809 |
-
if voting_method == "Promedio":
|
| 1810 |
-
if similarities: # Verificar que la lista no esté vacía
|
| 1811 |
-
final_similarity = sum(similarities) / len(similarities)
|
| 1812 |
-
else:
|
| 1813 |
-
final_similarity = 0.0 # Valor predeterminado si no hay similitudes
|
| 1814 |
-
elif voting_method == "Mejor coincidencia":
|
| 1815 |
-
if similarities: # Verificar que la lista no esté vacía
|
| 1816 |
-
final_similarity = max(similarities)
|
| 1817 |
-
else:
|
| 1818 |
-
final_similarity = 0.0 # Valor predeterminado si no hay similitudes
|
| 1819 |
-
else: # Votación ponderada
|
| 1820 |
-
if similarities: # Verificar que la lista no esté vacía
|
| 1821 |
-
# Dar más peso a similitudes más altas
|
| 1822 |
-
weighted_sum = sum(s * (i+1) for i, s in enumerate(sorted(similarities)))
|
| 1823 |
-
weights_sum = sum(i+1 for i in range(len(similarities)))
|
| 1824 |
-
final_similarity = weighted_sum / weights_sum
|
| 1825 |
-
else:
|
| 1826 |
-
final_similarity = 0.0 # Valor predeterminado si no hay similitudes
|
| 1827 |
-
|
| 1828 |
-
matches.append({"name": name, "similarity": final_similarity, "count": info['count']})
|
| 1829 |
-
else:
|
| 1830 |
-
# Formato antiguo con un solo embedding
|
| 1831 |
-
registered_embedding = info['embedding']
|
| 1832 |
-
try:
|
| 1833 |
-
similarity = cosine_similarity([embedding["embedding"]], [registered_embedding])[0][0] * 100
|
| 1834 |
-
matches.append({"name": name, "similarity": similarity, "count": 1})
|
| 1835 |
-
except ValueError as e:
|
| 1836 |
-
# Si hay error de dimensiones incompatibles, omitir esta comparación
|
| 1837 |
-
# Modelos incompatibles: {embedding['model']} vs formato antiguo
|
| 1838 |
-
continue
|
| 1839 |
-
|
| 1840 |
-
# Ordenar coincidencias por similitud
|
| 1841 |
-
matches.sort(key=lambda x: x["similarity"], reverse=True)
|
| 1842 |
-
|
| 1843 |
-
# Dibujar resultado en la imagen
|
| 1844 |
-
x1, y1, x2, y2, _ = bbox
|
| 1845 |
-
|
| 1846 |
-
if matches and matches[0]["similarity"] >= similarity_threshold:
|
| 1847 |
-
# Coincidencia encontrada
|
| 1848 |
-
best_match = matches[0]
|
| 1849 |
-
|
| 1850 |
-
# Color basado en nivel de similitud
|
| 1851 |
-
if best_match["similarity"] >= 80:
|
| 1852 |
-
color = (0, 255, 0) # Verde para alta similitud
|
| 1853 |
-
elif best_match["similarity"] >= 65:
|
| 1854 |
-
color = (0, 255, 255) # Amarillo para media similitud
|
| 1855 |
-
else:
|
| 1856 |
-
color = (0, 165, 255) # Naranja para baja similitud
|
| 1857 |
|
| 1858 |
-
#
|
| 1859 |
-
|
| 1860 |
-
cv2.rectangle(result_image, (x1, y1), (x2, y2), color, 2)
|
| 1861 |
-
cv2.putText(result_image, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
|
| 1862 |
|
| 1863 |
-
#
|
| 1864 |
-
|
| 1865 |
-
for j, match in enumerate(matches[1:3]): # Mostrar las siguientes 2 mejores coincidencias
|
| 1866 |
-
sub_label = f"#{j+2}: {match['name']}: {match['similarity']:.1f}%"
|
| 1867 |
-
cv2.putText(result_image, sub_label, (x1, y1-(j+2)*20), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (200, 200, 200), 1)
|
| 1868 |
|
| 1869 |
-
|
| 1870 |
-
|
| 1871 |
-
|
| 1872 |
-
|
| 1873 |
-
|
| 1874 |
-
|
| 1875 |
-
|
| 1876 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1877 |
if show_all_matches and len(matches) > 1:
|
| 1878 |
-
|
| 1879 |
-
|
| 1880 |
-
|
| 1881 |
-
|
| 1882 |
-
|
| 1883 |
-
|
| 1884 |
-
|
| 1885 |
-
|
| 1886 |
-
|
| 1887 |
-
|
| 1888 |
-
|
| 1889 |
-
|
| 1890 |
-
|
| 1891 |
-
|
| 1892 |
-
|
| 1893 |
-
|
| 1894 |
-
|
| 1895 |
-
|
| 1896 |
-
|
| 1897 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1898 |
|
| 1899 |
# Mostrar resultado
|
| 1900 |
st.subheader("Recognition Result")
|
|
|
|
| 1469 |
# Botón de registro
|
| 1470 |
register_button = st.form_submit_button("Register Face")
|
| 1471 |
|
| 1472 |
+
if register_button:
|
| 1473 |
+
# Validar que se haya proporcionado un nombre
|
| 1474 |
+
if not person_name:
|
| 1475 |
+
st.error("Person's name is required. Please enter a name.")
|
| 1476 |
+
elif uploaded_file is None:
|
| 1477 |
+
st.error("Please upload an image.")
|
| 1478 |
+
else:
|
| 1479 |
+
# Mostrar spinner durante el procesamiento
|
| 1480 |
+
with st.spinner('Processing image and extracting facial features...'):
|
| 1481 |
+
# Process imagen
|
| 1482 |
+
raw_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
|
| 1483 |
+
image = cv2.imdecode(raw_bytes, cv2.IMREAD_COLOR)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1484 |
|
| 1485 |
+
# Detect rostros
|
| 1486 |
+
face_net = load_face_model()
|
| 1487 |
+
detections = detect_face_dnn(face_net, image, conf_threshold=confidence_threshold)
|
| 1488 |
|
| 1489 |
+
# Procesar detecciones y obtener bounding boxes
|
| 1490 |
+
processed_image, bboxes = process_face_detections(image, detections, confidence_threshold)
|
| 1491 |
+
|
| 1492 |
+
if not bboxes:
|
| 1493 |
+
st.error("No faces detected in the image. Please upload another image.")
|
| 1494 |
+
elif len(bboxes) > 1:
|
| 1495 |
+
st.warning("Multiple faces detected. The first one will be used.")
|
| 1496 |
+
|
| 1497 |
+
# Extraer embeddings del primer rostro
|
| 1498 |
+
if bboxes and len(bboxes) > 0 and len(bboxes[0]) == 5:
|
| 1499 |
+
embeddings_all_models = extract_face_embeddings_all_models(image, bboxes[0])
|
| 1500 |
+
|
| 1501 |
+
if embeddings_all_models:
|
| 1502 |
+
# Guardar en la base de datos
|
| 1503 |
+
if add_to_existing and person_name in st.session_state.face_database:
|
| 1504 |
+
# Añadir a persona existente
|
| 1505 |
+
if 'embeddings' in st.session_state.face_database[person_name]:
|
| 1506 |
+
# Formato nuevo con múltiples embeddings
|
| 1507 |
+
for embedding in embeddings_all_models:
|
| 1508 |
+
model_name = embedding['model']
|
| 1509 |
+
model_idx = -1
|
| 1510 |
+
|
| 1511 |
+
# Buscar si ya existe un embedding de este modelo
|
| 1512 |
+
for i, model in enumerate(st.session_state.face_database[person_name]['models']):
|
| 1513 |
+
if model == model_name:
|
| 1514 |
+
model_idx = i
|
| 1515 |
+
break
|
| 1516 |
+
|
| 1517 |
+
if model_idx >= 0:
|
| 1518 |
+
# Actualizar embedding existente
|
| 1519 |
+
st.session_state.face_database[person_name]['embeddings'][model_idx] = embedding['embedding']
|
| 1520 |
+
else:
|
| 1521 |
+
# Añadir nuevo modelo
|
| 1522 |
+
st.session_state.face_database[person_name]['models'].append(model_name)
|
| 1523 |
+
st.session_state.face_database[person_name]['embeddings'].append(embedding['embedding'])
|
| 1524 |
|
| 1525 |
+
# Incrementar contador
|
| 1526 |
+
st.session_state.face_database[person_name]['count'] += 1
|
| 1527 |
+
else:
|
| 1528 |
+
st.error("Failed to extract embeddings. Please try again with a clearer image.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1529 |
else:
|
| 1530 |
+
# Solo un rostro detectado
|
| 1531 |
+
embeddings_all_models = extract_face_embeddings_all_models(image, bboxes[0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1532 |
|
| 1533 |
+
if embeddings_all_models:
|
| 1534 |
+
# Guardar en la base de datos
|
| 1535 |
+
if add_to_existing and person_name in st.session_state.face_database:
|
| 1536 |
+
# Añadir a persona existente
|
| 1537 |
+
if 'embeddings' in st.session_state.face_database[person_name]:
|
| 1538 |
+
# Formato nuevo con múltiples embeddings
|
| 1539 |
+
for embedding in embeddings_all_models:
|
| 1540 |
+
model_name = embedding['model']
|
| 1541 |
+
model_idx = -1
|
| 1542 |
+
|
| 1543 |
+
# Buscar si ya existe un embedding de este modelo
|
| 1544 |
+
for i, model in enumerate(st.session_state.face_database[person_name]['models']):
|
| 1545 |
+
if model == model_name:
|
| 1546 |
+
model_idx = i
|
| 1547 |
+
break
|
| 1548 |
+
|
| 1549 |
+
if model_idx >= 0:
|
| 1550 |
+
# Actualizar embedding existente
|
| 1551 |
+
st.session_state.face_database[person_name]['embeddings'][model_idx] = embedding['embedding']
|
| 1552 |
+
else:
|
| 1553 |
+
# Añadir nuevo modelo
|
| 1554 |
+
st.session_state.face_database[person_name]['models'].append(model_name)
|
| 1555 |
+
st.session_state.face_database[person_name]['embeddings'].append(embedding['embedding'])
|
| 1556 |
+
|
| 1557 |
+
st.success(f"Face registered successfully for {person_name}!")
|
| 1558 |
+
|
| 1559 |
+
# Mostrar la imagen con el rostro detectado
|
| 1560 |
+
processed_image, _ = process_face_detections(image, [bboxes[0]], confidence_threshold)
|
| 1561 |
+
st.image(cv2.cvtColor(processed_image, cv2.COLOR_BGR2RGB), caption=f"Registered face: {person_name}")
|
| 1562 |
+
else:
|
| 1563 |
+
st.error("Failed to extract embeddings. Please try again with a clearer image.")
|
| 1564 |
else:
|
| 1565 |
st.error("Failed to extract embeddings. Please try again with a clearer image.")
|
| 1566 |
|
|
|
|
| 1687 |
)
|
| 1688 |
|
| 1689 |
if uploaded_file is not None:
|
| 1690 |
+
# Mostrar spinner durante el procesamiento
|
| 1691 |
+
with st.spinner('Processing image and analyzing faces...'):
|
| 1692 |
+
# Process la imagen subida
|
| 1693 |
+
raw_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
|
| 1694 |
+
image = cv2.imdecode(raw_bytes, cv2.IMREAD_COLOR)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1695 |
|
| 1696 |
+
# Detect rostros
|
| 1697 |
+
detections = detect_face_dnn(face_net, image, confidence_threshold)
|
| 1698 |
+
processed_image, bboxes = process_face_detections(image, detections, confidence_threshold)
|
| 1699 |
|
| 1700 |
+
if not bboxes:
|
| 1701 |
+
st.error("No se detectaron rostros en la imagen.")
|
| 1702 |
+
else:
|
| 1703 |
+
# Mostrar imagen con rostros detectados
|
| 1704 |
+
st.image(processed_image, channels='BGR', caption="Faces detected")
|
| 1705 |
+
|
| 1706 |
+
# Reconocer cada rostro
|
| 1707 |
+
result_image = image.copy()
|
| 1708 |
|
| 1709 |
+
# Crear columnas para mostrar estadísticas
|
| 1710 |
+
stats_cols = st.columns(len(bboxes) if len(bboxes) <= 3 else 3)
|
| 1711 |
+
|
| 1712 |
+
for i, bbox in enumerate(bboxes):
|
| 1713 |
+
# Extraer embedding del rostro
|
| 1714 |
+
embedding = extract_face_embeddings(image, bbox, model_name=model_choice)
|
| 1715 |
|
| 1716 |
+
if embedding is not None:
|
| 1717 |
+
# Compare con rostros registrados
|
| 1718 |
+
matches = []
|
| 1719 |
+
|
| 1720 |
+
for name, info in st.session_state.face_database.items():
|
| 1721 |
+
if 'embeddings' in info:
|
| 1722 |
+
# Nuevo formato con múltiples embeddings
|
| 1723 |
+
similarities = []
|
| 1724 |
+
|
| 1725 |
+
for idx, registered_embedding in enumerate(info['embeddings']):
|
| 1726 |
+
# Usar el mismo modelo si es posible
|
| 1727 |
+
if info['models'][idx] == model_choice:
|
| 1728 |
+
weight = 1.0 # Dar más peso a embeddings del mismo modelo
|
| 1729 |
+
else:
|
| 1730 |
+
weight = 0.8 # Peso menor para embeddings de otros modelos
|
| 1731 |
+
|
| 1732 |
+
# Asegurarse de que los embeddings sean compatibles
|
| 1733 |
+
try:
|
| 1734 |
+
similarity = cosine_similarity([embedding["embedding"]], [registered_embedding])[0][0] * 100 * weight
|
| 1735 |
+
similarities.append(similarity)
|
| 1736 |
+
except ValueError as e:
|
| 1737 |
+
# Si hay error de dimensiones incompatibles, omitir esta comparación
|
| 1738 |
+
# Modelos incompatibles: {info['models'][idx]} vs {embedding['model']}
|
| 1739 |
+
continue
|
| 1740 |
+
|
| 1741 |
+
# Aplicar método de votación seleccionado
|
| 1742 |
+
if voting_method == "Promedio":
|
| 1743 |
+
if similarities: # Verificar que la lista no esté vacía
|
| 1744 |
+
final_similarity = sum(similarities) / len(similarities)
|
| 1745 |
+
else:
|
| 1746 |
+
final_similarity = 0.0 # Valor predeterminado si no hay similitudes
|
| 1747 |
+
elif voting_method == "Mejor coincidencia":
|
| 1748 |
+
if similarities: # Verificar que la lista no esté vacía
|
| 1749 |
+
final_similarity = max(similarities)
|
| 1750 |
+
else:
|
| 1751 |
+
final_similarity = 0.0 # Valor predeterminado si no hay similitudes
|
| 1752 |
+
else: # Votación ponderada
|
| 1753 |
+
if similarities: # Verificar que la lista no esté vacía
|
| 1754 |
+
# Dar más peso a similitudes más altas
|
| 1755 |
+
weighted_sum = sum(s * (i+1) for i, s in enumerate(sorted(similarities)))
|
| 1756 |
+
weights_sum = sum(i+1 for i in range(len(similarities)))
|
| 1757 |
+
final_similarity = weighted_sum / weights_sum
|
| 1758 |
+
else:
|
| 1759 |
+
final_similarity = 0.0 # Valor predeterminado si no hay similitudes
|
| 1760 |
+
|
| 1761 |
+
matches.append({"name": name, "similarity": final_similarity, "count": info['count']})
|
| 1762 |
+
else:
|
| 1763 |
+
# Formato antiguo con un solo embedding
|
| 1764 |
+
registered_embedding = info['embedding']
|
| 1765 |
try:
|
| 1766 |
+
similarity = cosine_similarity([embedding["embedding"]], [registered_embedding])[0][0] * 100
|
| 1767 |
+
matches.append({"name": name, "similarity": similarity, "count": 1})
|
| 1768 |
except ValueError as e:
|
| 1769 |
# Si hay error de dimensiones incompatibles, omitir esta comparación
|
| 1770 |
+
# Modelos incompatibles: {embedding['model']} vs formato antiguo
|
| 1771 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1772 |
|
| 1773 |
+
# Ordenar coincidencias por similitud
|
| 1774 |
+
matches.sort(key=lambda x: x["similarity"], reverse=True)
|
|
|
|
|
|
|
| 1775 |
|
| 1776 |
+
# Dibujar resultado en la imagen
|
| 1777 |
+
x1, y1, x2, y2, _ = bbox
|
|
|
|
|
|
|
|
|
|
| 1778 |
|
| 1779 |
+
if matches and matches[0]["similarity"] >= similarity_threshold:
|
| 1780 |
+
# Coincidencia encontrada
|
| 1781 |
+
best_match = matches[0]
|
| 1782 |
+
|
| 1783 |
+
# Color basado en nivel de similitud
|
| 1784 |
+
if best_match["similarity"] >= 80:
|
| 1785 |
+
color = (0, 255, 0) # Verde para alta similitud
|
| 1786 |
+
elif best_match["similarity"] >= 65:
|
| 1787 |
+
color = (0, 255, 255) # Amarillo para media similitud
|
| 1788 |
+
else:
|
| 1789 |
+
color = (0, 165, 255) # Naranja para baja similitud
|
| 1790 |
+
|
| 1791 |
+
# Dibujar rectángulo y etiqueta principal
|
| 1792 |
+
label = f"{best_match['name']}: {best_match['similarity']:.1f}%"
|
| 1793 |
+
cv2.rectangle(result_image, (x1, y1), (x2, y2), color, 2)
|
| 1794 |
+
cv2.putText(result_image, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
|
| 1795 |
+
|
| 1796 |
+
# Mostrar coincidencias adicionales si está activado
|
| 1797 |
if show_all_matches and len(matches) > 1:
|
| 1798 |
+
for j, match in enumerate(matches[1:3]): # Mostrar las siguientes 2 mejores coincidencias
|
| 1799 |
+
sub_label = f"#{j+2}: {match['name']}: {match['similarity']:.1f}%"
|
| 1800 |
+
cv2.putText(result_image, sub_label, (x1, y1-(j+2)*20), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (200, 200, 200), 1)
|
| 1801 |
+
|
| 1802 |
+
# Mostrar estadísticas en columnas
|
| 1803 |
+
col_idx = i % 3
|
| 1804 |
+
with stats_cols[col_idx]:
|
| 1805 |
+
st.metric(
|
| 1806 |
+
f"Rostro {i+1}",
|
| 1807 |
+
f"{best_match['name']}",
|
| 1808 |
+
f"{best_match['similarity']:.1f}%"
|
| 1809 |
+
)
|
| 1810 |
+
if show_all_matches and len(matches) > 1:
|
| 1811 |
+
st.write("Otras coincidencias:")
|
| 1812 |
+
for j, match in enumerate(matches[1:3]):
|
| 1813 |
+
st.write(f"- {match['name']}: {match['similarity']:.1f}%")
|
| 1814 |
+
else:
|
| 1815 |
+
# No hay coincidencia
|
| 1816 |
+
label = "Desconocido"
|
| 1817 |
+
if matches:
|
| 1818 |
+
label += f": {matches[0]['similarity']:.1f}%"
|
| 1819 |
+
|
| 1820 |
+
cv2.rectangle(result_image, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
| 1821 |
+
cv2.putText(result_image, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
|
| 1822 |
+
|
| 1823 |
+
# Mostrar estadísticas en columnas
|
| 1824 |
+
col_idx = i % 3
|
| 1825 |
+
with stats_cols[col_idx]:
|
| 1826 |
+
st.metric(
|
| 1827 |
+
f"Rostro {i+1}",
|
| 1828 |
+
"Desconocido",
|
| 1829 |
+
f"{matches[0]['similarity']:.1f}%" if matches else "N/A"
|
| 1830 |
+
)
|
| 1831 |
|
| 1832 |
# Mostrar resultado
|
| 1833 |
st.subheader("Recognition Result")
|