Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vector_search function for pipeline aggregation #30

Merged
merged 6 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions API/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,29 @@ def find_one_and_delete(self, collection, query):

def update_one(self, collection, query, update):
return self.db[collection].update_one(query, update)

# add a function for pipeline aggregation vector search
def vector_search(self, collection, embedding):

result = self.db[collection].aggregate([
{
"$vectorSearch": {
"index": "vector_index",
"path": "face_embedding",
"queryVector": embedding,
"numCandidates": 20,
"limit": 20
}
}, {
'$project': {
'_id': 0,
'Name': 1,
'Image': 1,
'score': {
'$meta': 'vectorSearchScore'
}
}
}
])
result_arr = [i for i in result]
return result_arr
Comment on lines 22 to +50

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting the query and projection into variables for better readability and maintainability. This practice enhances code clarity and simplifies future modifications.

def vector_search(self, collection, embedding):
    query = {
        "$vectorSearch": {
            "index": "vector_index",
            "path": "face_embedding",
            "queryVector": embedding,
            "numCandidates": 20,
            "limit": 20
        }
    }
    projection = {
        '$project': {
            '_id': 0, 
            'Name': 1,
            'Image': 1,
            'score': {'$meta': 'vectorSearchScore'}
        }
    }
    result = self.db[collection].aggregate([query, projection])
    return [i for i in result]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

41 changes: 40 additions & 1 deletion API/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@

from bson import ObjectId
from deepface import DeepFace
from fastapi import APIRouter, HTTPException, Response
from fastapi import APIRouter, HTTPException, Response, UploadFile, File
from matplotlib import pyplot as plt
from PIL import Image
from pydantic import BaseModel

from API.database import Database
from API.utils import init_logging_config
from dotenv import load_dotenv

load_dotenv()
init_logging_config()

MONGO_URI = os.getenv("MONGO_URL1")
router = APIRouter()


client = Database()
client2 = Database(MONGO_URI, "FaceRec")

collection = "faceEntries"
collection2 = "ImageDB"


# Models for the data to be sent and received by the server
Devasy23 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -267,3 +272,37 @@ async def delete_employees(EmployeeCode: int):
client.find_one_and_delete(collection, {"EmployeeCode": EmployeeCode})

return {"Message": "Successfully Deleted"}


@router.post("/recognize_face", response_class=Response)
async def recognize_face(Face: UploadFile = File(...)):
"""
Recognize a face from the provided image.

Args:
Face (UploadFile): The image file to be recognized.

Returns:
Response: A response object containing the recognized employee information in JSON format.

Raises:
HTTPException: If an internal server error occurs.
"""
logging.info("Recognizing Face")
try:
img_data = await Face.read()
with open("temp.png", "wb") as f:
f.write(img_data)

embedding = DeepFace.represent(img_path="temp.png", model_name="Facenet")
result = client2.vector_search(collection2, embedding[0]['embedding'])
logging.info(f"Result: {result}")
os.remove("temp.png")
except Exception as e:
logging.error(f"Error: {e}")
os.remove("temp.png")
raise HTTPException(status_code=500, detail="Internal server error")
return Response(
content=bytes(json.dumps(result[0], default=str), "utf-8"),
media_type="application/json",
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Temporary File Creation: Directly writing the uploaded image to a file (temp.png) can lead to concurrency issues and security concerns. Use a temporary file with a context manager to ensure it gets cleaned up properly, even in case of errors.
from tempfile import NamedTemporaryFile

async def recognize_face(Face: UploadFile = File(...)):
    logging.info("Recognizing Face")
    try:
        img_data = await Face.read()
        with NamedTemporaryFile(delete=True, suffix=".png") as temp_file:
            temp_file.write(img_data)
            temp_file.flush()
            embedding = DeepFace.represent(img_path=temp_file.name, model_name="Facenet")
            result = client2.vector_search(collection2, embedding[0]['embedding'])
    except Exception as e:
        logging.error(f"Error: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")
  • Error Handling: Current error handling might catch too broad of a range of exceptions, potentially swallowing unexpected errors and making debugging difficult. Be specific about which errors you catch or ensure to re-raise unexpected ones.

  • File Reading Directly in Endpoint: It's a good practice to separate out logic into service layers or utility functions. This aids in keeping your endpoint functions clean and more maintainable.

  • Use Environment Variables for file paths or model names to make the application more flexible and secure.

  • DRY Principle: Consider whether the pattern of removing a file is repeated elsewhere in your code. If so, abstract the cleanup logic into a utility function.

Overall, ensure every aspect adheres to scalability, security, and maintainability principles.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested changes has been made 🎉

15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,17 @@
- Resolved various bugs and issues identified during the testing process.

### Removed
- Removed deprecated code and unused dependencies from the project.
- Removed deprecated code and unused dependencies from the project.

## [0.1.4] - 2024-03-16 - 23:00

### Added
- Implemented a new `recognize_Face` endpoint in [`route.py`](API/route.py). This endpoint accepts a base64 string as input, converts it into embeddings, and performs a vector search query on the MongoDB Atlas database. Changes made by @Devasy23.
- Added a new `vector_search` function in [`database.py`](API/database.py). This function performs a vector similarity search on the MongoDB Atlas database using Euclidean distance as the similarity measure. Changes made by @Devasy23.
- Updated [`index.ipynb`](index.ipynb) to include examples and usage of the new `recognize_Face` endpoint and `vector_search` function. Changes made by @Devasy23.

### Changed
- Updated the `Database` class in [`database.py`](API/database.py) to include the new `vector_search` function. Changes made by @Devasy23.

### Fixed
- Resolved various bugs and issues identified during the implementation and testing of the new features. Fixes made by @Devasy23.
233 changes: 0 additions & 233 deletions Vector Search/index.ipynb

This file was deleted.

279 changes: 279 additions & 0 deletions index.ipynb

Large diffs are not rendered by default.

Loading