commit a16d736a5aa5f1cec39673bcbacd4b04eadf5573 Author: Arturo Corro Date: Tue Oct 22 17:12:57 2024 -0600 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..37b0770 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Caches +.cache/ +*.cache + +# Virtual environment +env/ +venv/ +ENV/ +.venv/ + +# Local environment configurations +.env +*.env + +# Database files +*.db +*.sqlite3 +*.db-journal + +# Images, temporary files, and uploads +/tmp/ +*.png +*.jpg +*.jpeg +*.gif +*.bmp + +# Logs +*.log +logs/ + +# Jupyter Notebook checkpoints +.ipynb_checkpoints/ + +# Compiled C extensions +*.so + +# macOS specific files +.DS_Store + +# Windows specific files +Thumbs.db +ehthumbs.db +Desktop.ini + +# System files +.vscode/ +.idea/ diff --git a/app.py b/app.py new file mode 100644 index 0000000..de14588 --- /dev/null +++ b/app.py @@ -0,0 +1,123 @@ +from fastapi import FastAPI, File, UploadFile, HTTPException, Form +from deepface import DeepFace +from manticoresearch import ApiClient, Configuration +from manticoresearch.api import IndexApi, SearchApi +from manticoresearch.model import SearchRequest +from manticoresearch.model import InsertDocumentRequest +import os +import shutil +from numpy import dot +from numpy.linalg import norm + +# Configurar el cliente API +config = Configuration() +config.host = "http://localhost:9308" +client = ApiClient(configuration=config) + +# Inicializar las APIs +index_api = IndexApi(client) +search_api = SearchApi(client) + +app = FastAPI() + +# Función para calcular similitud de coseno +def cosine_similarity(vec1, vec2): + return dot(vec1, vec2) / (norm(vec1) * norm(vec2)) + +# Ruta para registrar un nuevo usuario +@app.post("/register_user") +async def register_user(user_id: str = Form(...), image: UploadFile = File(...)): + try: + # Guardar la imagen temporalmente + img_path = f"/tmp/{image.filename}" + with open(img_path, "wb") as buffer: + shutil.copyfileobj(image.file, buffer) + + # Obtener los embeddings faciales + embedding_result = DeepFace.represent(img_path, model_name="ArcFace") + + # Extraer los embeddings + embeddings = embedding_result[0]['embedding'] # Extrae el vector de embeddings + + # Verifica si embeddings tiene 512 valores + if len(embeddings) != 512: + raise HTTPException(status_code=400, detail=f"Los embeddings generados no tienen 512 valores: {len(embeddings)}") + + # Almacenar los embeddings en Manticore Search + doc = { + "user_id": user_id, + "embeddings": embeddings + } + + # Crea la solicitud de inserción de documento + insert_request = InsertDocumentRequest(index="users", doc=doc) + + # Inserta el documento en Manticore Search + index_api.insert(insert_document_request=insert_request) + + # Eliminar el archivo temporal + os.remove(img_path) + + return {"status": "user registered successfully"} + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error registering user: {str(e)}") + +# Ruta para verificar una imagen +@app.post("/verify_user") +async def verify_user(image: UploadFile = File(...)): + try: + # Guardar la imagen temporalmente + img_path = f"/tmp/{image.filename}" + with open(img_path, "wb") as buffer: + shutil.copyfileobj(image.file, buffer) + + # Obtener los embeddings faciales de la imagen + query_embeddings = DeepFace.represent(img_path, model_name="ArcFace")[0]['embedding'] + + # Consulta en Manticore Search para obtener los usuarios registrados + query = { + "size": 100, # Ajusta el tamaño si tienes muchos usuarios + "query": { + "match_all": {} + } + } + + # Crear la solicitud de búsqueda + search_request = SearchRequest(index="users", query=query) + + # Realizar la búsqueda + result = search_api.search(search_request) + + # Recuperar todos los documentos (usuarios y sus embeddings) + hits = result.hits.hits + if not hits: + raise HTTPException(status_code=404, detail="No registered users found.") + + # Calcular similitud de coseno con cada usuario registrado + max_similarity = -1 + matched_user = None + for hit in hits: + stored_embeddings = hit['_source']['embeddings'] # Embeddings del usuario en la base de datos + similarity = cosine_similarity(query_embeddings, stored_embeddings) + + # Imprimir la similitud de cada usuario para depuración + print(f"Similitud con {hit['_source']['user_id']}: {similarity}") + + # Encontrar la mayor similitud + if similarity > max_similarity: + max_similarity = similarity + matched_user = hit['_source']['user_id'] + + # Establecer un umbral para decidir si hay coincidencia + similarity_threshold = 0.5 # Ajusta este valor según tus necesidades + if max_similarity < similarity_threshold: + raise HTTPException(status_code=404, detail="No matching user found") + + # Eliminar el archivo temporal + os.remove(img_path) + + return {"status": "success", "matched_user": matched_user, "similarity": max_similarity} + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error verifying user: {str(e)}") diff --git a/migrations/pacedeep_fastapi.sql b/migrations/pacedeep_fastapi.sql new file mode 100644 index 0000000..3bba5de --- /dev/null +++ b/migrations/pacedeep_fastapi.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS users; +CREATE TABLE users (user_id text, embeddings float_vector KNN_TYPE='hnsw' KNN_DIMS='512' HNSW_SIMILARITY='cosine'); diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..820cf4e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,74 @@ +absl-py==2.1.0 +annotated-types==0.7.0 +anyio==4.6.2.post1 +astunparse==1.6.3 +beautifulsoup4==4.12.3 +blinker==1.8.2 +certifi==2024.8.30 +charset-normalizer==3.4.0 +click==8.1.7 +deepface==0.0.93 +exceptiongroup==1.2.2 +fastapi==0.115.2 +filelock==3.16.1 +fire==0.7.0 +Flask==3.0.3 +Flask-Cors==5.0.0 +flatbuffers==24.3.25 +gast==0.6.0 +gdown==5.2.0 +google-pasta==0.2.0 +grpcio==1.67.0 +gunicorn==23.0.0 +h11==0.14.0 +h5py==3.12.1 +idna==3.10 +itsdangerous==2.2.0 +Jinja2==3.1.4 +joblib==1.4.2 +keras==3.6.0 +libclang==18.1.1 +lz4==4.3.3 +manticoresearch==4.0.0 +Markdown==3.7 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +ml-dtypes==0.4.1 +mtcnn==1.0.0 +namex==0.0.8 +numpy==1.26.4 +opencv-python==4.10.0.84 +opt_einsum==3.4.0 +optree==0.13.0 +packaging==24.1 +pandas==2.2.3 +pillow==11.0.0 +protobuf==4.25.5 +pydantic==2.9.2 +pydantic_core==2.23.4 +Pygments==2.18.0 +PySocks==1.7.1 +python-dateutil==2.9.0.post0 +python-multipart==0.0.12 +pytz==2024.2 +requests==2.32.3 +retina-face==0.0.17 +rich==13.9.2 +six==1.16.0 +sniffio==1.3.1 +soupsieve==2.6 +starlette==0.40.0 +tensorboard==2.17.1 +tensorboard-data-server==0.7.2 +tensorflow==2.17.0 +tensorflow-io-gcs-filesystem==0.37.1 +termcolor==2.5.0 +tf_keras==2.17.0 +tqdm==4.66.5 +typing_extensions==4.12.2 +tzdata==2024.2 +urllib3==2.2.3 +uvicorn==0.32.0 +Werkzeug==3.0.4 +wrapt==1.16.0