[deep learning] Python face recognition library_ Recognition tutorial

Posted by Dookster on Sat, 26 Feb 2022 01:27:13 +0100

Python face recognition library_ Recognition tutorial

face_recognition is known as the simplest open source face recognition library in the world, which can recognize and operate faces through Python or command line. face_recognition provides very complete technical documents and application examples. Beginners of face recognition recommend studying the library. face_ The official code warehouse of recognition is: face_recognition . face_recognition also has its own official Chinese document. For details of this document, see: face_ Chinese instructions for recognition.

All the code and most of the test images in this article come from face_ examples folder of the official code warehouse of recognition. For practical use, please refer to the function interface description in the official document face_recognition function interface.

face_ The face recognition model in recognition comes from the open source machine learning library Dlib. The official code warehouse of Dlib is shown in: dlib . Most models are tested with Labeled Faces in the Wild face dataset, and the accuracy is as high as 99.38%. However, the recognition accuracy of children and Asian faces needs to be improved. Labeled Faces in the Wild is a face dataset produced by the University of Massachusetts Amherst, which contains more than 13000 face images collected from the network. This data set is a very small face data set.

Generally speaking, the face recognition model of this project is based on adults, and the effect may be average on children. face_ Generally, it's better to learn code and use or study source code, which is still a certain distance from engineering application.

For all codes in this article, see:

github: Python-Study-Notes

1 face_recognition installation and related knowledge

face_recognition supports linux, mac and windows systems. It is recommended to use face for linux systems_ recognition. Install face_ The python Library of dlib needs to be installed before the recognition library. See the installation instructions for the python Library of dlib: [common tools] dlib compilation and call Guide The fourth section of. Note that the installation of dlib Library under windows is not so easy. Check the documentation more.

When dlib is installed, enter the following command to install face_recognition.

pip install face_recognition

The general process of face recognition generally includes three steps: face detection, face alignment and face recognition:

  • 1 face detection and location: face detection is to find the specific location of the face in the picture and output the bounding rectangle containing the location of the face. Some detection algorithms can output the corresponding key points of the face at the same time.
  • 2 face alignment: the so-called face alignment is that sometimes the angle of the face is not correct. According to the key point detection results, align the face to a preset fixed position (usually the front face) through image transformation or other methods. In this way, the eyes and nose of different faces are placed in the same position, which greatly improves the recognition accuracy.
  • 3 face recognition: face recognition has many application directions, but the purpose is to identify the person corresponding to the current face.

The simple general flow diagram of face recognition is shown in the figure below. In face_ All codes in recognition involve these steps; However, face alignment is a direct call to dlib code, and there is no example.

Of course, in mature commercial engineering applications, there are not only these three parts, such as face quality judgment and in vivo detection, but general projects include these three steps. For more information about face recognition, see: https://www.cnblogs.com/xiaoyh/p/11874270.html

2 face detection / location

This part mainly detects and locates the face, and outputs the corresponding rectangular box of the face. Mainly used face_ The built-in functions of recognition include:

  • face_recognition.api.face_locations(img, number_of_times_to_upsample=1, model='hog')

    • Purpose: face detection, return the array of face bounding boxes in the image
    • img: input image, numpy array
    • number_of_times_to_upsample: upsample the image to find a smaller face. The default value is 1
    • Model: detection model. The default is hog machine learning model. In addition, cnn can be set to select convolutional neural network model to improve detection accuracy
    • Return: a list array containing multiple face bounding boxes. The bounding box data is represented in the order of faces (top, right, bottom, left)
  • face_recognition.api.load_image_file(file, mode='RGB')

    • Purpose: load image
    • file: image pathname
    • mode: image color type. Set RGB to return RGB image and 'L' to return grayscale image
    • Return: numpy array

2.1 face detection based on machine learning

From examples/find_faces_in_picture.py

%matplotlib inline
import matplotlib.pyplot as plt 
from PIL import Image
import face_recognition

# Load pictures through PIL
image = face_recognition.load_image_file("test_img/obama.jpg")
# Face recognition based on hog machine learning model cannot be accelerated by gpu
face_locations = face_recognition.face_locations(image)

# Find some faces
print("I found {} face(s) in this photograph.".format(len(face_locations)))

for face_location in face_locations:

    # Print face information
    top, right, bottom, left = face_location
    print("A face is located at pixel location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))

    # Face extraction
    face_image = image[top:bottom, left:right]
    pil_image = Image.fromarray(face_image)
    # jupyter drawing
    # pil_image.show()
    plt.imshow(pil_image)
    plt.axis('off')    
    plt.show()
I found 1 face(s) in this photograph.
A face is located at pixel location Top: 142, Left: 349, Bottom: 409, Right: 617

2.2 face detection based on convolutional neural network

From examples/find_faces_in_picture_cnn.py

%matplotlib inline
import matplotlib.pyplot as plt 
from PIL import Image
import face_recognition

# Load pictures through PIL
image = face_recognition.load_image_file("test_img/obama.jpg")

# Face recognition based on cnn, whether to use gpu to see the installation environment
face_locations = face_recognition.face_locations(image, number_of_times_to_upsample=0, model="cnn")

print("I found {} face(s) in this photograph.".format(len(face_locations)))

for face_location in face_locations:

    # Print face information
    top, right, bottom, left = face_location
    print("A face is located at pixel location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))

    # Face extraction
    face_image = image[top:bottom, left:right]
    pil_image = Image.fromarray(face_image)
    # jupyter drawing
    # pil_image.show()
    plt.imshow(pil_image)
    plt.axis('off')    
    plt.show()
I found 1 face(s) in this photograph.
A face is located at pixel location Top: 154, Left: 375, Bottom: 390, Right: 611

2.3 face mosaic

From examples/blur_faces_on_webcam.py

%matplotlib inline
import matplotlib.pyplot as plt
import face_recognition
import cv2

frame = cv2.imread("test_img/obama.jpg")

# Reduce the image to speed up
small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)

# Find face
face_locations = face_recognition.face_locations(small_frame, model="cnn")

for top, right, bottom, left in face_locations:
    # Extract the bounding box at the scale of the original drawing
    top *= 4
    right *= 4
    bottom *= 4
    left *= 4

# Face extraction
face_image = frame[top:bottom, left:right]

# Gaussian blurred face
face_image = cv2.GaussianBlur(face_image, (99, 99), 30)

# Original face replacement
frame[top:bottom, left:right] = face_image

# Display image
img = frame[:,:,::-1] 
plt.axis('off')
plt.imshow(img)
<matplotlib.image.AxesImage at 0x2139a75cdf0>

3 face key point recognition

This part mainly recognizes the key points of face and outputs the position of face features. Mainly used face_ The built-in functions of recognition include:

  • face_recognition.api.face_landmarks(face_image, face_locations=None, model='large')
    • Purpose: face key point recognition, return the dictionary of image face feature location
    • face_image: input image, numpy array
    • face_locations: list of locations to identify (optional)
    • Model: the recognition model used. The default value is large, indicating a large model. Small represents a small model, but only five feature points are returned
    • Return: dictionary list of feature locations (eyes, nose, etc.)

3.1 extracting face key points in image

From examples/find_facial_features_in_picture.py

%matplotlib inline
import matplotlib.pyplot as plt 
from PIL import Image, ImageDraw
import face_recognition

# Load pictures through PIL
image = face_recognition.load_image_file("test_img/two_people.jpg")

# Find all facial features of all faces in the image and return to the dictionary
face_landmarks_list = face_recognition.face_landmarks(image)

# Number of faces found
print("I found {} face(s) in this photograph.".format(len(face_landmarks_list)))

# Create an image showing the results
pil_image = Image.fromarray(image)
d = ImageDraw.Draw(pil_image)

# Draw keys
for face_landmarks in face_landmarks_list:

    # Print the location of each facial feature in this image
    # for facial_feature in face_landmarks.keys():
       # print("The {} in this face has the following points: {}".format(facial_feature, face_landmarks[facial_feature]))

    # Outline each facial feature in the image with a line
    for facial_feature in face_landmarks.keys():
        d.line(face_landmarks[facial_feature], width=5)


# jupyter drawing
# pil_image.show()
plt.imshow(pil_image)
plt.axis('off')    
plt.show()
I found 2 face(s) in this photograph.

3.2 face coloring

From examples/digital_makeup.py

%matplotlib inline
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
import face_recognition

# Load pictures through PIL
image = face_recognition.load_image_file("test_img/two_people.jpg")

# Find all facial features of all faces in the image and return to the dictionary
face_landmarks_list = face_recognition.face_landmarks(image)

pil_image = Image.fromarray(image)

# mapping
for face_landmarks in face_landmarks_list:
    d = ImageDraw.Draw(pil_image, 'RGBA')

    # Eyebrow coloring
    d.polygon(face_landmarks['left_eyebrow'], fill=(68, 54, 39, 128))
    d.polygon(face_landmarks['right_eyebrow'], fill=(68, 54, 39, 128))
    d.line(face_landmarks['left_eyebrow'], fill=(68, 54, 39, 150), width=5)
    d.line(face_landmarks['right_eyebrow'], fill=(68, 54, 39, 150), width=5)

    # Lip coloring
    d.polygon(face_landmarks['top_lip'], fill=(150, 0, 0, 128))
    d.polygon(face_landmarks['bottom_lip'], fill=(150, 0, 0, 128))
    d.line(face_landmarks['top_lip'], fill=(150, 0, 0, 64), width=8)
    d.line(face_landmarks['bottom_lip'], fill=(150, 0, 0, 64), width=8)

    # Eye coloring
    d.polygon(face_landmarks['left_eye'], fill=(255, 255, 255, 30))
    d.polygon(face_landmarks['right_eye'], fill=(255, 255, 255, 30))

    # Eyeliner coloring
    d.line(face_landmarks['left_eye'] + [face_landmarks['left_eye'][0]], fill=(0, 0, 0, 110), width=6)
    d.line(face_landmarks['right_eye'] + [face_landmarks['right_eye'][0]], fill=(0, 0, 0, 110), width=6)

# jupyter drawing
# pil_image.show()
plt.imshow(pil_image)
plt.axis('off')
plt.show()

3.3 recognition of human eye opening and closing state

From examples / blink_detection.py

This part of the code is used to calculate the aspect ratio of the human eye according to the key point data of the human eye. When the human eye is open, the vertical and horizontal are relatively high, and when the human eye is closed, the vertical and horizontal are relatively small. If the number of eyes closed exceeds the set threshold, the output human eye is in the closed state.

import matplotlib.pylab as plt
import face_recognition
import cv2
from scipy.spatial import distance as dist

# This is a demonstration of detecting eye state
# The number of times the human eye is closed exceeds the set threshold EYES_CLOSED_SECONDS, judge that the human eye is closed
EYES_CLOSED_SECONDS = 2


def main():
    # Number of eye closures
    closed_count = 0
    # Read two images to imitate people's eyes
    img_eye_opened = cv2.imread('test_img/eye_opened.jpg')
    img_eye_closed = cv2.imread('test_img/eye_closed.jpg')
    # Set the image input sequence. The first one opens the eyes, the middle three closes the eyes, and the last one opens the eyes
    frame_inputs = [img_eye_opened] + [img_eye_closed] * 3 + [img_eye_opened] * 1

    for frame_num, frame in enumerate(frame_inputs):
        # Zoom out picture
        small_frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
        # bgr channel becomes rgb channel
        rgb_small_frame = small_frame[:, :, ::-1]
        # Face key point detection
        face_landmarks_list = face_recognition.face_landmarks(rgb_small_frame)
        # No key detected
        if len(face_landmarks_list) < 1:
            continue

        # Obtain the position of human eye feature points
        for face_landmark in face_landmarks_list:
            # Each eye has six key points, which are arranged clockwise on the leftmost side of the eye
            left_eye = face_landmark['left_eye']
            right_eye = face_landmark['right_eye']

            # Calculate the aspect ratio ear of the eye. Ear doesn't mean ear here
            ear_left = get_ear(left_eye)
            ear_right = get_ear(right_eye)
            # Judge whether the eyes are closed
            # If the aspect ratio of two eyes is less than 0.2, the eyes are considered closed
            closed = ear_left < 0.2 and ear_right < 0.2
            # Set the closing times of eye detection
            if closed:
                closed_count += 1
            else:
                closed_count = 0
            # If the eyes are closed more than EYES_CLOSED_SECONDS, output eyes closed
            if closed_count > EYES_CLOSED_SECONDS:
                eye_status = "frame {} | EYES CLOSED".format(frame_num)
            elif closed_count > 0:
                eye_status = "frame {} | MAYBE EYES CLOSED ".format(frame_num)
            else:
                eye_status = "frame {} | EYES OPENED ".format(frame_num)
            print(eye_status)

            plt.imshow(rgb_small_frame)
            # The color of the first key point of the left and right eye contour is red, the color of the last key point is blue, and the other key points are yellow
            color = ['red'] + ['yellow'] * int(len(left_eye) - 2) + ['blue']
            # Draw eye keys in order
            for index in range(len(left_eye)):
                leye = left_eye[index]
                reye = right_eye[index]
                plt.plot(leye[0], leye[1], 'bo', color=color[index])
                plt.plot(reye[0], reye[1], 'bo', color=color[index])
                plt.title(eye_status)

            plt.show()

# Calculate the aspect ratio of human eyes
def get_ear(eye):
    # Calculate the distance between the upper and lower keys in the vertical direction of the eye contour
    A = dist.euclidean(eye[1], eye[5])
    B = dist.euclidean(eye[2], eye[4])

    # Calculate the horizontal distance of the key point
    C = dist.euclidean(eye[0], eye[3])

    # Calculate the aspect ratio of the eye
    ear = (A + B) / (2.0 * C)

    # Returns the aspect ratio of the eye
    return ear


if __name__ == "__main__":
    main()
frame 0 | EYES OPENED 

frame 1 | MAYBE EYES CLOSED 

frame 2 | MAYBE EYES CLOSED 

frame 3 | EYES CLOSED

frame 4 | EYES OPENED 

4 face recognition

This part is mainly for face recognition and provides a variety of practical task cases. Mainly used face_ The built-in functions of recognition include:

  • face_recognition.api.face_encodings(face_image, known_face_locations=None, num_jitters=1, model='small')

    • Purpose: return 128 dimensional face features of each face in the image
    • face_image: input image, numpy array
    • known_face_locations: the bounding box of each face (optional), which can greatly improve the recognition speed
    • num_jitters: the number of times to resample faces when calculating face features. Higher, more accurate, but slower, i.e. set to 100 times slower
    • Model: the recognition model used. The default value is small, indicating a small model. Only five feature points are returned; Can be set to large
    • Return: list containing facial features
  • face_recognition.api.compare_faces(known_face_encodings, face_encoding_to_check, tolerance=0.6)

    • Purpose: compare face features with candidate face features to see if they match.
    • known_face_encodings: list of known facial features
    • face_encoding_to_check: a single face feature compared with the list of known face features
    • Tolerance: the smaller the face distance is, the closer the face is. When the face distance is less than tolerance, it means the same person; 0.6 is the default value and the best value considered by the author (different from the actual value)
    • Return: a list containing True or False to indicate whether it is the same face
  • face_recognition.api.face_distance(face_encodings, face_to_compare)

    • Purpose: give a list of human face features, compare them with known face features, and obtain the Euclidean distance between face feature vectors. The smaller the distance, the more similar the face hole is.
    • face_encodings: list of known facial features
    • face_to_compare: list of unknown facial features
    • Return: numpy array representing distance, and face_encodings are sorted in the same way

4.1 face comparison

From examples/recognize_faces_in_pictures.py

This part of the code is to input two known face images and one unknown face image to see which image of the unknown face image and the known face represents the same person.

%matplotlib inline
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
import face_recognition

# Load pictures through PIL
biden_image = face_recognition.load_image_file("test_img/biden.jpg")
obama_image = face_recognition.load_image_file("test_img/obama.jpg")
unknown_image = face_recognition.load_image_file("test_img/obama2.jpg")

plt.imshow(biden_image)
plt.title('biden')
plt.axis('off')
plt.show()
plt.imshow(obama_image)
plt.title('obama')
plt.axis('off')
plt.show()
plt.imshow(unknown_image)
plt.title('unknown')
plt.axis('off')
plt.show()

# Obtain the face feature of each face in the input image file, and the face feature dimension is 128
# Since there may be multiple faces in the input image, it returns a list of features.
# By default, the input image has only one face and only cares about the first feature in each image, so set the feature acquisition index to 0
# It is recommended to take a step-by-step look at the operation mechanism of this function
try:
    biden_face_encoding = face_recognition.face_encodings(biden_image)[0]
    obama_face_encoding = face_recognition.face_encodings(obama_image)[0]
    unknown_face_encoding = face_recognition.face_encodings(unknown_image)[0]
except IndexError:
    # No face found
    print("I wasn't able to locate any faces in at least one of the images. Check the image files. Aborting...")
    quit()

# The list of known faces is Biden's face features and Obama's face features in order
known_faces = [
    biden_face_encoding,
    obama_face_encoding
]

# If the unknown face matches someone in the known face array, the matching result is true
# This function calls face_distance facial feature distance calculation function can be debugged in one step to see the source code
results = face_recognition.compare_faces(known_faces, unknown_face_encoding)

# Match the first person
print("Is the unknown face a picture of Biden? {}".format(results[0]))
# Match with the second person
print("Is the unknown face a picture of Obama? {}".format(results[1]))
# Have you ever seen this face
print("Is the unknown face a new person that we've never seen before? {}".format(not True in results))

Is the unknown face a picture of Biden? False
Is the unknown face a picture of Obama? True
Is the unknown face a new person that we've never seen before? False

4.2 after face recognition, draw a frame on the original image and mark the name

From examples/identify_and_draw_boxes_on_faces.py

This part of the code is to input two known face images and one unknown face image, then carry out face recognition and mark each face identity information in the unknown face image

%matplotlib inline
import matplotlib.pyplot as plt
import face_recognition
from PIL import Image, ImageDraw
import numpy as np

# Load the first sample image and extract features
obama_image = face_recognition.load_image_file("test_img/obama.jpg")
obama_face_encoding = face_recognition.face_encodings(obama_image)[0]

# Load the second sample image and extract features
biden_image = face_recognition.load_image_file("test_img/biden.jpg")
biden_face_encoding = face_recognition.face_encodings(biden_image)[0]

# Create data of known face features and their names
known_face_encodings = [
    obama_face_encoding,
    biden_face_encoding
]
known_face_names = [
    "Barack Obama",
    "Joe Biden"
]

# Load unknown face image
unknown_image = face_recognition.load_image_file("test_img/two_people.jpg")

# Face detection
face_locations = face_recognition.face_locations(unknown_image)
# Face feature extraction
face_encodings = face_recognition.face_encodings(unknown_image, face_locations)

# View input image
plt.imshow(biden_image)
plt.title('biden')
plt.axis('off')
plt.show()
plt.imshow(obama_image)
plt.title('obama')
plt.axis('off')
plt.show()
plt.imshow(unknown_image)
plt.title('unknown')
plt.axis('off')
plt.show()

# mapping
pil_image = Image.fromarray(unknown_image)
draw = ImageDraw.Draw(pil_image)

# Processing of each face in unknown face images
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
    # Determine which face matches
    matches = face_recognition.compare_faces(known_face_encodings, face_encoding)

    name = "Unknown"

    # Result matching method 1
    # Multiple faces are matched successfully, and only the first face is the result
    # if True in matches:
    #     first_match_index = matches.index(True)
    #     name = known_face_names[first_match_index]

    # Result matching method 2
    # A better result matching method uses the known face with the smallest distance from the new face as the result
    # Calculate the distance between the known face and the unknown face feature vector. The smaller the distance, the greater the possibility that the two faces are the same person
    face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
    # Extract the known face number with the smallest distance from the unknown face
    best_match_index = np.argmin(face_distances)
    # Extract the matched known face name
    if matches[best_match_index]:
        name = known_face_names[best_match_index]

    # Draw bounding box for face
    draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255))

    # Draw the name of the person whose face belongs below the face bounding box
    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

# jupyter drawing
# pil_image.show()
plt.imshow(pil_image)
plt.axis('off')
plt.show()

# Save identification results
# pil_image.save("image_with_boxes.jpg")

4.3 face comparison with different accuracy

From examples/face_distance.py

The function of this part of the code is similar to that of 4.1. The difference is to judge whether two faces represent the same person according to the distance of face feature vector and different distance thresholds

import face_recognition

# Load image
known_obama_image = face_recognition.load_image_file("test_img/obama.jpg")
known_biden_image = face_recognition.load_image_file("test_img/biden.jpg")

# Obtain face image features
obama_face_encoding = face_recognition.face_encodings(known_obama_image)[0]
biden_face_encoding = face_recognition.face_encodings(known_biden_image)[0]

known_encodings = [
    obama_face_encoding,
    biden_face_encoding
]

# Load unknown face image
image_to_test = face_recognition.load_image_file("test_img/obama2.jpg")
image_to_test_encoding = face_recognition.face_encodings(image_to_test)[0]

# Calculate the distance between an unknown face and a known face
face_distances = face_recognition.face_distance(known_encodings, image_to_test_encoding)

# View face matching results under different distance thresholds
for i, face_distance in enumerate(face_distances):
    # Print distance
    print("The test image has a distance of {:.2} from known image #{}".format(face_distance, i))
    # When the threshold is 0.6, does it match
    print("- With a normal cutoff of 0.6, would the test image match the known image? {}".format(face_distance < 0.6))
    # When the threshold is 0.5 more stringent, is it matched
    print("- With a very strict cutoff of 0.5, would the test image match the known image? {}".format(
        face_distance < 0.5))
    print()
The test image has a distance of 0.35 from known image #0
- With a normal cutoff of 0.6, would the test image match the known image? True
- With a very strict cutoff of 0.5, would the test image match the known image? True

The test image has a distance of 0.82 from known image #1
- With a normal cutoff of 0.6, would the test image match the known image? False
- With a very strict cutoff of 0.5, would the test image match the known image? False

4.4 face recognition based on K-nearest neighbor KNN classification algorithm

From examples/face_recognition_knn.py

This part of the code is the same as the previous part of the code, except that after extracting the face features, the KNN nearest neighbor algorithm is used for classification, rather than judging by distance.

%matplotlib inline
"""
use k-Nearest neighbor( KNN)Example of face recognition based on Algorithm
"""

from matplotlib import pyplot as plt
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

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}


def train(train_dir, model_save_path=None, n_neighbors=None, knn_algo='ball_tree', verbose=False):
    """
    train k Nearest neighbor classifier for face recognition.
    :param train_dir: A directory containing each known person and their faces.
     Structure:
        <train_dir>/
        ├── <person1>/
        │   ├── <somename1>.jpeg
        │   ├── <somename2>.jpeg
        │   ├── ...
        ├── <person2>/
        │   ├── <somename1>.jpeg
        │   └── <somename2>.jpeg
        └── ...
    :param model_save_path: (Optional) Model save directory
    :param n_neighbors: (Optional) The number of neighbors to be weighted in the classification. If it is not specified, it is automatically selected, that is k-NN of k Select the nearest value k Point
    :param knn_algo: (Optional) knn Underlying search algorithm
    :param verbose: Print training information
    :return: Return to the trained model
    """
    X = []
    y = []

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

        # Read the face image of the current person
        for img_path in image_files_in_folder(os.path.join(train_dir, class_dir)):
            # Load picture
            image = face_recognition.load_image_file(img_path)
            # Face detection
            face_bounding_boxes = face_recognition.face_locations(image)

            if len(face_bounding_boxes) != 1:
                # No one will skip the current picture
                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:
                # Save face features and categories
                X.append(face_recognition.face_encodings(image, known_face_locations=face_bounding_boxes)[0])
                y.append(class_dir)

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

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

    # Save 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_img_path, knn_clf=None, model_path=None, distance_threshold=0.6):
    """
    Use trained KNN The classifier recognizes the face in a given image
    :param X_img_path: input image 
    :param knn_clf: (Optional) knn Models, and model_path One must be available
    :param model_path: (Optional) knn Model path, and knn_clf One must be available
    :param distance_threshold: (Optional) Distance threshold of face classification. The higher the threshold, the easier it is to make false positives.
    :return: Person name corresponding to face and its bounding box
    """
    if not os.path.isfile(X_img_path) or os.path.splitext(X_img_path)[1][1:] not in ALLOWED_EXTENSIONS:
        raise Exception("Invalid image path: {}".format(X_img_path))

    if knn_clf is None and model_path is None:
        raise Exception("Must supply knn classifier either thourgh knn_clf or model_path")

    # Loading model
    if knn_clf is None:
        with open(model_path, 'rb') as f:
            knn_clf = pickle.load(f)

    # Reading pictures and face detection
    X_img = face_recognition.load_image_file(X_img_path)
    X_face_locations = face_recognition.face_locations(X_img)

    # If no face is detected, an empty list is returned
    if len(X_face_locations) == 0:
        return []

    # Face feature extraction
    faces_encodings = face_recognition.face_encodings(X_img, known_face_locations=X_face_locations)

    # Classification using K-nearest neighbor
    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 forecast results
    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(img_path, predictions):
    """
    Visualization of prediction results
    :param img_path: Prediction image
    :param predictions: Prediction results
    :return:
    """
    pil_image = Image.open(img_path).convert("RGB")
    draw = ImageDraw.Draw(pil_image)

    for name, (top, right, bottom, left) in predictions:
        # Picture frame
        draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255))

        # uft-8 code is required to set the name
        name = name.encode("UTF-8")

        # Mark person's name
        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

    # jupyter drawing
    # pil_image.show()
    plt.imshow(pil_image)
    plt.axis('off')
    plt.show()


