/ python

Face Detection on Your Photo Collection in Python

Have you ever wanted to cut out all the faces from your photo collection? In this article we show how this can be achieved in an entirely non-creepy way using face detection algorithms. Face detection is simply the process of finding all faces in an image. Face recognition on the other hand is the process of distinguishing faces to identify a particular person. Using some relatively simple code in Python/OpenCV we walk through a face-detection script that can be applied to your photo collection. A follow-up article will detail how these faces can be labelled with the names of the particular person.

Haar Cascades

Face detection and recognition are relatively easy tasks for humans, but have traditionally been challenging for machines. There are numerous approaches for tackling face detection but we will focus on the use of Haar features [1] which are used in OpenCV. Haar features are a set of convolution kernels such as the ones shown below in which a feature is obtained by subtracting the sum of the pixel values in white from those which are black.

violaFeatures

These features are trained on images of face and non-face images using the Adaboost classifier. To improve the efficiency of the prediction process we can use a cascade of classifiers in which the first classifier uses very few Haar features, the next one uses more etc., improving accuracy at each step. A window is dragged across the image in order to see whether a face falls within the window.

Face Detection in OpenCV

Fortunately OpenCV makes face detection very easy as it has a pre-trained cascade of classifiers for faces, eyes etc. Let's take a look at a function get_faces which takes as a parameter an image file name and will return all of the faces in the image.

# Import OpenCV
import cv2

def get_faces(filename):
    face_cascade = cv2.CascadeClassifier('/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml')
    eye_cascade = cv2.CascadeClassifier('/usr/share/opencv/haarcascades/haarcascade_eye.xml')
    img = cv2.imread(filename)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.2, 5)

    face_images = []

    for (x, y, w, h) in faces:
        roi_gray = gray[y:y + h, x:x + w]
        roi_color = img[y:y + h, x:x + w]

        eyes = eye_cascade.detectMultiScale(roi_gray)
        if len(eyes) >= 1:
            face_images.append(roi_color)

    return face_images

In lines 5 and 6 we create the cascades of classifiers for faces and eyes respectively. The input image is loaded and then converted to grayscale before performing the face detection using cv2.detectMultiScale. The output to this function is a list of rectangles of detected faces represented as a 4-tuple (x, y, width, height). In the for loop we run through the detected faces and just check if at least one eye is present in each face to cut down on the number of false positives. We could of course leave out this last step, and I didn't experiment much to see if this resulted in many missed face detections.

Next, we'll define a function scan_imagesto find all the faces in a given directory root_dir, including its subdirectories, and then output faces to files in output_dir:

def scan_images(root_dir, output_dir):
    image_extensions = ["jpg", "png"]
    num_faces = 0
    num_images = 0

    for dir_name, subdir_list, file_list in os.walk(root_dir):
        print('Scanning directory: %s' % dir_name)
        for filename in file_list:
            extension = os.path.splitext(filename)[1][1:]
            if extension in image_extensions:
                faces = get_faces(os.path.join(dir_name, filename))
                num_images += 1

                for face in faces:
                    face_filename = os.path.join(output_dir, "face{}.png".format(num_faces))
                    cv2.imwrite(face_filename, face)
                    print("\tWrote {} extracted from {}".format(face_filename, filename))
                    num_faces += 1

On line 6 we use os.walk to walk through all the subdirectories and files in root_dir. For image files (in this case png and jpeg files) we call get_faces and save the resulting faces into separate files in line 16.

To complete the code, we use the argparse module to parse command line arguments to the Python script so that the user can specify an image and output directory:

import argparse
parser = argparse.ArgumentParser(description='Extract faces from photos.')
parser.add_argument('imagesdir', type=str, help='Input directory of images')
parser.add_argument('outputdir', type=str, help='Output directory for faces')
args = parser.parse_args()

This completes the simple face detection script which is available here. Please give it a try on your photo collection!

How can this script be improved? We can process faces in parallel to make use of multi-core CPUs. We could also experiment more with the face detection parameters to get better accuracy rates. Finally, on a large photo collection, the user has no idea how long the script will take so an initial scan could give an estimate of timings.

Summary

This article shows how we can perform face detection using Python and OpenCV. We walked through the creation of a script which can scan a photo collection for faces, and then output cropped faces into a user-defined directory. In a future article we'll show how to perform face recognition on these images.

Footnotes


  1. Viola P, Jones M. Rapid object detection using a boosted cascade of simple features. In Computer Vision and Pattern Recognition, 2001. CVPR 2001. Proceedings of the 2001 IEEE Computer Society Conference on 2001 (Vol. 1, pp. I-511). IEEE. ↩︎