this is an example of using the knearestneighbors 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 facefeatures under euclidean distance in its training set and performing a majority vote possibly weighted on their label for example if k3 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 closerneighbors 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 subdirectory for each known person 2 then call the train function with the appropriate parameters make sure to pass in the modelsavepath if you want to save the model to disk so you can reuse the model without having to retrain it 3 call predict and pass in your trained model to recognize the people in a live video stream note this example requires scikitlearn opencv and numpy to be installed you can install it with pip pip3 install scikitlearn pip3 install numpy pip3 install opencvcontribpython trains a knearest neighbors classifier for face recognition param traindir directory that contains a subdirectory for each known person with its name view in source code to see traindir example tree structure structure traindir person1 somename1 jpeg somename2 jpeg person2 somename1 jpeg somename2 jpeg param modelsavepath optional path to save model on disk param nneighbors optional number of neighbors to weigh in classification chosen automatically if not specified param knnalgo optional underlying data structure to support knn default is balltree param verbose verbosity of training return returns knn classifier that was trained on the given data loop through each person in the training set loop through each training image for the current person if there are no people or too many people in a training image skip the image add face encoding for current image to the training set determine how many neighbors to use for weighting in the knn classifier create and train the knn classifier save the trained knn classifier recognizes faces in given image using a trained knn classifier param xframe frame to do the prediction on param knnclf optional a knn classifier object if not specified modelsavepath must be specified param modelpath optional path to a pickled knn classifier if not specified modelsavepath must be knnclf param distancethreshold optional distance threshold for face classification the larger it is the more chance of misclassifying 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 load a trained knn model if one was passed in if no faces are found in the image return an empty result find encodings for faces in the test image use the knn model to find the best matches for the test face predict classes and remove classifications that aren t within the threshold 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 enlarge the predictions for the full sized image draw a box around the face using the pillow module there s a bug in pillow where it blows up with nonutf8 text when using the default bitmap font draw a label with a name below the face remove the drawing library from memory as per the pillow docs save image in opencv format to be able to show it process one frame in every 30 frames for speed multiple cameras can be used with the format url http username passwordcameraip port different resizing options can be chosen based on desired program runtime image resizing for more stable streaming 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 loop through each person in the training set loop through each training image for the current person if there are no people or too many people in a training image skip the image add face encoding for current image to the training set determine how many neighbors to use for weighting in the knn classifier create and train the knn classifier save the trained knn classifier 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 load a trained knn model if one was passed in if no faces are found in the image return an empty result find encodings for faces in the test image use the knn model to find the best matches for the test face predict classes and remove classifications that aren t within the threshold 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 enlarge the predictions for the full sized image draw a box around the face using the pillow module there s a bug in pillow where it blows up with non utf 8 text when using the default bitmap font draw a label with a name below the face remove the drawing library from memory as per the pillow docs save image in open cv format to be able to show it process one frame in every 30 frames for speed multiple cameras can be used with the format url http username password camera_ip port different resizing options can be chosen based on desired program runtime image resizing for more stable streaming
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):
    
    X = []
    y = []

    
    for class_dir in os.listdir(train_dir):
        if not os.path.isdir(os.path.join(train_dir, class_dir)):
            continue

        
        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 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:
                
                X.append(face_recognition.face_encodings(image, known_face_locations=face_bounding_boxes)[0])
                y.append(class_dir)

    
    if n_neighbors is None:
        n_neighbors = int(round(math.sqrt(len(X))))
        if verbose:
            print("Chose n_neighbors automatically:", n_neighbors)

    
    knn_clf = neighbors.KNeighborsClassifier(n_neighbors=n_neighbors, algorithm=knn_algo, weights='distance')
    knn_clf.fit(X, y)

    
    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):
    
    if knn_clf is None and model_path is None:
        raise Exception("Must supply knn classifier either thourgh knn_clf or model_path")

    
    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 len(X_face_locations) == 0:
        return []

    
    faces_encodings = face_recognition.face_encodings(X_frame, known_face_locations=X_face_locations)

    
    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))]

    
    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):
    
    pil_image = Image.fromarray(frame)
    draw = ImageDraw.Draw(pil_image)

    for name, (top, right, bottom, left) in predictions:
        
        top *= 2
        right *= 2
        bottom *= 2
        left *= 2
        
        draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255))

        
        
        name = name.encode("UTF-8")

        
        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))

    
    del draw
    

    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_this_frame = 29
    print('Setting cameras up...')
    
    url = 'http://admin:admin@192.168.0.106:8081/'
    cap = cv2.VideoCapture(url)
    while 1 > 0:
        ret, frame = cap.read()
        if ret:
            
            
            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):
                cap.release()
                cv2.destroyAllWindows()
                exit(0)