if __name__ == "__main__":
    # Training picture download address: https://github.com/ageitgey/face_recognition/tree/master/examples/knn_examples
    # STEP 1 training KNN classifier
    print("Training KNN classifier...")
    classifier = train("./test_img/knn_examples/train", model_save_path="trained_knn_model.clf", n_neighbors=2)
    print("Training complete!")

    # STEP 2 uses the trained KNN classifier to recognize the tested face image
    for image_file in os.listdir("./test_img/knn_examples/test"):
        # Face image path to be tested
        full_file_path = os.path.join("./test_img/knn_examples/test", image_file)

        print("Looking for faces in {}".format(image_file))

        # The trained classifier model is used to find everyone in the image
        predictions = predict(full_file_path, model_path="trained_knn_model.clf")

        # Print results
        for name, (top, right, bottom, left) in predictions:
            print("- Found {} at ({}, {})".format(name, left, top))

        # Show results
        show_prediction_labels_on_image(os.path.join("./test_img/knn_examples/test", image_file), predictions)
Training KNN classifier...
Training complete!
Looking for faces in alex_lacamoire1.jpg
- Found alex_lacamoire at (633, 206)

Looking for faces in johnsnow_test1.jpg
- Found kit_harington at (262, 180)

Looking for faces in kit_with_rose.jpg
- Found rose_leslie at (79, 130)
- Found kit_harington at (247, 92)

