-
Notifications
You must be signed in to change notification settings - Fork 30
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
feat: Add Frontend Support for vector search function and improve face recognition #43
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feedback from Senior Dev Bot
API/database.py
Outdated
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The vector_search
function lacks input validation and error handling, and the function name could be more descriptive. Consider validating the inputs and handling potential exceptions.
def search_by_embedding(self, collection, embedding):
if not collection or not embedding:
raise ValueError("Collection and embedding are required.")
# rest of the code...
}); | ||
}); | ||
|
||
$(document).ready(function () { | ||
$("#captureButton1").on("click", function () { | ||
$.ajax({ | ||
type: "POST", | ||
url: "/capturing", | ||
success: function (response) { | ||
console.log(response) | ||
updateImage(); | ||
enableImage(); | ||
}, | ||
error: function (error) { | ||
console.error(error); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
|
||
|
||
|
||
|
||
|
||
function updateImage(){ | ||
var img = document.getElementById('Image'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The code changes are functional, but they lack readability. It's better to remove the excessive empty lines. Also, consider using arrow functions for brevity and consistency. Ensure error handling is consistent throughout the codebase.
$(document).ready(() => {
$("#captureButton1").on("click", () => {
$.ajax({
type: "POST",
url: "/capturing",
success: (response) => {
console.log(response);
updateImage();
enableImage();
},
error: (error) => {
console.error(error);
}
});
});
});
function updateImage() {
var img = document.getElementById('Image');
}
FaceRec/templates/edit.html
Outdated
<div style="padding-top: 10px;"> | ||
<select id="Gender" name="Gender" class="form-select form-select-lg mb-3 bg-primary.bg-gradient" aria-label=".form-select-lg example" value="{{employee_data.gender}}" required> | ||
|
||
<option value="Male" class="dropdown-item active">Male</option> | ||
<option value="Male" class="dropdown-item ">Male</option> | ||
<option value="Female" class="dropdown-item" >Female</option> | ||
<option value="Other" class="dropdown-item">Other</option> | ||
</select><br><br></div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The use of inline styles is not ideal. It's better to separate the CSS from the HTML for maintainability. Also, the use of classes like "dropdown-item" appears to be Bootstrap specific, but it's being used in a select element. Consider using appropriate classes or styles for the select element.
<style>
.select-wrapper {
padding-top: 10px;
}
</style>
<div class="select-wrapper">
<select id="Gender" name="Gender" class="form-select form-select-lg mb-3 bg-primary.bg-gradient" aria-label=".form-select-lg example" value="{{employee_data.gender}}" required>
<option value="Male">Male</option>
<option value="Female">Female</option>
<option value="Other">Other</option>
</select>
</div>
var uploadElement = document.getElementById('Upload'); | ||
uploadElement.removeAttribute('hidden'); | ||
} | ||
|
||
|
||
myButton.addEventListener("click", function () { | ||
myPopup.classList.add("show"); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The code could be improved by using modern JavaScript conventions such as const
and addEventListener
for event handling. Additionally, consistent spacing and indentation will improve readability.
const uploadElement = document.getElementById('Upload');
uploadElement.removeAttribute('hidden');
myButton.addEventListener('click', () => {
myPopup.classList.add('show');
});
FaceRec/templates/edit.html
Outdated
<div style="padding-top: 10px;"> | ||
|
||
<select id="Department" name="Department" class="form-select form-select-lg mb-3 bg-primary.bg-gradient" aria-label=".form-select-lg example"value={{employee_data.Department}} required> | ||
<option value="Software" class="dropdown-item active">Software</option> | ||
<option value="Software" class="dropdown-item ">Software</option> | ||
<option value="BPO/KPO" class="dropdown-item" >BPO/KPO</option> | ||
<option value="Other" class="dropdown-item">Other</option> | ||
</select><br><br></div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The changes look good, but let's separate the styling from the HTML. Also, let's ensure proper indentation and follow accessibility best practices by providing labels for the select element.
<div style="padding-top: 10px;">
<label for="Department">Department:</label>
<select id="Department" name="Department" class="form-select form-select-lg mb-3 bg-primary.bg-gradient" aria-label=".form-select-lg example" value={{employee_data.Department}} required>
<option value="Software" class="dropdown-item">Software</option>
<option value="BPO/KPO" class="dropdown-item">BPO/KPO</option>
<option value="Other" class="dropdown-item">Other</option>
</select>
</div>
FaceRec/app/main/Employee.py
Outdated
# logger.info(resp.status_code) | ||
# logger.info(resp.json()) | ||
employees = resp.json() | ||
|
||
except requests.exceptions.RequestException as e: | ||
print(f"Request failed: {e}") | ||
return render_template("table.html", employees=employees) | ||
|
||
|
||
# To add employee record | ||
@flk_blueprint.route("/Add_employee") | ||
def add_employee(): | ||
return render_template("index.html") | ||
|
||
|
||
# To submit the form data to server and save it in database | ||
@flk_blueprint.route("/submit_form", methods=["POST"]) | ||
def submit_form(): | ||
|
||
Employee_Code = request.form["EmployeeCode"] | ||
Name = request.form["Name"] | ||
gender = request.form["Gender"] | ||
Department = request.form["Department"] | ||
|
||
if request.files["File"]: | ||
if "File" not in request.files: | ||
return jsonify({"message": "No file part"}), 400 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
It's important to handle exceptions and errors properly. Consider logging the exception instead of printing it and returning a generic 500 status code.
except requests.exceptions.RequestException as e:
logger.error(f"Request failed: {e}")
return render_template("error.html"), 500
Also, the file upload handling logic seems incorrect. It's better to reorganize the condition and return the response for the missing file error outside the file existence check.
if "File" not in request.files:
return jsonify({"message": "No file part"}), 400
if request.files["File"]:
# handle file upload
FaceRec/app/main/Employee.py
Outdated
"Image": encoded_image, | ||
} | ||
url = "http://127.0.0.1:8000/create_new_faceEntry" | ||
try: | ||
resp = requests.post( | ||
url=url, | ||
json={ | ||
"EmployeeCode": 134, | ||
"Name": "Name", | ||
"gender": "gender", | ||
"Department": "Department", | ||
"Image": "your_image", | ||
}, | ||
) | ||
resp.status_code | ||
except requests.exceptions.RequestException as e: | ||
print(f"Request failed: {e}") | ||
payload.status_code | ||
# try: | ||
# resp = requests.post( | ||
# url=url, | ||
# json={ | ||
# "EmployeeCode": 134, | ||
# "Name": "Name", | ||
# "gender": "gender", | ||
# "Department": "Department", | ||
# "Image": "your_image", | ||
# }, | ||
# ) | ||
# resp.status_code | ||
# except requests.exceptions.RequestException as e: | ||
# print(f"Request failed: {e}") | ||
jsonify({"message": "Successfully executed"}) | ||
print("Executed.") | ||
if resp.status_code == 200: | ||
if payload.status_code == 200: | ||
return redirect("DisplayingEmployees") | ||
else: | ||
return jsonify({"message": "Failed to execute"}) | ||
|
||
|
||
# To edit an employee details | ||
@flk_blueprint.route("/edit/<int:EmployeeCode>", methods=["POST", "GET"]) | ||
def edit(EmployeeCode): | ||
if request.method == "POST": | ||
Name = request.form["Name"] | ||
gender = request.form["Gender"] | ||
Department = request.form["Department"] | ||
with open(Config.image_data_file, "r") as file: | ||
image_data = json.load(file) | ||
encoded_image = image_data.get("base64_image", "") | ||
payload = { | ||
"Name": Name, | ||
"gender": gender, | ||
"Department": Department, | ||
"Image": encoded_image, | ||
} | ||
# logger.info(payload) | ||
try: | ||
url = requests.put( | ||
f"http://127.0.0.1:8000/update/{EmployeeCode}", json=payload | ||
) | ||
url.status_code | ||
# logger.info(url.json()) | ||
|
||
return redirect("/") | ||
|
||
except requests.exceptions.RequestException as e: | ||
print(f"Request failed: {e}") | ||
response = requests.get(f"http://127.0.0.1:8000/read/{EmployeeCode}") | ||
# logger.info(response.status_code) | ||
# logger.info(response.json()) | ||
if response.status_code == 200: | ||
employee_data = response.json() | ||
return render_template("edit.html", employee_data=employee_data) | ||
else: | ||
return f"Error {response.status_code}: Failed to retrieve employee data." | ||
|
||
|
||
|
||
|
||
# To delete employee details | ||
@flk_blueprint.route("/Delete/<int:EmployeeCode>", methods=["DELETE", "GET"]) | ||
def Delete(EmployeeCode): | ||
# logger.info(employees) | ||
response = requests.delete(f"http://127.0.0.1:8000/delete/{EmployeeCode}") | ||
jsonify(response.json()) | ||
|
||
return redirect("/DisplayingEmployees") | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
In the first block, the commented-out code for making a POST request should be removed entirely. Besides, the second block uses 'url.status_code' instead of 'requests.put' for making a PUT request. Avoid unnecessary comments and handle exceptions appropriately. Also, consider using the response directly rather than calling 'response.json()' and 'jsonify(response.json())'.
# Remove commented-out code
# Replace 'url.status_code' with requests.put
resp = requests.put(
f"http://127.0.0.1:8000/update/{EmployeeCode}", json=payload
)
# Use response directly
return jsonify(response.json())
API/route.py
Outdated
plt.imsave(f"Images/Faces/{Name}.jpg", face_image_data[0]["face"]) | ||
logging.info(f"Face saved {Name}") | ||
embedding = DeepFace.represent( | ||
image_filename, model_name="Facenet", detector_backend="mtcnn" | ||
image_filename, model_name="Facenet512", detector_backend="mtcnn" | ||
) | ||
embeddings.append(embedding) | ||
logging.info(f"Embedding created Embeddings for {Name}") | ||
os.remove(image_filename) | ||
|
||
logging.debug(f"About to insert Embeddings: {embeddings}") | ||
# Store the data in the database | ||
client.insert_one( | ||
collection, | ||
client2.insert_one( | ||
collection2, | ||
{ | ||
"EmployeeCode": EmployeeCode, | ||
"Name": Name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The code changes look good. Consider refactoring the code for better readability and modularity. Also, it's a good practice to handle potential errors when saving the face image and inserting into the database.
# Refactored code for better readability and error handling
save_face_image(f"Images/Faces/{Name}.jpg", face_image_data[0]["face"])
log_info(f"Face saved {Name}")
embedding = create_embedding(image_filename)
embeddings.append(embedding)
log_info(f"Embedding created for {Name}")
delete_temp_image(image_filename)
log_debug(f"About to insert Embeddings: {embeddings}")
client2.insert_one(collection2, {"EmployeeCode": EmployeeCode, "Name": Name, ...})
API/route.py
Outdated
|
||
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("mongodb+srv://devansh:[email protected]/?retryWrites=true&w=majority&appName=Devasy23", "FaceRec") | ||
|
||
collection = "faceEntries" | ||
collection2 = "ImageDB" | ||
|
||
|
||
# Models for the data to be sent and received by the server |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The addition of the dotenv
package and loading environment variables is a good practice. However, it's important to handle the import of the os
package. Also, using descriptive names for variables is essential for readability.
import os
from dotenv import load_dotenv
load_dotenv()
MONGO_URI = os.getenv("MONGO_URL1")
client = Database()
second_client = Database("mongodb+srv://devansh:[email protected]/?retryWrites=true&w=majority&appName=Devasy23", "FaceRec")
API/route.py
Outdated
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="Facenet512", detector_backend="mtcnn") | ||
result = client2.vector_search(collection2, embedding[0]['embedding']) | ||
logging.info(f"Result: {result[0]['Name']}, {result[0]['score']}") | ||
os.remove("temp.png") | ||
if result[0]['score'] < 0.5: | ||
return Response(status_code=404, content=json.dumps({"message": "No match found"})) | ||
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", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE REVIEW
The recognize_face
function should be refactored to improve readability and maintainability. Consider breaking down the logic into smaller functions, using descriptive variable names, and applying error handling for file removal. Also, consider using context managers for file operations.
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.
"""
try:
temp_image_path = "temp.png"
await save_uploaded_image(Face, temp_image_path)
embedding = extract_embedding(temp_image_path)
result = search_for_matching_vector(embedding)
remove_temp_image(temp_image_path)
handle_matching_result(result)
except Exception as e:
handle_recognition_error(e)
async def save_uploaded_image(file: UploadFile, file_path: str) -> None:
async with aiofiles.open(file_path, "wb") as temp_image:
data = await file.read()
await temp_image.write(data)
def extract_embedding(image_path: str):
with open(image_path, "rb") as image_file:
embedding = DeepFace.represent(img_path=image_path, model_name="Facenet512", detector_backend="mtcnn")
return embedding
def search_for_matching_vector(embedding):
result = client2.vector_search(collection2, embedding[0]['embedding'])
return result
def remove_temp_image(file_path: str):
os.remove(file_path)
def handle_matching_result(result):
if result[0]['score'] < 0.5:
return Response(status_code=404, content=json.dumps({"message": "No match found"}))
def handle_recognition_error(error):
remove_temp_image("temp.png")
raise HTTPException(status_code=500, detail="Internal server error")
|
GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
---|---|---|---|---|---|
11383748 | Triggered | MongoDB Credentials | 096aba7 | API/route.py | View secret |
11383748 | Triggered | MongoDB Credentials | 1362215 | API/route.py | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
API/route.py
Outdated
router = APIRouter() | ||
|
||
|
||
client = Database() | ||
client2 = Database("mongodb+srv://devansh:[email protected]/?retryWrites=true&w=majority&appName=Devasy23", "FaceRec") |
Check failure
Code scanning / SonarCloud
MongoDB database passwords should not be disclosed
<title>Capture image</title> | ||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script> | ||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
Check warning
Code scanning / CodeQL
Inclusion of functionality from an untrusted source Medium
url = requests.put( | ||
f"http://127.0.0.1:8000/update/{EmployeeCode}", json=payload | ||
) |
Check failure
Code scanning / CodeQL
Partial server-side request forgery Critical
user-provided value
|
||
except requests.exceptions.RequestException as e: | ||
print(f"Request failed: {e}") | ||
response = requests.get(f"http://127.0.0.1:8000/read/{EmployeeCode}") |
Check failure
Code scanning / CodeQL
Partial server-side request forgery Critical
user-provided value
Quality Gate failedFailed conditions See analysis details on SonarCloud Catch issues before they fail your Quality Gate with our IDE extension SonarLint |
This pull request adds a new
vector_search
function to thedatabase.py
file, which performs a pipeline aggregation vector search on the MongoDB Atlas database. It also improves the face recognition code by refactoring it to use the Facenet512 model for better accuracy. Additionally, error handling has been added for cases where no match is found in face recognition. The pull request also includes code for therecognize_face
API.