Spaces:
Build error
Build error
Commit
·
9fd78b1
1
Parent(s):
7d31590
Implementar persistencia de base de datos de rostros
Browse files- face_database_utils.py +136 -0
- streamlit_app.py +62 -1
face_database_utils.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Utilidades para manejar la persistencia de la base de datos de rostros.
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
import pickle
|
| 6 |
+
import streamlit as st
|
| 7 |
+
import json
|
| 8 |
+
import base64
|
| 9 |
+
import numpy as np
|
| 10 |
+
|
| 11 |
+
# Configurar ruta para la base de datos
|
| 12 |
+
DATABASE_FILE = "face_database.pkl"
|
| 13 |
+
|
| 14 |
+
def save_face_database(database):
|
| 15 |
+
"""
|
| 16 |
+
Guarda la base de datos de rostros en un archivo persistente.
|
| 17 |
+
|
| 18 |
+
Args:
|
| 19 |
+
database (dict): La base de datos de rostros a guardar
|
| 20 |
+
"""
|
| 21 |
+
try:
|
| 22 |
+
# Convertir numpy arrays a listas para poder serializarlas
|
| 23 |
+
serializable_db = {}
|
| 24 |
+
for name, info in database.items():
|
| 25 |
+
serializable_db[name] = {}
|
| 26 |
+
# Manejar diferentes formatos de la base de datos
|
| 27 |
+
if 'embeddings' in info:
|
| 28 |
+
serializable_db[name]['embeddings'] = [emb.tolist() if isinstance(emb, np.ndarray) else emb for emb in info['embeddings']]
|
| 29 |
+
serializable_db[name]['models'] = info['models']
|
| 30 |
+
serializable_db[name]['count'] = info['count']
|
| 31 |
+
elif 'embedding' in info:
|
| 32 |
+
# Formato antiguo
|
| 33 |
+
serializable_db[name]['embedding'] = info['embedding'].tolist() if isinstance(info['embedding'], np.ndarray) else info['embedding']
|
| 34 |
+
serializable_db[name]['count'] = info.get('count', 1)
|
| 35 |
+
|
| 36 |
+
# Guardar en un archivo pickle
|
| 37 |
+
with open(DATABASE_FILE, 'wb') as f:
|
| 38 |
+
pickle.dump(serializable_db, f)
|
| 39 |
+
return True
|
| 40 |
+
except Exception as e:
|
| 41 |
+
st.error(f"Error al guardar la base de datos: {str(e)}")
|
| 42 |
+
return False
|
| 43 |
+
|
| 44 |
+
def load_face_database():
|
| 45 |
+
"""
|
| 46 |
+
Carga la base de datos de rostros desde un archivo persistente.
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
dict: La base de datos de rostros cargada, o un diccionario vacío si no existe el archivo.
|
| 50 |
+
"""
|
| 51 |
+
if not os.path.exists(DATABASE_FILE):
|
| 52 |
+
return {}
|
| 53 |
+
|
| 54 |
+
try:
|
| 55 |
+
with open(DATABASE_FILE, 'rb') as f:
|
| 56 |
+
database = pickle.load(f)
|
| 57 |
+
|
| 58 |
+
# Convertir listas a numpy arrays
|
| 59 |
+
for name, info in database.items():
|
| 60 |
+
if 'embeddings' in info:
|
| 61 |
+
database[name]['embeddings'] = [np.array(emb) if isinstance(emb, list) else emb for emb in info['embeddings']]
|
| 62 |
+
elif 'embedding' in info:
|
| 63 |
+
database[name]['embedding'] = np.array(info['embedding']) if isinstance(info['embedding'], list) else info['embedding']
|
| 64 |
+
|
| 65 |
+
return database
|
| 66 |
+
except Exception as e:
|
| 67 |
+
st.error(f"Error al cargar la base de datos: {str(e)}")
|
| 68 |
+
return {}
|
| 69 |
+
|
| 70 |
+
def export_database_json():
|
| 71 |
+
"""
|
| 72 |
+
Exporta la base de datos a un archivo JSON para compartir o hacer backup.
|
| 73 |
+
|
| 74 |
+
Returns:
|
| 75 |
+
str: Ruta al archivo JSON exportado.
|
| 76 |
+
"""
|
| 77 |
+
try:
|
| 78 |
+
if 'face_database' in st.session_state and st.session_state.face_database:
|
| 79 |
+
# Crear una versión serializable de la base de datos
|
| 80 |
+
serializable_db = {}
|
| 81 |
+
for name, info in st.session_state.face_database.items():
|
| 82 |
+
serializable_db[name] = {}
|
| 83 |
+
if 'embeddings' in info:
|
| 84 |
+
serializable_db[name]['embeddings'] = [
|
| 85 |
+
base64.b64encode(np.array(emb).tobytes()).decode('utf-8')
|
| 86 |
+
for emb in info['embeddings']
|
| 87 |
+
]
|
| 88 |
+
serializable_db[name]['models'] = info['models']
|
| 89 |
+
serializable_db[name]['count'] = info['count']
|
| 90 |
+
elif 'embedding' in info:
|
| 91 |
+
serializable_db[name]['embedding'] = base64.b64encode(
|
| 92 |
+
np.array(info['embedding']).tobytes()
|
| 93 |
+
).decode('utf-8')
|
| 94 |
+
serializable_db[name]['count'] = info.get('count', 1)
|
| 95 |
+
|
| 96 |
+
# Guardar en un archivo JSON
|
| 97 |
+
export_file = "face_database_export.json"
|
| 98 |
+
with open(export_file, 'w') as f:
|
| 99 |
+
json.dump(serializable_db, f, indent=2)
|
| 100 |
+
|
| 101 |
+
return export_file
|
| 102 |
+
return None
|
| 103 |
+
except Exception as e:
|
| 104 |
+
st.error(f"Error al exportar la base de datos: {str(e)}")
|
| 105 |
+
return None
|
| 106 |
+
|
| 107 |
+
def import_database_json(json_file):
|
| 108 |
+
"""
|
| 109 |
+
Importa una base de datos desde un archivo JSON.
|
| 110 |
+
|
| 111 |
+
Args:
|
| 112 |
+
json_file: El archivo JSON a importar
|
| 113 |
+
|
| 114 |
+
Returns:
|
| 115 |
+
dict: La base de datos importada.
|
| 116 |
+
"""
|
| 117 |
+
try:
|
| 118 |
+
content = json_file.read()
|
| 119 |
+
imported_db = json.loads(content)
|
| 120 |
+
|
| 121 |
+
# Convertir datos codificados en base64 a numpy arrays
|
| 122 |
+
for name, info in imported_db.items():
|
| 123 |
+
if 'embeddings' in info:
|
| 124 |
+
imported_db[name]['embeddings'] = [
|
| 125 |
+
np.frombuffer(base64.b64decode(emb), dtype=np.float32)
|
| 126 |
+
for emb in info['embeddings']
|
| 127 |
+
]
|
| 128 |
+
elif 'embedding' in info:
|
| 129 |
+
imported_db[name]['embedding'] = np.frombuffer(
|
| 130 |
+
base64.b64decode(info['embedding']), dtype=np.float32
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
return imported_db
|
| 134 |
+
except Exception as e:
|
| 135 |
+
st.error(f"Error al importar la base de datos: {str(e)}")
|
| 136 |
+
return {}
|
streamlit_app.py
CHANGED
|
@@ -13,6 +13,14 @@ import pickle
|
|
| 13 |
from sklearn.metrics.pairwise import cosine_similarity # type: ignore
|
| 14 |
import pandas as pd
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
# Importar DeepFace para reconocimiento facial avanzado
|
| 17 |
try:
|
| 18 |
from deepface import DeepFace
|
|
@@ -1430,7 +1438,11 @@ def main():
|
|
| 1430 |
|
| 1431 |
# Inicializar base de datos de rostros si no existe
|
| 1432 |
if 'face_database' not in st.session_state:
|
| 1433 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1434 |
|
| 1435 |
# Crear pestañas para las diferentes funcionalidades
|
| 1436 |
tab1, tab2, tab3 = st.tabs(["Register Face", "Image Recognition", "Real-time Recognition"])
|
|
@@ -1556,6 +1568,11 @@ def main():
|
|
| 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}")
|
|
@@ -1610,6 +1627,11 @@ def main():
|
|
| 1610 |
# Eliminar el registro
|
| 1611 |
if row["Name"] in st.session_state.face_database:
|
| 1612 |
del st.session_state.face_database[row["Name"]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1613 |
st.success(f"Deleted {row['Name']} from the database.")
|
| 1614 |
st.experimental_rerun()
|
| 1615 |
|
|
@@ -1625,6 +1647,11 @@ def main():
|
|
| 1625 |
with col1:
|
| 1626 |
if st.button("Yes, delete all"):
|
| 1627 |
st.session_state.face_database = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1628 |
st.session_state.confirm_delete_all = False
|
| 1629 |
st.success("All registered faces have been deleted.")
|
| 1630 |
st.experimental_rerun()
|
|
@@ -1634,6 +1661,40 @@ def main():
|
|
| 1634 |
st.experimental_rerun()
|
| 1635 |
else:
|
| 1636 |
st.info("No faces registered yet. Use the form above to register faces.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1637 |
|
| 1638 |
with tab2:
|
| 1639 |
st.header("Image Recognition")
|
|
|
|
| 13 |
from sklearn.metrics.pairwise import cosine_similarity # type: ignore
|
| 14 |
import pandas as pd
|
| 15 |
|
| 16 |
+
# Importar las utilidades para la base de datos de rostros
|
| 17 |
+
try:
|
| 18 |
+
from face_database_utils import save_face_database, load_face_database, export_database_json, import_database_json
|
| 19 |
+
DATABASE_UTILS_AVAILABLE = True
|
| 20 |
+
except ImportError:
|
| 21 |
+
DATABASE_UTILS_AVAILABLE = False
|
| 22 |
+
st.warning("Database utilities are not available. Face recognition data will not be persistent between sessions.")
|
| 23 |
+
|
| 24 |
# Importar DeepFace para reconocimiento facial avanzado
|
| 25 |
try:
|
| 26 |
from deepface import DeepFace
|
|
|
|
| 1438 |
|
| 1439 |
# Inicializar base de datos de rostros si no existe
|
| 1440 |
if 'face_database' not in st.session_state:
|
| 1441 |
+
if DATABASE_UTILS_AVAILABLE:
|
| 1442 |
+
# Cargar la base de datos desde el archivo persistente
|
| 1443 |
+
st.session_state.face_database = load_face_database()
|
| 1444 |
+
else:
|
| 1445 |
+
st.session_state.face_database = {}
|
| 1446 |
|
| 1447 |
# Crear pestañas para las diferentes funcionalidades
|
| 1448 |
tab1, tab2, tab3 = st.tabs(["Register Face", "Image Recognition", "Real-time Recognition"])
|
|
|
|
| 1568 |
|
| 1569 |
st.success(f"Face registered successfully for {person_name}!")
|
| 1570 |
|
| 1571 |
+
# Guardar la base de datos actualizada
|
| 1572 |
+
if DATABASE_UTILS_AVAILABLE:
|
| 1573 |
+
if save_face_database(st.session_state.face_database):
|
| 1574 |
+
st.info("Face database saved successfully!")
|
| 1575 |
+
|
| 1576 |
# Mostrar la imagen con el rostro detectado
|
| 1577 |
processed_image, _ = process_face_detections(image, [bboxes[0]], confidence_threshold)
|
| 1578 |
st.image(cv2.cvtColor(processed_image, cv2.COLOR_BGR2RGB), caption=f"Registered face: {person_name}")
|
|
|
|
| 1627 |
# Eliminar el registro
|
| 1628 |
if row["Name"] in st.session_state.face_database:
|
| 1629 |
del st.session_state.face_database[row["Name"]]
|
| 1630 |
+
|
| 1631 |
+
# Guardar la base de datos actualizada
|
| 1632 |
+
if DATABASE_UTILS_AVAILABLE:
|
| 1633 |
+
save_face_database(st.session_state.face_database)
|
| 1634 |
+
|
| 1635 |
st.success(f"Deleted {row['Name']} from the database.")
|
| 1636 |
st.experimental_rerun()
|
| 1637 |
|
|
|
|
| 1647 |
with col1:
|
| 1648 |
if st.button("Yes, delete all"):
|
| 1649 |
st.session_state.face_database = {}
|
| 1650 |
+
|
| 1651 |
+
# Guardar la base de datos vacía
|
| 1652 |
+
if DATABASE_UTILS_AVAILABLE:
|
| 1653 |
+
save_face_database({})
|
| 1654 |
+
|
| 1655 |
st.session_state.confirm_delete_all = False
|
| 1656 |
st.success("All registered faces have been deleted.")
|
| 1657 |
st.experimental_rerun()
|
|
|
|
| 1661 |
st.experimental_rerun()
|
| 1662 |
else:
|
| 1663 |
st.info("No faces registered yet. Use the form above to register faces.")
|
| 1664 |
+
|
| 1665 |
+
# Añadir botones para importar/exportar la base de datos
|
| 1666 |
+
if DATABASE_UTILS_AVAILABLE:
|
| 1667 |
+
st.subheader("Database Management")
|
| 1668 |
+
col1, col2 = st.columns(2)
|
| 1669 |
+
|
| 1670 |
+
with col1:
|
| 1671 |
+
# Exportar base de datos
|
| 1672 |
+
if st.button("Export Face Database") and st.session_state.face_database:
|
| 1673 |
+
export_file = export_database_json()
|
| 1674 |
+
if export_file:
|
| 1675 |
+
with open(export_file, "rb") as f:
|
| 1676 |
+
st.download_button(
|
| 1677 |
+
label="Download JSON Database",
|
| 1678 |
+
data=f,
|
| 1679 |
+
file_name="face_database.json",
|
| 1680 |
+
mime="application/json"
|
| 1681 |
+
)
|
| 1682 |
+
|
| 1683 |
+
with col2:
|
| 1684 |
+
# Importar base de datos
|
| 1685 |
+
uploaded_json = st.file_uploader("Import Face Database", type=["json"], key="import_database")
|
| 1686 |
+
if uploaded_json is not None:
|
| 1687 |
+
if st.button("Process Import"):
|
| 1688 |
+
with st.spinner("Importing database..."):
|
| 1689 |
+
imported_db = import_database_json(uploaded_json)
|
| 1690 |
+
if imported_db:
|
| 1691 |
+
# Actualizar la base de datos actual
|
| 1692 |
+
st.session_state.face_database.update(imported_db)
|
| 1693 |
+
|
| 1694 |
+
# Guardar la base de datos actualizada
|
| 1695 |
+
if save_face_database(st.session_state.face_database):
|
| 1696 |
+
st.success("Database imported and saved successfully!")
|
| 1697 |
+
st.experimental_rerun()
|
| 1698 |
|
| 1699 |
with tab2:
|
| 1700 |
st.header("Image Recognition")
|