Looking for faces in obama1.jpg
- Found obama at (546, 204)

Looking for faces in obama_and_biden.jpg
- Found biden at (737, 449)
- Found obama at (1133, 390)
- Found unknown at (1594, 1062)

4.5 benchmark performance test

From examples / benchmark py

This part of the code implements a very simple benchmark, which can let you understand the speed of each step of face recognition running on your system

import timeit

# This is a very simple benchmark that allows you to understand the speed of each step of face recognition running on your system. Note that face detection becomes very slow at large image sizes
TEST_IMAGES = [
    "test_img/obama-240p.jpg",
    "test_img/obama-480p.jpg",
    "test_img/obama-720p.jpg",
    "test_img/obama-1080p.jpg"
]


# Test function
def run_test(setup, test, iterations_per_test=2, tests_to_run=3):
    """
    :param setup: Data loading function
    :param test: Data test function
    :param iterations_per_test: Number of tests
    :param tests_to_run: How many times does each round of test call the function
    :return: execution_time Single function reasoning time, fps Number of processes per second
    """
    fastest_execution = min(timeit.Timer(test, setup=setup).repeat(tests_to_run, iterations_per_test))
    execution_time = fastest_execution / iterations_per_test
    fps = 1.0 / execution_time
    return execution_time, fps


