enniorampello commited on
Commit
3322c73
·
1 Parent(s): 3ab67fa
Files changed (8) hide show
  1. .gitignore +10 -0
  2. README.md +32 -1
  3. app.py +69 -0
  4. create_bucket.py +16 -0
  5. feature_pipeline.py +81 -0
  6. inference-pipeline.py +71 -0
  7. requirements.txt +11 -0
  8. training_pipline.py +46 -0
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ credentials.json
3
+ cmake*
4
+ __pycache__/
5
+ .inference*
6
+ encodings.pkl
7
+ labels.pkl
8
+ model.pkl
9
+ dlib
10
+ flagged
README.md CHANGED
@@ -10,4 +10,35 @@ pinned: false
10
  license: mit
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  license: mit
11
  ---
12
 
13
+ # Face Recognition ML pipeline
14
+ This project is composed by three different components: feature pipeline, model retraining and inference pipeline. The pipeline diagram can be seen below.
15
+ ![Interactive UI for inference (1)](https://user-images.githubusercontent.com/48623568/211796472-e0f8f9de-2b70-4ebb-9cff-bf8a6386f3cb.jpg)
16
+
17
+ ## Feature pipeline
18
+
19
+ This pipeline is hosted on a GCP (Google Cloud Platform) instance and performs the following tasks:
20
+ 1. Receive an image from the user (from a Gradio UI).
21
+ 2. Create multiple different versions of the images by applying filters, rotating it, brighten it, etc...
22
+ 3. For each of the resulting images, generate an embedding using the [Histogram of Gradients (HOG)](https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients) method
23
+ 4. Upload the original image and the embeddings to a GCP bucket.
24
+ 5. Trigger model retraining.
25
+
26
+ ## Model retraining
27
+
28
+ Once the training pipeline is called, the embeddings are taken from the bucket, and the training of the KNN classifier is performed. The model is then stored on the local device as our inference pipeline uses the same GCP instance to run the inference.
29
+
30
+ ## Inference pipeline
31
+
32
+ Our inference pipeline is in real-time, hence the Gradio UI shows the live webcam with all the predictions on each face. Note: This is not a very well trained model, and hence there would be several false positives.
33
+
34
+
35
+ ## Gradio public links:
36
+ - The app to upload an image is provided [here](https://1d5d09cf-5ef3-47f6.gradio.live/)
37
+ - The app to run the real-time inference is [here](https://c3c30239-c028-48a3.gradio.live/)
38
+ ## Project structure
39
+
40
+ - The dataset is stored in `./dataset/<first name of the person>`.
41
+ - The feature pipeline creates a 128d vector for each image in the dataset folder along with some augmentations. Once this is done, the encodings are stored in the file `./encodings.pkl`.
42
+ - The training pipeline then makes use of the encoding file and trains a knn classifier and stores the model in the file `./model.pkl`. The training file also stores the labels (before using a label encoder) in the file 'labels.pkl'.
43
+ - The inference pipeline makes use of all these files and runs the inference in the gradio UI.
44
+
app.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ import gradio as gr
4
+
5
+ from google.cloud import storage
6
+ from google.oauth2 import service_account
7
+
8
+ from feature_pipeline import preprocess
9
+ from training_pipline import train
10
+
11
+ use_bucket = True
12
+
13
+ creds = service_account.Credentials.from_service_account_info({
14
+ "type": "service_account",
15
+ "project_id": "scalable-ml-lab-2",
16
+ "private_key_id": os.environ.get("GCP_PRIVATE_KEY_ID"),
17
+ "private_key": os.environ.get("GCP_PRIVATE_KEY"),
18
+ "client_email": os.environ.get("GCP_CLIENT_EMAIL"),
19
+ "client_id": os.environ.get("GCP_CLIENT_ID"),
20
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
21
+ "token_uri": "https://oauth2.googleapis.com/token",
22
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
23
+ "client_x509_cert_url": f"https://www.googleapis.com/robot/v1/metadata/x509/613127572368-compute%40developer.gserviceaccount.com"
24
+ })
25
+ client = storage.Client(credentials=creds)
26
+
27
+ def upload_to_bucket(client, bucket_name, file_name, object_name):
28
+ try:
29
+ bucket = client.bucket(bucket_name)
30
+ blob = bucket.blob(object_name)
31
+ blob.upload_from_filename(file_name)
32
+
33
+ return True
34
+ except Exception as e:
35
+ print(e)
36
+ return False
37
+
38
+
39
+ def image_upload(_, img, name):
40
+ file_id = random.randint(0, 1000000)
41
+
42
+ if os.path.exists(f"./dataset/{name}") == False:
43
+ os.mkdir(f"./dataset/{name}")
44
+
45
+ img.save(f"dataset/{name}/{name}_{file_id}.jpg")
46
+
47
+ embeddings_filename = preprocess()
48
+
49
+ if use_bucket:
50
+ upload_to_bucket(client, "bucket-faces", f"./dataset/{name}/{name}_{file_id}.jpg", f"{name}/{name}_{file_id}.jpg")
51
+ upload_to_bucket(client, "bucket-embeddings", embeddings_filename, embeddings_filename)
52
+
53
+ train(client, "bucket-embeddings", embeddings_filename)
54
+
55
+ return "Model retrained!"
56
+
57
+
58
+ gr.Interface(
59
+ image_upload,
60
+ [
61
+ gr.Markdown("""
62
+ # Hello!! Enter your first name and upload one picture of your face.
63
+ ## The face recognition model will be retrained with the knowledge you gave it of your face.
64
+ """),
65
+ gr.Webcam(source="webcam", type="pil", label="Upload a beautiful picture of yourself."),
66
+ gr.Textbox(placeholder="write your name here...", label="Your name.")
67
+ ],
68
+ outputs="text"
69
+ ).launch(server_name="0.0.0.0", share=True)
create_bucket.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from google.cloud import storage
2
+ from google.oauth2 import service_account
3
+
4
+ creds = service_account.Credentials.from_service_account_file("credentials.json")
5
+ client = storage.Client(credentials=creds)
6
+
7
+ def create_bucket(bucket_name):
8
+ try:
9
+ bucket = client.create_bucket(bucket_name)
10
+ print(f"Bucket {bucket.name} created")
11
+ return True
12
+ except Exception as e:
13
+ print(e)
14
+ return False
15
+
16
+ create_bucket("bucket-embeddings")
feature_pipeline.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import imutils
3
+ from imutils import paths
4
+ import face_recognition
5
+ import pickle
6
+ import cv2
7
+ import os
8
+ import argparse
9
+
10
+ def augment_image(file_path):
11
+ """
12
+ Augment the image by rotating it by 10, -10, 30, -30 degrees, flipping it horizontally and increasing/decreasing the brightness
13
+
14
+ params:
15
+ file_path: path to the image file
16
+
17
+ returns:
18
+ list of 8 augmented images
19
+ """
20
+ image = cv2.imread(file_path)
21
+ images = []
22
+
23
+ # convert the input image from BGR to RGB
24
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
25
+
26
+ # add the original image to the list of images
27
+ images.append(rgb_image)
28
+ # rotate the image by 10 degrees and add it to the list of images
29
+ images.append(imutils.rotate_bound(rgb_image, 10))
30
+ # rotate the image by -10 degrees and add it to the list of images
31
+ images.append(imutils.rotate_bound(rgb_image, -10))
32
+ # rotate the image by 30 degrees and add it to the list of images
33
+ images.append(imutils.rotate_bound(rgb_image, 30))
34
+ # rotate the image by -30 degrees and add it to the list of images
35
+ images.append(imutils.rotate_bound(rgb_image, -30))
36
+ # flip the image horizontally and add it to the list of images
37
+ images.append(cv2.flip(rgb_image, 1))
38
+ #increase the brightness of the image and add it to the list of images
39
+ images.append(cv2.convertScaleAbs(rgb_image, alpha=1.5, beta=0))
40
+ #decrease the brightness of the image and add it to the list of images
41
+ images.append(cv2.convertScaleAbs(rgb_image, alpha=0.5, beta=0))
42
+
43
+ return images
44
+
45
+
46
+ def preprocess():
47
+ dataset = './dataset' # path to input dataset directory
48
+ encodings_file = './encodings.pkl' #path to output encodings file
49
+
50
+ imagePaths = list(paths.list_images(dataset))
51
+ known_encodings = []
52
+ known_names = []
53
+ s = time.time()
54
+ print(imagePaths)
55
+ for i, imagePath in enumerate(imagePaths):
56
+ images = augment_image(imagePath)
57
+ name = imagePath.split(os.path.sep)[-2]
58
+ print(f"processing image [{name}] {i+1}/{len(imagePaths)}")
59
+
60
+ for l in images:
61
+ boxes = face_recognition.face_locations(l, model='hog')
62
+ encodings = face_recognition.face_encodings(l, boxes)
63
+
64
+ # creating the training set
65
+ for encoding in encodings:
66
+ known_encodings.append(encoding)
67
+ known_names.append(name)
68
+
69
+ e = time.time()
70
+ print(f"Encoding dataset took: {(e-s)} seconds")
71
+ data = {"encodings": known_encodings, "names": known_names}
72
+ f = open(encodings_file, "wb")
73
+ f.write(pickle.dumps(data))
74
+ f.close()
75
+
76
+ return encodings_file
77
+
78
+ # replace the above few lines with the script to upload this to hopsworks
79
+
80
+ if __name__ == '__main__':
81
+ preprocess()
inference-pipeline.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import the necessary packages
2
+ import face_recognition
3
+ import pickle
4
+ import argparse
5
+ import cv2
6
+ from sklearn.neighbors import KNeighborsClassifier
7
+ import gradio as gr
8
+ import numpy as np
9
+
10
+
11
+ def inf(_, image):
12
+ # input_image = 'multiple.jpg'
13
+ # encodings_file = 'encodings.pkl'
14
+ detection_method = 'hog'
15
+ classifier_model_file = 'model.pkl'
16
+ labels_file = 'labels.pkl'
17
+ # data = pickle.loads(open(encodings_file, "rb").read())
18
+
19
+ # image = cv2.imread(input_image)
20
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
21
+
22
+ boxes = face_recognition.face_locations(rgb_image, model=detection_method)
23
+ encodings = face_recognition.face_encodings(rgb_image, boxes)
24
+ print(f'Found {len(boxes)} faces')
25
+
26
+ if len(boxes) == 0:
27
+ return image
28
+ # initialize the list of names for each face detected
29
+ names = []
30
+
31
+ # # load the model from disk
32
+ model = pickle.loads(open(classifier_model_file, "rb").read())
33
+ # predict on the new encodings and display the probabilities
34
+ y_pred = model.predict_proba(encodings)
35
+ # print(y_pred)
36
+ max_y = y_pred.max(axis=1)
37
+ print(max_y)
38
+ labels = pickle.loads(open(labels_file, "rb").read())
39
+ for id, m in enumerate(max_y):
40
+
41
+ if m >= 0.8:
42
+ names.append(labels[y_pred.argmax(axis=1)[id]])
43
+ else:
44
+ names.append('Unknown')
45
+
46
+
47
+ # loop over the recognized faces
48
+ for ((top, right, bottom, left), name) in zip(boxes, names):
49
+ # draw the predicted face name on the image
50
+ cv2.rectangle(image, (left, top), (right, bottom), (0, 255, 0), 2)
51
+ y = top - 15 if top - 15 > 15 else top + 15
52
+ cv2.putText(image, name, (left, y), cv2.FONT_HERSHEY_SIMPLEX,
53
+ 0.75, (0, 255, 0), 2)
54
+
55
+ # save the resulting image
56
+ # cv2.imwrite('output.jpg', image)
57
+ return image
58
+
59
+
60
+ demo = gr.Interface(
61
+ inf,
62
+ [
63
+ gr.Markdown("""
64
+ ## Welcome to the face recognition software !!!
65
+ """),
66
+ gr.Image(source="webcam", streaming=True)],
67
+ "image",
68
+ live=True
69
+ )
70
+ demo.launch(server_name="0.0.0.0", share=True)
71
+
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Click
2
+ dlib
3
+ face-recognition
4
+ face-recognition-models
5
+ imutils
6
+ numpy
7
+ Pillow
8
+ opencv-python
9
+ scikit-learn
10
+ pydrive
11
+ gdown
training_pipline.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+ import argparse
3
+ import cv2
4
+ import numpy as np
5
+ from sklearn.svm import SVC
6
+ from sklearn.preprocessing import LabelEncoder
7
+ from sklearn.neighbors import KNeighborsClassifier
8
+
9
+
10
+
11
+ def train(storage_client, bucket_name, embeddings_file):
12
+
13
+ bucket = storage_client.get_bucket(bucket_name)
14
+ blob = bucket.blob(embeddings_file)
15
+ blob.download_to_filename(embeddings_file)
16
+
17
+ data = pickle.loads(open(embeddings_file, "rb").read())
18
+
19
+ # train a classification model on these embeddings
20
+ # use the model to make predictions on the test data
21
+
22
+ X = data['encodings']
23
+ y_raw = data['names']
24
+
25
+ le = LabelEncoder()
26
+ y = le.fit_transform(y_raw)
27
+ print(le.classes_)
28
+ # save the labels in a file
29
+ f = open('labels.pkl', "wb")
30
+ f.write(pickle.dumps(le.classes_))
31
+ f.close()
32
+
33
+ model = KNeighborsClassifier(n_neighbors=3)
34
+ model.fit(X, y)
35
+
36
+ accuracy = model.score(X, y)
37
+ print(f'Accuracy: {accuracy}')
38
+
39
+ #save the model to disk
40
+ f = open('model.pkl', "wb")
41
+ f.write(pickle.dumps(model))
42
+ f.close()
43
+
44
+
45
+ if __name__ == '__main__':
46
+ train()