Register
Login
Resources
Docs Blog Datasets Glossary Case Studies Tutorials & Webinars
Product
Data Engine LLMs Platform Enterprise
Pricing Explore
Connect to our Discord channel

face_recognition_knn.py 8.8 KB

You have to be logged in to leave a comment. Sign In
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
  1. """
  2. This is an example of using the k-nearest-neighbors (KNN) algorithm for face recognition.
  3. When should I use this example?
  4. This example is useful when you wish to recognize a large set of known people,
  5. and make a prediction for an unknown person in a feasible computation time.
  6. Algorithm Description:
  7. The knn classifier is first trained on a set of labeled (known) faces and can then predict the person
  8. in an unknown image by finding the k most similar faces (images with closet face-features under eucledian distance)
  9. in its training set, and performing a majority vote (possibly weighted) on their label.
  10. For example, if k=3, and the three closest face images to the given image in the training set are one image of Biden
  11. and two images of Obama, The result would be 'Obama'.
  12. * This implementation uses a weighted vote, such that the votes of closer-neighbors are weighted more heavily.
  13. Usage:
  14. 1. Prepare a set of images of the known people you want to recognize. Organize the images in a single directory
  15. with a sub-directory for each known person.
  16. 2. Then, call the 'train' function with the appropriate parameters. Make sure to pass in the 'model_save_path' if you
  17. want to save the model to disk so you can re-use the model without having to re-train it.
  18. 3. Call 'predict' and pass in your trained model to recognize the people in an unknown image.
  19. NOTE: This example requires scikit-learn to be installed! You can install it with pip:
  20. $ pip3 install scikit-learn
  21. """
  22. import math
  23. from sklearn import neighbors
  24. import os
  25. import os.path
  26. import pickle
  27. from PIL import Image, ImageDraw
  28. import face_recognition
  29. from face_recognition.face_recognition_cli import image_files_in_folder
  30. ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
  31. def train(train_dir, model_save_path=None, n_neighbors=None, knn_algo='ball_tree', verbose=False):
  32. """
  33. Trains a k-nearest neighbors classifier for face recognition.
  34. :param train_dir: directory that contains a sub-directory for each known person, with its name.
  35. (View in source code to see train_dir example tree structure)
  36. Structure:
  37. <train_dir>/
  38. ├── <person1>/
  39. │ ├── <somename1>.jpeg
  40. │ ├── <somename2>.jpeg
  41. │ ├── ...
  42. ├── <person2>/
  43. │ ├── <somename1>.jpeg
  44. │ └── <somename2>.jpeg
  45. └── ...
  46. :param model_save_path: (optional) path to save model on disk
  47. :param n_neighbors: (optional) number of neighbors to weigh in classification. Chosen automatically if not specified
  48. :param knn_algo: (optional) underlying data structure to support knn.default is ball_tree
  49. :param verbose: verbosity of training
  50. :return: returns knn classifier that was trained on the given data.
  51. """
  52. X = []
  53. y = []
  54. # Loop through each person in the training set
  55. for class_dir in os.listdir(train_dir):
  56. if not os.path.isdir(os.path.join(train_dir, class_dir)):
  57. continue
  58. # Loop through each training image for the current person
  59. for img_path in image_files_in_folder(os.path.join(train_dir, class_dir)):
  60. image = face_recognition.load_image_file(img_path)
  61. face_bounding_boxes = face_recognition.face_locations(image)
  62. if len(face_bounding_boxes) != 1:
  63. # If there are no people (or too many people) in a training image, skip the image.
  64. if verbose:
  65. 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"))
  66. else:
  67. # Add face encoding for current image to the training set
  68. X.append(face_recognition.face_encodings(image, known_face_locations=face_bounding_boxes)[0])
  69. y.append(class_dir)
  70. # Determine how many neighbors to use for weighting in the KNN classifier
  71. if n_neighbors is None:
  72. n_neighbors = int(round(math.sqrt(len(X))))
  73. if verbose:
  74. print("Chose n_neighbors automatically:", n_neighbors)
  75. # Create and train the KNN classifier
  76. knn_clf = neighbors.KNeighborsClassifier(n_neighbors=n_neighbors, algorithm=knn_algo, weights='distance')
  77. knn_clf.fit(X, y)
  78. # Save the trained KNN classifier
  79. if model_save_path is not None:
  80. with open(model_save_path, 'wb') as f:
  81. pickle.dump(knn_clf, f)
  82. return knn_clf
  83. def predict(X_img_path, knn_clf=None, model_path=None, distance_threshold=0.6):
  84. """
  85. Recognizes faces in given image using a trained KNN classifier
  86. :param X_img_path: path to image to be recognized
  87. :param knn_clf: (optional) a knn classifier object. if not specified, model_save_path must be specified.
  88. :param model_path: (optional) path to a pickled knn classifier. if not specified, model_save_path must be knn_clf.
  89. :param distance_threshold: (optional) distance threshold for face classification. the larger it is, the more chance
  90. of mis-classifying an unknown person as a known one.
  91. :return: a list of names and face locations for the recognized faces in the image: [(name, bounding box), ...].
  92. For faces of unrecognized persons, the name 'unknown' will be returned.
  93. """
  94. if not os.path.isfile(X_img_path) or os.path.splitext(X_img_path)[1][1:] not in ALLOWED_EXTENSIONS:
  95. raise Exception("Invalid image path: {}".format(X_img_path))
  96. if knn_clf is None and model_path is None:
  97. raise Exception("Must supply knn classifier either thourgh knn_clf or model_path")
  98. # Load a trained KNN model (if one was passed in)
  99. if knn_clf is None:
  100. with open(model_path, 'rb') as f:
  101. knn_clf = pickle.load(f)
  102. # Load image file and find face locations
  103. X_img = face_recognition.load_image_file(X_img_path)
  104. X_face_locations = face_recognition.face_locations(X_img)
  105. # If no faces are found in the image, return an empty result.
  106. if len(X_face_locations) == 0:
  107. return []
  108. # Find encodings for faces in the test iamge
  109. faces_encodings = face_recognition.face_encodings(X_img, known_face_locations=X_face_locations)
  110. # Use the KNN model to find the best matches for the test face
  111. closest_distances = knn_clf.kneighbors(faces_encodings, n_neighbors=1)
  112. are_matches = [closest_distances[0][i][0] <= distance_threshold for i in range(len(X_face_locations))]
  113. # Predict classes and remove classifications that aren't within the threshold
  114. return [(pred, loc) if rec else ("unknown", loc) for pred, loc, rec in zip(knn_clf.predict(faces_encodings), X_face_locations, are_matches)]
  115. def show_prediction_labels_on_image(img_path, predictions):
  116. """
  117. Shows the face recognition results visually.
  118. :param img_path: path to image to be recognized
  119. :param predictions: results of the predict function
  120. :return:
  121. """
  122. pil_image = Image.open(img_path).convert("RGB")
  123. draw = ImageDraw.Draw(pil_image)
  124. for name, (top, right, bottom, left) in predictions:
  125. # Draw a box around the face using the Pillow module
  126. draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255))
  127. # There's a bug in Pillow where it blows up with non-UTF-8 text
  128. # when using the default bitmap font
  129. name = name.encode("UTF-8")
  130. # Draw a label with a name below the face
  131. text_width, text_height = draw.textsize(name)
  132. draw.rectangle(((left, bottom - text_height - 10), (right, bottom)), fill=(0, 0, 255), outline=(0, 0, 255))
  133. draw.text((left + 6, bottom - text_height - 5), name, fill=(255, 255, 255, 255))
  134. # Remove the drawing library from memory as per the Pillow docs
  135. del draw
  136. # Display the resulting image
  137. pil_image.show()
  138. if __name__ == "__main__":
  139. # STEP 1: Train the KNN classifier and save it to disk
  140. # Once the model is trained and saved, you can skip this step next time.
  141. print("Training KNN classifier...")
  142. classifier = train("knn_examples/train", model_save_path="trained_knn_model.clf", n_neighbors=2)
  143. print("Training complete!")
  144. # STEP 2: Using the trained classifier, make predictions for unknown images
  145. for image_file in os.listdir("knn_examples/test"):
  146. full_file_path = os.path.join("knn_examples/test", image_file)
  147. print("Looking for faces in {}".format(image_file))
  148. # Find all people in the image using a trained classifier model
  149. # Note: You can pass in either a classifier file name or a classifier model instance
  150. predictions = predict(full_file_path, model_path="trained_knn_model.clf")
  151. # Print results on the console
  152. for name, (top, right, bottom, left) in predictions:
  153. print("- Found {} at ({}, {})".format(name, left, top))
  154. # Display results overlaid on an image
  155. show_prediction_labels_on_image(os.path.join("knn_examples/test", image_file), predictions)
Tip!

Press p or to see the previous file or, n or to see the next file

Comments

Loading...