# The following sets different test function codes
# The data loading code starts with setup and the function test code starts with test
setup_locate_faces = """
import face_recognition

image = face_recognition.load_image_file("{}")
"""

test_locate_faces = """
face_locations = face_recognition.face_locations(image)
"""

setup_face_landmarks = """
import face_recognition

image = face_recognition.load_image_file("{}")
face_locations = face_recognition.face_locations(image)
"""

test_face_landmarks = """
landmarks = face_recognition.face_landmarks(image, face_locations=face_locations)[0]
"""

setup_encode_face = """
import face_recognition

image = face_recognition.load_image_file("{}")
face_locations = face_recognition.face_locations(image)
"""

test_encode_face = """
encoding = face_recognition.face_encodings(image, known_face_locations=face_locations)[0]
"""

setup_end_to_end = """
import face_recognition

image = face_recognition.load_image_file("{}")
"""

test_end_to_end = """
encoding = face_recognition.face_encodings(image)[0]
"""

# All benchmarks use only one CPU core
print("Benchmarks (Note: All benchmarks are only using a single CPU core)")
print()

for image in TEST_IMAGES:
    size = image.split("-")[1].split(".")[0]
    print("Timings at {}:".format(size))

    # Test face detection
    print(" - Face locations: {:.4f}s ({:.2f} fps)".format(
        *run_test(setup_locate_faces.format(image), test_locate_faces)))
    print(" - Face landmarks: {:.4f}s ({:.2f} fps)".format(
        *run_test(setup_face_landmarks.format(image), test_face_landmarks)))
    print(" - Encode face (inc. landmarks): {:.4f}s ({:.2f} fps)".format(
        *run_test(setup_encode_face.format(image), test_encode_face)))
    print(" - End-to-end: {:.4f}s ({:.2f} fps)".format(*run_test(setup_end_to_end.format(image), test_end_to_end)))
    print()
Benchmarks (Note: All benchmarks are only using a single CPU core)

Timings at 240p:
 - Face locations: 0.0819s (12.21 fps)
 - Face landmarks: 0.0029s (344.69 fps)
 - Encode face (inc. landmarks): 0.4879s (2.05 fps)
 - End-to-end: 0.5978s (1.67 fps)

Timings at 480p:
 - Face locations: 0.3257s (3.07 fps)
 - Face landmarks: 0.0028s (362.23 fps)
 - Encode face (inc. landmarks): 0.4959s (2.02 fps)
 - End-to-end: 0.8203s (1.22 fps)

Timings at 720p:
 - Face locations: 0.7046s (1.42 fps)
 - Face landmarks: 0.0028s (355.30 fps)
 - Encode face (inc. landmarks): 0.4993s (2.00 fps)
 - End-to-end: 1.1888s (0.84 fps)

Timings at 1080p:
 - Face locations: 1.5179s (0.66 fps)
 - Face landmarks: 0.0030s (334.93 fps)
 - Encode face (inc. landmarks): 0.4838s (2.07 fps)
 - End-to-end: 1.9404s (0.52 fps)

4.6 multithreaded face recognition

From facerec_from_webcam_multiprocessing.py

This part of the code realizes multi-threaded reading video for face recognition. It is very simple, but it is not written in this way. Just look at the code.

import face_recognition
import cv2
from multiprocessing import Process, Manager, cpu_count, set_start_method
import time
import numpy
import threading
import platform


# Multithreaded face recognition
# Get the id of the next thread
def next_id(current_id, worker_num):
    if current_id == worker_num:
        return 1
    else:
        return current_id + 1


# Gets the id of the previous thread
def prev_id(current_id, worker_num):
    if current_id == 1:
        return worker_num
    else:
        return current_id - 1


# Graph reading thread
def capture(read_frame_list, Global, worker_num):
    # Read video
    video_capture = cv2.VideoCapture('./test_img/short_hamilton_clip.mp4')
    print("Width: %d, Height: %d, FPS: %d" % (video_capture.get(3), video_capture.get(4), video_capture.get(5)))

    while not Global.is_exit:
        # Judge whether to read the image, and ensure that the thread currently caching the image and the next thread processing the image are not the same thread, so as to ensure that the image is cached before the processing thread starts
        if Global.buff_num != next_id(Global.read_num, worker_num):
            # Read an image
            ret, frame = video_capture.read()
            read_frame_list[Global.buff_num] = frame # Save the image to be processed by the corresponding image processing thread
            Global.buff_num = next_id(Global.buff_num, worker_num) # The next image processing thread to cache the image
        else:
            time.sleep(0.01)

    # Release video
    video_capture.release()


# Image processing thread
def process(worker_id, read_frame_list, write_frame_list, Global, worker_num):
    known_face_encodings = Global.known_face_encodings
    known_face_names = Global.known_face_names
    while not Global.is_exit:

        # Wait to read the image. When the thread is the thread that needs to process the image, start processing the image and ensure that the image has been cached
        while Global.read_num != worker_id or Global.read_num != prev_id(Global.buff_num, worker_num):
            # Determine whether to exit
            if Global.is_exit:
                break

            time.sleep(0.01)

        # Delayed read guarantee calculation
        time.sleep(Global.frame_delay)

        # Read an image
        frame_process = read_frame_list[worker_id]

        # Set the next thread to read video
        Global.read_num = next_id(Global.read_num, worker_num)

        # Switching channel
        rgb_frame = frame_process[:, :, ::-1]

        # Face recognition
        face_locations = face_recognition.face_locations(rgb_frame)
        face_encodings = face_recognition.face_encodings(rgb_frame, face_locations)

        # Face drawing
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            # Does it match a known face
            matches = face_recognition.compare_faces(known_face_encodings, face_encoding)

            name = "Unknown"

            # If there is a matching face, replace it with the name that matches the face
            if True in matches:
                first_match_index = matches.index(True)
                name = known_face_names[first_match_index]

            # Draw bounding box
            cv2.rectangle(frame_process, (left, top), (right, bottom), (0, 0, 255), 2)

            # Draw face Tags
            cv2.rectangle(frame_process, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(frame_process, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)

        # Whether the current thread allows saving images
        while Global.write_num != worker_id:
            time.sleep(0.01)

        # Save results
        write_frame_list[worker_id] = frame_process

        # Next image processing thread to save image
        Global.write_num = next_id(Global.write_num, worker_num)


if __name__ == '__main__':

    # Macos settings
    if platform.system() == 'Darwin':
        set_start_method('forkserver')

    # global variable
    Global = Manager().Namespace()
    Global.buff_num = 1 # Image processing thread caching image
    Global.read_num = 1 # Image thread processing image
    Global.write_num = 1 # Image processing thread saving results
    Global.frame_delay = 0 # delay time 
    Global.is_exit = False # Exit
    read_frame_list = Manager().dict()
    write_frame_list = Manager().dict()

    # Number of processing threads
    if cpu_count() > 2:
        # Minus 1 is to set aside a thread to read the video
        worker_num = cpu_count() - 1
    else:
        worker_num = 2

    # List of child threads
    p = []

    # Create a thread to capture frames (if a child thread is used, it will crash on the Mac)
    # Thread 0 is a graph reading thread
    p.append(threading.Thread(target=capture, args=(read_frame_list, Global, worker_num,)))
    p[0].start()

    # Read existing image
    obama_image = face_recognition.load_image_file("./test_img/obama.jpg")
    obama_face_encoding = face_recognition.face_encodings(obama_image)[0]
    biden_image = face_recognition.load_image_file("./test_img/lin-manuel-miranda.png")
    biden_face_encoding = face_recognition.face_encodings(biden_image)[0]

    # Create existing data information
    Global.known_face_encodings = [
        obama_face_encoding,
        biden_face_encoding
    ]
    Global.known_face_names = [
        "Barack Obama",
        "lin-manuel-miranda."
    ]

    # Create image processing sub thread
    for worker_id in range(1, worker_num + 1):
        p.append(Process(target=process, args=(worker_id, read_frame_list, write_frame_list, Global, worker_num,)))
        p[worker_id].start()

    # Start video reading
    last_num = 1 # Processed image serial number
    fps_list = []
    tmp_time = time.time()
    while not Global.is_exit:
        while Global.write_num != last_num:
            last_num = int(Global.write_num)

            # Calculate FPS
            delay = time.time() - tmp_time
            tmp_time = time.time()
            fps_list.append(delay)
            if len(fps_list) > 5 * worker_num:
                fps_list.pop(0)
            fps = len(fps_list) / numpy.sum(fps_list)
            print("fps: %.2f" % fps)

            # Dynamically adjust the detection performance according to the delay
            if fps < 6:
                Global.frame_delay = (1 / fps) * 0.75
            elif fps < 20:
                Global.frame_delay = (1 / fps) * 0.5
            elif fps < 30:
                Global.frame_delay = (1 / fps) * 0.25
            else:
                Global.frame_delay = 0

            # Show results
            cv2.imshow('Video', write_frame_list[prev_id(Global.write_num, worker_num)])

        # sign out
        if cv2.waitKey(1) & 0xFF == ord('q'):
            Global.is_exit = True
            break

        time.sleep(0.01)

5 reference

5.1 code

5.2 documentation

Topics: Python Computer Vision Deep Learning