Skip to content

Instantly share code, notes, and snippets.

@Karan071
Last active January 21, 2025 15:34
Show Gist options
  • Save Karan071/68c6a224b0d5c0c975871fb7cd8c8a6a to your computer and use it in GitHub Desktop.
Save Karan071/68c6a224b0d5c0c975871fb7cd8c8a6a to your computer and use it in GitHub Desktop.
Face Recognition
from fastapi import FastAPI, HTTPException, UploadFile, Form
from fastapi.middleware.cors import CORSMiddleware
from prisma import Prisma
from deepface import DeepFace
import numpy as np
from io import BytesIO
from PIL import Image, UnidentifiedImageError
import json
import os
import base64
import uvicorn
# Suppress TensorFlow warnings
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
app = FastAPI()
# Initialize Prisma Client
db = Prisma()
# CORS Settings
origins = ["https://localhost:5174", "https://localhost:5173"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
MODEL_NAME = "VGG-Face" # Model to use for embeddings
@app.on_event("startup")
async def startup():
await db.connect()
@app.on_event("shutdown")
async def shutdown():
await db.disconnect()
@app.get("/")
def backend_init():
return {"backend": "Backend running successfully :))"}
# Utility function to calculate cosine similarity
def calculate_cosine_similarity(embedding1, embedding2):
"""Calculate cosine similarity between two embeddings."""
embedding1 = np.array(embedding1)
embedding2 = np.array(embedding2)
return np.dot(embedding1, embedding2) / (np.linalg.norm(embedding1) * np.linalg.norm(embedding2))
def preprocess_image(photo_bytes: bytes):
"""Preprocess image: Validate, resize, and normalize."""
try:
# Open the image
photo = Image.open(BytesIO(photo_bytes))
# Validate image mode and convert if necessary
if photo.mode != "RGB":
photo = photo.convert("RGB")
# Resize the image to a standard size (e.g., 224x224) for better processing
photo = photo.resize((224, 224))
# Convert the image to a NumPy array
photo_array = np.array(photo)
return photo_array
except UnidentifiedImageError:
raise HTTPException(status_code=400, detail="Uploaded file is not a valid image")
def extract_face_embedding(photo_bytes: bytes):
"""Extract face embedding using DeepFace."""
try:
# Preprocess the image
photo_array = preprocess_image(photo_bytes)
# Debugging: Log image array shape
print(f"Processing image with shape: {photo_array.shape}")
# Extract embedding using DeepFace
embedding = DeepFace.represent(
img_path=photo_array, model_name=MODEL_NAME, enforce_detection=False
)
return embedding[0]["embedding"]
except Exception as e:
print(f"Error extracting embedding: {str(e)}") # Debugging log
raise HTTPException(status_code=500, detail=f"Error extracting embedding: {str(e)}")
async def save_to_db(table: str, name: str, age: int, gender: str, photo_base64: str, embedding: list):
"""Save data to the database using Prisma."""
embedding_json = json.dumps(embedding)
if table == "employee":
await db.employee.create(
data={
"name": name,
"age": age,
"gender": gender,
"photoBase64": photo_base64,
"embedding": embedding_json
}
)
elif table == "visitor":
await db.visitor.create(
data={
"name": name,
"age": age,
"gender": gender,
"photoBase64": photo_base64,
"embedding": embedding_json
}
)
@app.post("/register-employee/")
async def register_employee(
name: str = Form(...), age: int = Form(...), gender: str = Form(...), photo: UploadFile = None
):
try:
# Read and encode the photo
photo_bytes = await photo.read()
photo_base64 = base64.b64encode(photo_bytes).decode("utf-8")
# Extract embedding
embedding = extract_face_embedding(photo_bytes)
# Save to DB
await save_to_db("employee", name, age, gender, photo_base64, embedding)
return {"message": "Employee registered successfully"}
except Exception as e:
print(f"Error during employee registration: {str(e)}") # Debugging log
raise HTTPException(status_code=500, detail=f"Error during employee registration: {str(e)}")
@app.post("/register-visitor/")
async def register_visitor(
name: str = Form(...), age: int = Form(...), gender: str = Form(...), photo: UploadFile = None
):
try:
# Read and encode the photo
photo_bytes = await photo.read()
photo_base64 = base64.b64encode(photo_bytes).decode("utf-8")
# Extract embedding
embedding = extract_face_embedding(photo_bytes)
# Save to DB
await save_to_db("visitor", name, age, gender, photo_base64, embedding)
return {"message": "Visitor registered successfully"}
except Exception as e:
print(f"Error during visitor registration: {str(e)}") # Debugging log
raise HTTPException(status_code=500, detail=f"Error during visitor registration: {str(e)}")
@app.post("/recognize-employee/")
async def recognize_employee(photo: UploadFile):
try:
# Read the uploaded photo
photo_bytes = await photo.read()
uploaded_embedding = extract_face_embedding(photo_bytes)
# Fetch all employee embeddings from the database
employees = await db.employee.find_many()
if not employees:
return {"message": "No employees registered."}
# Compare embeddings and find the best match
best_match = None
highest_similarity = -1 # Start with the lowest similarity
for employee in employees:
stored_embedding = json.loads(employee.embedding) # Convert JSON string to list
similarity = calculate_cosine_similarity(uploaded_embedding, stored_embedding)
# Update the best match if the similarity is higher
if similarity > highest_similarity:
highest_similarity = similarity
best_match = employee
# Set a threshold for a valid match
if best_match and highest_similarity > 0.85: # Example threshold
return {"name": best_match.name, "similarity": highest_similarity}
else:
return {"message": "No match found."}
except Exception as e:
print(f"Error in employee recognition: {str(e)}") # Debugging log
raise HTTPException(status_code=500, detail=f"Error in face recognition: {str(e)}")
@app.post("/recognize-visitor/")
async def recognize_visitor(photo: UploadFile):
try:
# Read the uploaded photo
photo_bytes = await photo.read()
uploaded_embedding = extract_face_embedding(photo_bytes)
# Fetch all visitor embeddings from the database
visitors = await db.visitor.find_many()
if not visitors:
return {"message": "No visitors registered."}
# Compare embeddings and find the best match
best_match = None
highest_similarity = -1 # Start with the lowest similarity
for visitor in visitors:
stored_embedding = json.loads(visitor.embedding) # Convert JSON string to list
similarity = calculate_cosine_similarity(uploaded_embedding, stored_embedding)
# Update the best match if the similarity is higher
if similarity > highest_similarity:
highest_similarity = similarity
best_match = visitor
# Set a threshold for a valid match
if best_match and highest_similarity > 0.6: # Example threshold
return {"name": best_match.name, "similarity": highest_similarity}
else:
return {"message": "No match found."}
except Exception as e:
print(f"Error in visitor recognition: {str(e)}") # Debugging log
raise HTTPException(status_code=500, detail=f"Error in face recognition: {str(e)}")
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
from prisma import Prisma
db = Prisma()
async def connect_to_db():
try:
await db.connect()
print("Database connection successful!")
except Exception as e:
print(f"Failed to connect to the database: {str(e)}")
raise
async def disconnect_from_db():
try:
await db.disconnect()
print("Disconnected from the database.")
except Exception as e:
print(f"Error while disconnecting: {str(e)}")
services:
postgres:
image: postgres:15-alpine # Use the official Postgres image
container_name: postgres_db # Name of the container
ports:
- "5432:5432" # Expose the PostgreSQL port (default: 5432)
environment:
POSTGRES_USER: postgres # Username for PostgreSQL
POSTGRES_PASSWORD: postgres # Password for PostgreSQL
POSTGRES_DB: my_database # Name of the database to create on initialization
volumes:
- C:/Users/KaranChourasia/Desktop/deepface/backend/storage:/backend/storage
restart: always # Restart the container if it stops
# volumes:
# pgdata: ./backend/storage/
@Karan071
Copy link
Author

Major problems encountered:

The When using embeddings for facial recognition, a Vector Database can be very useful, especially for searching and retrieving embeddings efficiently.

In my current setup, the embeddings are stored as JSON strings in PostgreSQL, which is not optimized for similarity searches.

Reasons of the Recognition failure

  1. Json Format Issue: When retrieving embeddings from PostgreSQL , they are not parsed into Numpy arrays. that's why the cosine similarity calculations are failing.

  2. Similarity Search: Postgres is not ideal for vector similarity searches. It does not natively supporting efficient nearest neighbor searches for embedding which is crucial for facial recognization .

  3. Data preprocessing mismatch : We have to ensure the preprocessing of input images (size, normalization) is identical during both embedding generation and comparision.

Solution switch to Vector db for accurate Similarity search

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment