npuliga's picture
updated class names
10afa34
import numpy as np
import cv2
from matplotlib import pyplot as plt
import torch
# In the below line,remove '.' while working on your local system. However Make sure that '.' is present before face_recognition_model while uploading to the server, Do not remove it.
from .face_recognition_model import *
from PIL import Image
import base64
import io
import os
import joblib
import pickle
from sklearn.preprocessing import StandardScaler, normalize
# Add more imports if required
###########################################################################################################################################
# Caution: Don't change any of the filenames, function names and definitions #
# Always use the current_path + file_name for refering any files, without it we cannot access files on the server #
###########################################################################################################################################
# Current_path stores absolute path of the file from where it runs.
current_path = os.path.dirname(os.path.abspath(__file__))
# Simple error handling for missing models
def handle_missing_model(error_msg):
"""Helper function to handle missing model files"""
print(f"ERROR: {error_msg}")
return "Unknown"
#1) The below function is used to detect faces in the given image.
#2) It returns only one image which has maximum area out of all the detected faces in the photo.
#3) If no face is detected,then it returns zero(0).
def detected_face(image):
eye_haar = current_path + '/haarcascade_eye.xml'
face_haar = current_path + '/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(face_haar)
eye_cascade = cv2.CascadeClassifier(eye_haar)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
face_areas=[]
images = []
required_image = None
if len(faces) == 0:
return None
for i, (x,y,w,h) in enumerate(faces):
face_cropped = gray[y:y+h, x:x+w]
face_areas.append(w*h)
images.append(face_cropped)
if face_areas:
max_area_idx = np.argmax(face_areas)
required_image = images[max_area_idx]
required_image = Image.fromarray(required_image)
return required_image
return None
#1) Images captured from mobile is passed as parameter to the below function in the API call. It returns the similarity measure between given images.
#2) The image is passed to the function in base64 encoding, Code for decoding the image is provided within the function.
#3) Define an object to your siamese network here in the function and load the weight from the trained network, set it in evaluation mode.
#4) Get the features for both the faces from the network and return the similarity measure, Euclidean,cosine etc can be it. But choose the Relevant measure.
#5) For loading your model use the current_path+'your model file name', anyhow detailed example is given in comments to the function
#Caution: Don't change the definition or function name; for loading the model use the current_path for path example is given in comments to the function
def get_similarity(img1, img2):
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
det_img1 = detected_face(img1)
det_img2 = detected_face(img2)
if(det_img1 is None or det_img2 is None):
det_img1 = Image.fromarray(cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY))
det_img2 = Image.fromarray(cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY))
face1 = trnscm(det_img1).unsqueeze(0)
face2 = trnscm(det_img2).unsqueeze(0)
try:
# Load the Siamese model
model = SiameseNetwork().to(device)
model_path = current_path + '/siamese_model.t7'
if os.path.exists(model_path):
ckpt = torch.load(model_path, map_location=device)
model.load_state_dict(ckpt['net_dict'])
model.eval()
# Move tensors to device
face1 = face1.to(device)
face2 = face2.to(device)
# Get dissimilarity using Euclidean distance (same as notebook)
with torch.no_grad():
output1, output2 = model(face1, face2)
euclidean_distance = torch.nn.functional.pairwise_distance(output1, output2)
# Return raw distance as dissimilarity (lower = more similar)
dissimilarity = euclidean_distance.item()
return dissimilarity
else:
print(f"Model file not found: {model_path}")
return 0.0
except Exception as e:
print(f"Error in similarity calculation: {str(e)}")
return 0.0
#1) Image captured from mobile is passed as parameter to this function in the API call, It returns the face class in the string form ex: "Person1"
#2) The image is passed to the function in base64 encoding, Code to decode the image provided within the function
#3) Define an object to your network here in the function and load the weight from the trained network, set it in evaluation mode
#4) Perform necessary transformations to the input(detected face using the above function).
#5) Along with the siamese, you need the classifier as well, which is to be finetuned with the faces that you are training
##Caution: Don't change the definition or function name; for loading the model use the current_path for path example is given in comments to the function
def get_face_class(img1):
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"LOG: Using device: {device}")
det_img1 = detected_face(img1)
if(det_img1 is None):
det_img1 = Image.fromarray(cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY))
print("LOG: No face detected, using full image")
else:
print("LOG: Face detected successfully")
try:
# Load the Siamese model for feature extraction
model = SiameseNetwork().to(device)
model_path = current_path + '/siamese_model.t7'
print(f"LOG: Looking for model at: {model_path}")
print(f"LOG: Model file exists: {os.path.exists(model_path)}")
if os.path.exists(model_path):
ckpt = torch.load(model_path, map_location=device)
model.load_state_dict(ckpt['net_dict'])
model.eval()
print("LOG: Siamese model loaded successfully")
# Preprocess the image
face_tensor = trnscm(det_img1).unsqueeze(0).to(device)
print(f"LOG: Face tensor shape: {face_tensor.shape}")
# Extract features using Siamese network
with torch.no_grad():
features = model.forward_once(face_tensor)
features_np = features.cpu().numpy()
print(f"LOG: Extracted features shape: {features_np.shape}")
# Load the SVM classifier, scaler, and normalization flag
classifier_path = current_path + '/face_recognition_classifier.pkl'
scaler_path = current_path + '/face_recognition_scaler.pkl'
norm_flag_path = current_path + '/normalization_flag.pkl'
print(f"LOG: Looking for classifier files:")
print(f"LOG: Classifier: {os.path.exists(classifier_path)}")
print(f"LOG: Scaler: {os.path.exists(scaler_path)}")
print(f"LOG: Normalization flag: {os.path.exists(norm_flag_path)}")
# Load normalization flag (default to False if not found)
needs_normalization = False
if os.path.exists(norm_flag_path):
try:
with open(norm_flag_path, 'rb') as f:
needs_normalization = pickle.load(f)
print(f"LOG: Normalization flag loaded: {needs_normalization}")
except Exception as e:
print(f"WARN: Could not load normalization flag: {e}")
# Try to load the classifier
classifier = None
try:
if os.path.exists(classifier_path):
print("LOG: Loading SVM classifier...")
classifier = joblib.load(classifier_path)
print(f"LOG: SVM classifier loaded successfully")
print(f"LOG: Classifier type: {type(classifier).__name__}")
# Debug: Check the actual number of classes in the SVM
if hasattr(classifier, 'classes_'):
print(f"LOG: SVM trained classes: {classifier.classes_}")
print(f"LOG: Number of SVM classes: {len(classifier.classes_)}")
print(f"LOG: Class labels: {list(classifier.classes_)}")
else:
print("WARN: Classifier doesn't have classes_ attribute")
else:
print("ERROR: No classifier file found!")
return "Unknown"
except Exception as classifier_error:
print(f"ERROR: Failed to load classifier: {classifier_error}")
return handle_missing_model("Failed to load classifier")
if classifier is not None:
# Apply L2 normalization if needed
if needs_normalization:
print("LOG: Applying L2 normalization to features")
features_normalized = normalize(features_np, norm='l2')
else:
print("LOG: Using features without L2 normalization")
features_normalized = features_np
# Load and apply the scaler (only if it exists and is not None)
if os.path.exists(scaler_path):
scaler = joblib.load(scaler_path)
# Check if scaler is None (indicates it wasn't used during training)
if scaler is not None:
features_scaled = scaler.transform(features_normalized)
print("LOG: Features scaled using saved StandardScaler")
else:
features_scaled = features_normalized
print("LOG: StandardScaler not used (embeddings are L2 normalized)")
else:
print("WARN: Scaler file not found. Using unscaled features.")
features_scaled = features_normalized
# Make prediction (predict returns the class label, not the index into classes_)
predicted_label = int(classifier.predict(features_scaled)[0])
print(f"LOG: Predicted label: {predicted_label}")
# Get confidence scores if available; probabilities are aligned with classes_ indices
confidence = None
if hasattr(classifier, 'predict_proba'):
probabilities = classifier.predict_proba(features_scaled)[0]
if hasattr(classifier, 'classes_'):
try:
class_index = int(np.where(classifier.classes_ == predicted_label)[0][0])
except Exception as e:
print(f"WARN: Could not map label to classes_ index ({e}); using argmax as fallback")
class_index = int(np.argmax(probabilities))
else:
# Assume labels are 0..N-1 aligned with probability order
class_index = predicted_label if 0 <= predicted_label < len(probabilities) else int(np.argmax(probabilities))
confidence = float(probabilities[class_index])
print(f"LOG: Prediction confidence: {confidence:.3f} ({confidence*100:.1f}%)")
# Map predicted label to team member name
from .face_recognition_model import classes
predicted_class = classes[predicted_label]
if confidence is not None:
return f"{predicted_class} (confidence: {confidence:.3f})"
else:
return predicted_class
else:
print("ERROR: Failed to load classifier!")
return "Unknown"
else:
print(f"ERROR: Model file not found: {model_path}")
return "Unknown"
except Exception as e:
print(f"ERROR: Exception in face classification: {str(e)}")
import traceback
traceback.print_exc()
return "Unknown"