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
|
- """
- This is an example of using the k-nearest-neighbors (KNN) algorithm for face recognition.
- When should I use this example?
- This example is useful when you wish to recognize a large set of known people,
- and make a prediction for an unknown person in a feasible computation time.
- Algorithm Description:
- The knn classifier is first trained on a set of labeled (known) faces and can then predict the person
- in a live stream by finding the k most similar faces (images with closet face-features under eucledian distance)
- in its training set, and performing a majority vote (possibly weighted) on their label.
- For example, if k=3, and the three closest face images to the given image in the training set are one image of Biden
- and two images of Obama, The result would be 'Obama'.
- * This implementation uses a weighted vote, such that the votes of closer-neighbors are weighted more heavily.
- Usage:
- 1. Prepare a set of images of the known people you want to recognize. Organize the images in a single directory
- with a sub-directory for each known person.
- 2. Then, call the 'train' function with the appropriate parameters. Make sure to pass in the 'model_save_path' if you
- want to save the model to disk so you can re-use the model without having to re-train it.
- 3. Call 'predict' and pass in your trained model to recognize the people in a live video stream.
- NOTE: This example requires scikit-learn, opencv and numpy to be installed! You can install it with pip:
- $ pip3 install scikit-learn
- $ pip3 install numpy
- $ pip3 install opencv-contrib-python
- """
- import cv2
- import math
- from sklearn import neighbors
- import os
- import os.path
- import pickle
- from PIL import Image, ImageDraw
- import face_recognition
- from face_recognition.face_recognition_cli import image_files_in_folder
- import numpy as np
- ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'JPG'}
- def train(train_dir, model_save_path=None, n_neighbors=None, knn_algo='ball_tree', verbose=False):
- """
- Trains a k-nearest neighbors classifier for face recognition.
- :param train_dir: directory that contains a sub-directory for each known person, with its name.
- (View in source code to see train_dir example tree structure)
- Structure:
- <train_dir>/
- ├── <person1>/
- │ ├── <somename1>.jpeg
- │ ├── <somename2>.jpeg
- │ ├── ...
- ├── <person2>/
- │ ├── <somename1>.jpeg
- │ └── <somename2>.jpeg
- └── ...
- :param model_save_path: (optional) path to save model on disk
- :param n_neighbors: (optional) number of neighbors to weigh in classification. Chosen automatically if not specified
- :param knn_algo: (optional) underlying data structure to support knn.default is ball_tree
- :param verbose: verbosity of training
- :return: returns knn classifier that was trained on the given data.
- """
- X = []
- y = []
- # Loop through each person in the training set
- for class_dir in os.listdir(train_dir):
- if not os.path.isdir(os.path.join(train_dir, class_dir)):
- continue
- # Loop through each training image for the current person
- for img_path in image_files_in_folder(os.path.join(train_dir, class_dir)):
- image = face_recognition.load_image_file(img_path)
- face_bounding_boxes = face_recognition.face_locations(image)
- if len(face_bounding_boxes) != 1:
- # If there are no people (or too many people) in a training image, skip the image.
- if verbose:
- print("Image {} not suitable for training: {}".format(img_path, "Didn't find a face" if len(face_bounding_boxes) < 1 else "Found more than one face"))
- else:
- # Add face encoding for current image to the training set
- X.append(face_recognition.face_encodings(image, known_face_locations=face_bounding_boxes)[0])
- y.append(class_dir)
- # Determine how many neighbors to use for weighting in the KNN classifier
- if n_neighbors is None:
- n_neighbors = int(round(math.sqrt(len(X))))
- if verbose:
- print("Chose n_neighbors automatically:", n_neighbors)
- # Create and train the KNN classifier
- knn_clf = neighbors.KNeighborsClassifier(n_neighbors=n_neighbors, algorithm=knn_algo, weights='distance')
- knn_clf.fit(X, y)
- # Save the trained KNN classifier
- if model_save_path is not None:
- with open(model_save_path, 'wb') as f:
- pickle.dump(knn_clf, f)
- return knn_clf
- def predict(X_frame, knn_clf=None, model_path=None, distance_threshold=0.5):
- """
- Recognizes faces in given image using a trained KNN classifier
- :param X_frame: frame to do the prediction on.
- :param knn_clf: (optional) a knn classifier object. if not specified, model_save_path must be specified.
- :param model_path: (optional) path to a pickled knn classifier. if not specified, model_save_path must be knn_clf.
- :param distance_threshold: (optional) distance threshold for face classification. the larger it is, the more chance
- of mis-classifying an unknown person as a known one.
- :return: a list of names and face locations for the recognized faces in the image: [(name, bounding box), ...].
- For faces of unrecognized persons, the name 'unknown' will be returned.
- """
- if knn_clf is None and model_path is None:
- raise Exception("Must supply knn classifier either thourgh knn_clf or model_path")
- # Load a trained KNN model (if one was passed in)
- if knn_clf is None:
- with open(model_path, 'rb') as f:
- knn_clf = pickle.load(f)
- X_face_locations = face_recognition.face_locations(X_frame)
- # If no faces are found in the image, return an empty result.
- if len(X_face_locations) == 0:
- return []
- # Find encodings for faces in the test image
- faces_encodings = face_recognition.face_encodings(X_frame, known_face_locations=X_face_locations)
- # Use the KNN model to find the best matches for the test face
- closest_distances = knn_clf.kneighbors(faces_encodings, n_neighbors=1)
- are_matches = [closest_distances[0][i][0] <= distance_threshold for i in range(len(X_face_locations))]
- # Predict classes and remove classifications that aren't within the threshold
- return [(pred, loc) if rec else ("unknown", loc) for pred, loc, rec in zip(knn_clf.predict(faces_encodings), X_face_locations, are_matches)]
- def show_prediction_labels_on_image(frame, predictions):
- """
- Shows the face recognition results visually.
- :param frame: frame to show the predictions on
- :param predictions: results of the predict function
- :return opencv suited image to be fitting with cv2.imshow fucntion:
- """
- pil_image = Image.fromarray(frame)
- draw = ImageDraw.Draw(pil_image)
- for name, (top, right, bottom, left) in predictions:
- # enlarge the predictions for the full sized image.
- top *= 2
- right *= 2
- bottom *= 2
- left *= 2
- # Draw a box around the face using the Pillow module
- draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255))
- # There's a bug in Pillow where it blows up with non-UTF-8 text
- # when using the default bitmap font
- name = name.encode("UTF-8")
- # Draw a label with a name below the face
- text_width, text_height = draw.textsize(name)
- draw.rectangle(((left, bottom - text_height - 10), (right, bottom)), fill=(0, 0, 255), outline=(0, 0, 255))
- draw.text((left + 6, bottom - text_height - 5), name, fill=(255, 255, 255, 255))
- # Remove the drawing library from memory as per the Pillow docs.
- del draw
- # Save image in open-cv format to be able to show it.
- opencvimage = np.array(pil_image)
- return opencvimage
- if __name__ == "__main__":
- print("Training KNN classifier...")
- classifier = train("knn_examples/train", model_save_path="trained_knn_model.clf", n_neighbors=2)
- print("Training complete!")
- # process one frame in every 30 frames for speed
- process_this_frame = 29
- print('Setting cameras up...')
- # multiple cameras can be used with the format url = 'http://username:password@camera_ip:port'
- url = 'http://admin:admin@192.168.0.106:8081/'
- cap = cv2.VideoCapture(url)
- while 1 > 0:
- ret, frame = cap.read()
- if ret:
- # Different resizing options can be chosen based on desired program runtime.
- # Image resizing for more stable streaming
- img = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
- process_this_frame = process_this_frame + 1
- if process_this_frame % 30 == 0:
- predictions = predict(img, model_path="trained_knn_model.clf")
- frame = show_prediction_labels_on_image(frame, predictions)
- cv2.imshow('camera', frame)
- if ord('q') == cv2.waitKey(10):
- cap1.release()
- cv2.destroyAllWindows()
- exit(0)
|