File size: 12,802 Bytes
66ff2bc
 
 
 
 
 
 
 
 
 
 
 
e624310
66ff2bc
 
 
 
 
 
 
 
 
 
 
 
b7c0340
 
 
e8d631e
b7c0340
8406457
66ff2bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6525d1
66ff2bc
 
 
d6525d1
a99b378
 
66ff2bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e8d631e
66ff2bc
 
 
 
e8d631e
01108cd
e8d631e
66ff2bc
 
 
 
 
e8d631e
 
66ff2bc
 
 
 
 
e8d631e
66ff2bc
d6525d1
66ff2bc
e8d631e
66ff2bc
 
 
 
 
e8d631e
66ff2bc
e624310
 
d6525d1
e624310
d6525d1
e8d631e
 
 
 
01108cd
e624310
 
 
 
 
 
e8d631e
e624310
e8d631e
e624310
 
3587bdb
 
e624310
e8d631e
e624310
e8d631e
 
accaa5c
 
 
e8d631e
 
 
accaa5c
e8d631e
3587bdb
e8d631e
3587bdb
10afa34
e8d631e
b7c0340
3587bdb
 
e624310
 
e8d631e
e624310
 
e8d631e
e624310
 
 
d6525d1
 
e624310
 
 
e8d631e
e624310
 
e8d631e
d6525d1
e8d631e
e624310
d6525d1
8b07f8e
 
e8d631e
8b07f8e
 
 
e624310
 
8b07f8e
 
 
 
e8d631e
8b07f8e
accaa5c
8b07f8e
 
 
e8d631e
8b07f8e
e8d631e
 
 
 
8b07f8e
 
01108cd
8b07f8e
66ff2bc
e8d631e
66ff2bc
 
e8d631e
66ff2bc
 
e8d631e
01108cd
 
e8d631e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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"