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"