Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

face_recognition User Guide

Generated May 2026 from ageitgey/face_recognition at 2e2dcce

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Quick Start

face_recognition is the world's simplest Python library for recognizing and manipulating faces. Built on top of dlib's state-of-the-art deep learning models, it provides both a Python API and command-line tools for face detection, landmark identification, and face recognition with 99.38% accuracy on the Labeled Faces in the Wild benchmark.

Key features:

  • Detect faces in images using HOG or CNN models
  • Identify 68 facial landmarks (eyes, nose, mouth, chin, eyebrows)
  • Generate 128-dimensional face encodings for comparison
  • Compare faces with configurable tolerance thresholds
  • Process images from files, webcams, or video streams
  • Two CLI tools for scripted/batch workflows
  • GPU acceleration via CUDA when dlib is compiled with CUDA support
  • Multi-core parallel processing for batch image operations

Prerequisites: Python 3.5+ and a C++ compiler (for dlib)

Install:

pip install face_recognition

Minimal example:

import face_recognition

# Load images
known_image = face_recognition.load_image_file("known_person.jpg")
unknown_image = face_recognition.load_image_file("unknown_person.jpg")

# Encode faces
known_encoding = face_recognition.face_encodings(known_image)[0]
unknown_encoding = face_recognition.face_encodings(unknown_image)[0]

# Compare
results = face_recognition.compare_faces([known_encoding], unknown_encoding)
print(results)
[True]

Tip

For the fastest path to a working demo, use the face_recognition CLI tool — no Python code needed. See the Command-Line Tools section below.

What to try next:

  • Use the CLI tools for quick face detection and recognition
  • Explore facial landmarks for feature extraction
  • Build a web service with the Flask example
  • Set up real-time webcam recognition

2. Installation

System Requirements

Requirement Minimum Recommended
Python 3.5 3.8+
OS Linux, macOS, Windows Ubuntu/Debian Linux
RAM 2 GB 4 GB+
C++ Compiler Required (for dlib) gcc/g++ on Linux, Xcode on macOS
GPU (optional) NVIDIA with CUDA For CNN model and batch processing

Install Methods

pip (recommended)

pip install face_recognition

Docker (CPU)

docker run -it --rm -v /path/to/images:/images ageitgey/face_recognition

Docker (GPU)

docker run -it --rm --runtime=nvidia -v /path/to/images:/images ageitgey/face_recognition:gpu

From source

git clone https://github.com/ageitgey/face_recognition.git
cd face_recognition
pip install -r requirements.txt
python setup.py install

Important

The dlib dependency requires a C++ compiler. On Ubuntu/Debian, install build tools first: sudo apt-get install build-essential cmake. On macOS, install Xcode command-line tools: xcode-select --install. On Windows, install Visual Studio Build Tools with C++ workload.

Dependencies

Package Version Purpose
face_recognition_models >=0.3.0 Pre-trained dlib model files (face detection, landmarks, encoding)
dlib >=19.7 Core C++ machine learning library
Click >=6.0 Command-line interface framework
numpy any Array operations for image data and encodings
Pillow any Image loading and format conversion
scipy >=0.17.0 Scientific computing (used in some examples)

Post-Install Verification

face_detection --help
Usage: face_detection [OPTIONS] IMAGE_TO_CHECK

Options:
  --cpus INTEGER     number of CPU cores to use in parallel. -1 means "use
                     all in system"
  --model TEXT       Which face detection model to use. Options are "hog" or
                     "cnn".
  --upsample INTEGER How many times to upsample the image looking for faces.
                     Higher numbers find smaller faces.
  --help             Show this message and exit.
python -c "import face_recognition; print(face_recognition.__version__)"
1.4.0

Install Troubleshooting

Error: Please install face_recognition_models

Cause: The face_recognition_models package was not installed or failed to install. This package contains the pre-trained model data files required by dlib.

Solution:

  1. Install the models package directly from GitHub:
    pip install git+https://github.com/ageitgey/face_recognition_models
    
  2. Verify installation:
    python -c "import face_recognition_models; print('OK')"
    

Prevention: Always install via pip install face_recognition, which pulls in models as a dependency automatically.

Warning

Issues #1634 and #608 report this error frequently. If pip install face_recognition does not install the models package, install it manually from the GitHub URL above.

Error: RuntimeError: Unsupported image type, must be 8bit gray or RGB image

Cause: The image has an unsupported pixel format — typically a 32-bit RGBA PNG, a CMYK JPEG, or a 16-bit image. The dlib library only accepts 8-bit grayscale or 8-bit RGB images.

Solution:

  1. Convert the image to RGB before processing:
    image = face_recognition.load_image_file("photo.png", mode='RGB')
    
  2. The load_image_file() function converts to RGB by default (mode='RGB'). If you're loading images manually with PIL or OpenCV, ensure you convert:
    from PIL import Image
    import numpy as np
    img = np.array(Image.open("photo.png").convert("RGB"))
    

Prevention: Always use face_recognition.load_image_file() instead of loading images manually. It handles format conversion automatically. Issues #1573, #1575, #21.

Error: dlib compilation fails

Cause: Missing C++ compiler or CMake.

Solution:

Ubuntu/Debian

sudo apt-get install build-essential cmake
pip install dlib

macOS

xcode-select --install
brew install cmake
pip install dlib

Windows

pip install cmake
pip install dlib

Prevention: Install build tools before attempting to install face_recognition.

Error: cv2.error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'

Cause: OpenCV received an empty frame from the video capture device. This happens when the webcam is not connected, is in use by another application, or the video file path is incorrect.

Solution:

  1. Check that the webcam is connected and not in use by another application
  2. Verify the video file path exists
  3. Add a frame check in your code:
    ret, frame = video_capture.read()
    if not ret:
        print("Failed to capture frame")
        break
    

Prevention: Always check the return value of video_capture.read() before processing the frame. Issue #933.

Error: pkg_resources deprecation warning

Cause: The pkg_resources package (used internally by face_recognition_models) is scheduled for removal. Issue #1653.

Solution:

pip install setuptools

Prevention: Keep setuptools installed and up to date.

3. Core Usage

Loading Images

Every workflow starts with loading an image into a numpy array:

import face_recognition

image = face_recognition.load_image_file("photo.jpg")

The load_image_file() function accepts file paths or file-like objects and returns a numpy array.

Parameter Type Default Description
file str or file object — Image file path or file object
mode str 'RGB' Color mode: 'RGB' (3-channel color) or 'L' (grayscale)
# Load as grayscale
gray_image = face_recognition.load_image_file("photo.jpg", mode='L')

# Load from file object
with open("photo.jpg", "rb") as f:
    image = face_recognition.load_image_file(f)

Note

Supported image formats include JPEG, PNG, BMP, GIF, and any other format supported by Pillow. 32-bit images are automatically converted to 8-bit RGB when using the default mode='RGB'.

Detecting Faces

Locate all faces in an image and get their bounding box coordinates:

image = face_recognition.load_image_file("group_photo.jpg")
face_locations = face_recognition.face_locations(image)

for top, right, bottom, left in face_locations:
    print(f"Face found at: Top={top}, Right={right}, Bottom={bottom}, Left={left}")
Face found at: Top=142, Right=617, Bottom=409, Left=349
Parameter Type Default Description
img numpy array — The image to search
number_of_times_to_upsample int 1 How many times to upsample the image. Higher values find smaller faces but are slower.
model str "hog" Detection model: "hog" (faster, CPU) or "cnn" (more accurate, GPU-accelerated)

Face locations are returned as tuples in (top, right, bottom, left) order.

# Use CNN model for higher accuracy (requires GPU for reasonable speed)
face_locations = face_recognition.face_locations(image, model="cnn")

# Find smaller faces by upsampling
face_locations = face_recognition.face_locations(image, number_of_times_to_upsample=2)

Tip

The HOG model is the right choice for most use cases — it runs well on CPU and is fast enough for real-time webcam processing. Use CNN only if you need higher accuracy and have a CUDA-capable GPU.

Batch Face Detection (GPU)

Process multiple images in a single GPU batch for significantly faster throughput:

images = [face_recognition.load_image_file(f) for f in ["img1.jpg", "img2.jpg", "img3.jpg"]]
batch_locations = face_recognition.batch_face_locations(images, number_of_times_to_upsample=0)

for i, face_locations in enumerate(batch_locations):
    print(f"Image {i}: found {len(face_locations)} face(s)")
Parameter Type Default Description
images list of numpy arrays — List of images to process
number_of_times_to_upsample int 1 Upsample count for face detection
batch_size int 128 Number of images per GPU batch

Important

batch_face_locations() always uses the CNN model internally. It only provides a speed benefit when using a CUDA-capable GPU. On CPU-only systems, use face_locations() in a loop instead.

Finding Facial Landmarks

Identify facial features (eyes, nose, mouth, chin, eyebrows) for each detected face:

image = face_recognition.load_image_file("photo.jpg")
landmarks_list = face_recognition.face_landmarks(image)

for face_landmarks in landmarks_list:
    for feature_name, points in face_landmarks.items():
        print(f"{feature_name}: {len(points)} points")
chin: 17 points
left_eyebrow: 5 points
right_eyebrow: 5 points
nose_bridge: 4 points
nose_tip: 5 points
left_eye: 6 points
right_eye: 6 points
top_lip: 12 points
bottom_lip: 12 points
Parameter Type Default Description
face_image numpy array — The image to search
face_locations list or None None Pre-computed face locations (skips detection if provided)
model str "large" Landmark model: "large" (68 points, 9 features) or "small" (5 points, 3 features)

Large model features (68 landmarks):

Feature Points
chin 17
left_eyebrow 5
right_eyebrow 5
nose_bridge 4
nose_tip 5
left_eye 6
right_eye 6
top_lip 12
bottom_lip 12

Small model features (5 landmarks):

Feature Points
nose_tip 1
left_eye 2
right_eye 2

Warning

Passing an invalid model name (anything other than "large" or "small") raises ValueError: Invalid landmarks model type. Supported models are ['small', 'large'].

Encoding Faces

Generate a 128-dimensional encoding vector for each face, used for comparison:

image = face_recognition.load_image_file("person.jpg")
encodings = face_recognition.face_encodings(image)

print(f"Found {len(encodings)} face(s)")
print(f"Encoding dimensions: {len(encodings[0])}")
Found 1 face(s)
Encoding dimensions: 128
Parameter Type Default Description
face_image numpy array — Image containing faces
known_face_locations list or None None Pre-computed face locations (skips detection if provided)
num_jitters int 1 Times to re-sample each face for encoding. Higher is more accurate but slower (100 = 100x slower).
model str "small" Landmark model used internally: "large" or "small"
# Higher accuracy encoding (slower)
encodings = face_recognition.face_encodings(image, num_jitters=10)

# Skip face detection by providing known locations
locations = face_recognition.face_locations(image)
encodings = face_recognition.face_encodings(image, known_face_locations=locations)

Comparing Faces

Check if faces match by comparing their encodings:

known_image = face_recognition.load_image_file("known_person.jpg")
unknown_image = face_recognition.load_image_file("unknown_person.jpg")

known_encoding = face_recognition.face_encodings(known_image)[0]
unknown_encoding = face_recognition.face_encodings(unknown_image)[0]

results = face_recognition.compare_faces([known_encoding], unknown_encoding)
print(f"Match: {results[0]}")
Match: True
Parameter Type Default Description
known_face_encodings list — List of known face encodings to compare against
face_encoding_to_check numpy array — The face encoding to check
tolerance float 0.6 Distance threshold for a match. Lower is stricter.

Measuring Face Distance

Get the euclidean distance between face encodings for fine-grained similarity:

distances = face_recognition.face_distance(known_encodings, unknown_encoding)

for i, distance in enumerate(distances):
    print(f"Distance to person {i}: {distance:.4f}")
    print(f"  Match at 0.6 threshold: {distance <= 0.6}")
    print(f"  Match at 0.5 threshold: {distance <= 0.5}")
Distance to person 0: 0.3142
  Match at 0.6 threshold: True
  Match at 0.5 threshold: True
Distance to person 1: 0.7856
  Match at 0.6 threshold: False
  Match at 0.5 threshold: False
Parameter Type Default Description
face_encodings list — List of face encodings to compare
face_to_compare numpy array — The face encoding to measure against

Tip

The default tolerance of 0.6 works well for most cases. Lower it to 0.5 to reduce false positives at the cost of more false negatives. Use face_distance() to find the optimal threshold for your dataset.

Command-Line Tools

Two CLI commands are installed with the package:

face_recognition — Identify Faces

Compares faces in images against a folder of known people:

face_recognition ./known_people/ ./unknown_pictures/
unknown_pictures/photo1.jpg,Barack Obama
unknown_pictures/photo2.jpg,unknown_person
unknown_pictures/photo3.jpg,Joe Biden

The known people folder should contain one image per person, with the filename as the person's name (e.g., Barack_Obama.jpg).

Argument/Option Default Description
known_people_folder — Directory of known face images (filename = person name)
image_to_check — Image file or directory to check
--cpus 1 CPU cores for parallel processing. -1 = all cores.
--tolerance 0.6 Match tolerance. Lower = stricter.
--show-distance False Include face distance in output.
# Process a folder with all CPU cores
face_recognition --cpus -1 ./known_people/ ./photo_folder/

# Stricter matching with distance output
face_recognition --tolerance 0.5 --show-distance 1 ./known_people/ ./test.jpg
test.jpg,Barack Obama,0.3142

Note

Images larger than 1600px on either dimension are automatically scaled down to 1600x1600 for faster processing. Recognition still works on the scaled image.

face_detection — Locate Faces

Finds faces in images and outputs bounding box coordinates:

face_detection ./photo.jpg
photo.jpg,142,617,409,349

Output format: filename,top,right,bottom,left

Argument/Option Default Description
image_to_check — Image file or directory to scan
--cpus 1 CPU cores for parallel processing. -1 = all cores.
--model "hog" Detection model: "hog" or "cnn"
--upsample 0 Times to upsample for smaller face detection
# Use CNN model for higher accuracy
face_detection --model cnn ./photos/

# Find small faces with upsampling
face_detection --upsample 2 ./group_photo.jpg

4. Common Tasks

Recognize Faces in a Webcam Stream

import face_recognition
import cv2
import numpy as np

# Load known faces
known_image = face_recognition.load_image_file("known_person.jpg")
known_encoding = face_recognition.face_encodings(known_image)[0]
known_names = ["Known Person"]
known_encodings = [known_encoding]

video_capture = cv2.VideoCapture(0)

while True:
    ret, frame = video_capture.read()
    if not ret:
        break

    rgb_frame = frame[:, :, ::-1]

    face_locations = face_recognition.face_locations(rgb_frame)
    face_encodings = face_recognition.face_encodings(rgb_frame, face_locations)

    for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
        matches = face_recognition.compare_faces(known_encodings, face_encoding)
        name = "Unknown"

        face_distances = face_recognition.face_distance(known_encodings, face_encoding)
        best_match_index = np.argmin(face_distances)
        if matches[best_match_index]:
            name = known_names[best_match_index]

        cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
        cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
        cv2.putText(frame, name, (left + 6, bottom - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1)

    cv2.imshow('Video', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video_capture.release()
cv2.destroyAllWindows()

Tip

For better performance, process every other frame and resize to 1/4 size: small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25). Scale face coordinates back up by 4x for display.

Faster Webcam Recognition

Process at 1/4 resolution and skip every other frame:

import face_recognition
import cv2
import numpy as np

video_capture = cv2.VideoCapture(0)

# Load known faces
known_image = face_recognition.load_image_file("known_person.jpg")
known_encoding = face_recognition.face_encodings(known_image)[0]
known_encodings = [known_encoding]
known_names = ["Known Person"]

face_locations = []
face_names = []
process_this_frame = True

while True:
    ret, frame = video_capture.read()
    if not ret:
        break

    if process_this_frame:
        small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
        rgb_small_frame = small_frame[:, :, ::-1]

        face_locations = face_recognition.face_locations(rgb_small_frame)
        face_encodings_list = face_recognition.face_encodings(rgb_small_frame, face_locations)

        face_names = []
        for encoding in face_encodings_list:
            matches = face_recognition.compare_faces(known_encodings, encoding)
            name = "Unknown"
            distances = face_recognition.face_distance(known_encodings, encoding)
            best_idx = np.argmin(distances)
            if matches[best_idx]:
                name = known_names[best_idx]
            face_names.append(name)

    process_this_frame = not process_this_frame

    for (top, right, bottom, left), name in zip(face_locations, face_names):
        top *= 4; right *= 4; bottom *= 4; left *= 4
        cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
        cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
        cv2.putText(frame, name, (left + 6, bottom - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1)

    cv2.imshow('Video', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video_capture.release()
cv2.destroyAllWindows()

Recognize Faces in a Video File

import face_recognition
import cv2

input_movie = cv2.VideoCapture("input_video.mp4")
length = int(input_movie.get(cv2.CAP_PROP_FRAME_COUNT))

fourcc = cv2.VideoWriter_fourcc(*'XVID')
output_movie = cv2.VideoWriter('output.avi', fourcc, 29.97, (640, 360))

# Load known faces
known_image = face_recognition.load_image_file("known_person.png")
known_encoding = face_recognition.face_encodings(known_image)[0]

frame_number = 0
while True:
    ret, frame = input_movie.read()
    if not ret:
        break

    frame_number += 1
    rgb_frame = frame[:, :, ::-1]

    face_locations = face_recognition.face_locations(rgb_frame)
    face_encodings_list = face_recognition.face_encodings(rgb_frame, face_locations)

    for (top, right, bottom, left), encoding in zip(face_locations, face_encodings_list):
        match = face_recognition.compare_faces([known_encoding], encoding, tolerance=0.50)
        if match[0]:
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
            cv2.rectangle(frame, (left, bottom - 25), (right, bottom), (0, 0, 255), cv2.FILLED)
            cv2.putText(frame, "Known Person", (left + 6, bottom - 6),
                        cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255), 1)

    print(f"Writing frame {frame_number} / {length}")
    output_movie.write(frame)

input_movie.release()
cv2.destroyAllWindows()

Apply Digital Makeup

Use facial landmarks to draw on facial features:

from PIL import Image, ImageDraw
import face_recognition

image = face_recognition.load_image_file("photo.jpg")
face_landmarks_list = face_recognition.face_landmarks(image)

pil_image = Image.fromarray(image)
for face_landmarks in face_landmarks_list:
    d = ImageDraw.Draw(pil_image, 'RGBA')

    # Eyebrows
    d.polygon(face_landmarks['left_eyebrow'], fill=(68, 54, 39, 128))
    d.polygon(face_landmarks['right_eyebrow'], fill=(68, 54, 39, 128))

    # Lips
    d.polygon(face_landmarks['top_lip'], fill=(150, 0, 0, 128))
    d.polygon(face_landmarks['bottom_lip'], fill=(150, 0, 0, 128))

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

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

pil_image.show()

Blur Faces in a Webcam Stream

import face_recognition
import cv2

video_capture = cv2.VideoCapture(0)

while True:
    ret, frame = video_capture.read()
    if not ret:
        break

    small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
    face_locations = face_recognition.face_locations(small_frame, model="cnn")

    for top, right, bottom, left in face_locations:
        top *= 4; right *= 4; bottom *= 4; left *= 4
        face_image = frame[top:bottom, left:right]
        face_image = cv2.GaussianBlur(face_image, (99, 99), 30)
        frame[top:bottom, left:right] = face_image

    cv2.imshow('Video', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video_capture.release()
cv2.destroyAllWindows()

Build a KNN Face Classifier

For recognizing a large set of known people, train a k-nearest neighbors model:

import face_recognition
from face_recognition.face_recognition_cli import image_files_in_folder
from sklearn import neighbors
import os
import math
import pickle

def train(train_dir, model_save_path=None, n_neighbors=None, knn_algo='ball_tree'):
    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:
                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))))

    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

Important

The KNN example requires scikit-learn: pip install scikit-learn. Organize training images as train_dir/person_name/image.jpg — one subdirectory per person, each containing one or more face images of that person. Each training image must contain exactly one face.

Training directory structure:

train_dir/
├── person_1/
│   ├── img1.jpg
│   ├── img2.jpg
├── person_2/
│   ├── img1.jpg
│   └── img2.jpg
└── ...

Build a Face Recognition Web Service

import face_recognition
from flask import Flask, jsonify, request, redirect

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_image():
    if request.method == 'POST':
        if 'file' not in request.files:
            return redirect(request.url)
        file = request.files['file']
        if file.filename == '':
            return redirect(request.url)
        if file and allowed_file(file.filename):
            return detect_faces_in_image(file)
    return '''
    <!doctype html>
    <title>Face Recognition</title>
    <h1>Upload a picture to check for faces</h1>
    <form method="POST" enctype="multipart/form-data">
      <input type="file" name="file">
      <input type="submit" value="Upload">
    </form>
    '''

def detect_faces_in_image(file_stream):
    img = face_recognition.load_image_file(file_stream)
    encodings = face_recognition.face_encodings(img)
    result = {"face_found_in_image": len(encodings) > 0, "face_count": len(encodings)}
    return jsonify(result)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5001, debug=True)

Note

This requires Flask: pip install flask. Test with: curl -XPOST -F "file=@photo.jpg" http://127.0.0.1:5001

Use the eye aspect ratio (EAR) algorithm with facial landmarks:

import face_recognition
import cv2
from scipy.spatial import distance as dist

def get_ear(eye):
    A = dist.euclidean(eye[1], eye[5])
    B = dist.euclidean(eye[2], eye[4])
    C = dist.euclidean(eye[0], eye[3])
    return (A + B) / (2.0 * C)

video_capture = cv2.VideoCapture(0)

while True:
    ret, frame = video_capture.read()
    if not ret:
        break

    small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
    rgb_small_frame = small_frame[:, :, ::-1]

    face_landmarks_list = face_recognition.face_landmarks(rgb_small_frame)

    for face_landmarks in face_landmarks_list:
        left_ear = get_ear(face_landmarks['left_eye'])
        right_ear = get_ear(face_landmarks['right_eye'])

        if left_ear < 0.2 and right_ear < 0.2:
            print("EYES CLOSED")
        else:
            print("EYES OPEN")

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video_capture.release()
cv2.destroyAllWindows()

Run on a Raspberry Pi

import face_recognition
import picamera
import numpy as np

camera = picamera.PiCamera()
camera.resolution = (320, 240)
output = np.empty((240, 320, 3), dtype=np.uint8)

# Load a known face
known_image = face_recognition.load_image_file("known_person.jpg")
known_encoding = face_recognition.face_encodings(known_image)[0]

while True:
    camera.capture(output, format="rgb")

    face_locations = face_recognition.face_locations(output)
    face_encodings_list = face_recognition.face_encodings(output, face_locations)

    for face_encoding in face_encodings_list:
        match = face_recognition.compare_faces([known_encoding], face_encoding)
        if match[0]:
            print("Known person detected!")
        else:
            print("Unknown person")

Warning

Raspberry Pi requires picamera[array] module and camera enabled via raspi-config. Use a low resolution (320x240) for acceptable performance on RPi 2+.

5. Troubleshooting

Diagnostic Decision Tree

Problem?
├── Import error → Check dlib and face_recognition_models are installed
├── No faces found → Try upsampling, check image format, try CNN model
├── Wrong matches → Lower tolerance (try 0.5 or 0.4)
├── Too slow → Use HOG model, reduce image size, process fewer frames
├── Webcam not working → Check cv2.VideoCapture(0) returns frames
└── CUDA errors → Verify dlib was compiled with CUDA support

Error: ValueError: Invalid landmarks model type

Cause: An invalid model name was passed to face_landmarks(). Only "large" and "small" are supported.

Solution:

# Correct usage
landmarks = face_recognition.face_landmarks(image, model="large")  # 68 points
landmarks = face_recognition.face_landmarks(image, model="small")  # 5 points

Prevention: Only use "large" or "small" as the model parameter.

Error: RuntimeError: Error while calling cudaOccupancyMaxPotentialBlockSize

Cause: CUDA driver or runtime version mismatch, or insufficient GPU memory. Issue #1641.

Solution:

  1. Verify CUDA installation: nvidia-smi
  2. Ensure dlib was compiled with CUDA: python -c "import dlib; print(dlib.DLIB_USE_CUDA)"
  3. If CUDA is not available, fall back to HOG model:
    face_locations = face_recognition.face_locations(image, model="hog")
    

Prevention: Match CUDA toolkit version with your GPU driver version. Use HOG model if GPU is unreliable.

Error: compute_face_descriptor(): incompatible function arguments

Cause: Image data is in the wrong format — typically a PIL Image object was passed instead of a numpy array, or the array has the wrong dtype. Issue #1582.

Solution:

# Always use load_image_file() or convert properly
image = face_recognition.load_image_file("photo.jpg")
# OR
import numpy as np
from PIL import Image
image = np.array(Image.open("photo.jpg").convert("RGB"))

Prevention: Use face_recognition.load_image_file() for all image loading.

Multiprocessing output is interleaved

Cause: When using --cpus greater than 1, worker processes write to stdout concurrently, which can produce jumbled output. Issue #1665.

Solution: Post-process the output by sorting lines by filename:

face_recognition --cpus -1 ./known/ ./test/ | sort

Prevention: Use --cpus 1 if output ordering matters, or redirect to a file and sort afterward.

No faces detected in an image

Cause: Faces may be too small, the image may be low quality, or faces may be at extreme angles.

Solution:

  1. Increase upsampling:
    face_locations = face_recognition.face_locations(image, number_of_times_to_upsample=2)
    
  2. Try the CNN model for better accuracy:
    face_locations = face_recognition.face_locations(image, model="cnn")
    
  3. Ensure the image is in RGB format (not BGR from OpenCV):
    rgb_image = cv2_image[:, :, ::-1]
    

Prevention: Use the CNN model and higher upsampling for challenging images.

Caution

Face detection accuracy degrades significantly with very small faces (below ~80px), extreme head rotation (profile views), heavy occlusion, or very low-resolution images. There is no workaround for these physical limitations of the model.

Inaccurate photo recognition

Cause: The default tolerance of 0.6 may be too permissive for your dataset, leading to false positives. Issue #1647.

Solution:

  1. Lower the tolerance:
    results = face_recognition.compare_faces(known_encodings, unknown_encoding, tolerance=0.5)
    
  2. Use face_distance() to understand the distribution of distances in your dataset and select an appropriate threshold
  3. Use multiple training images per person for better encoding stability

Prevention: Test with --show-distance to calibrate the tolerance for your specific use case.

6. FAQ

Q: What is the accuracy of the face recognition?

The underlying model achieves 99.38% accuracy on the Labeled Faces in the Wild benchmark. Real-world accuracy depends on image quality, lighting, and face angles. The default tolerance of 0.6 balances false positives against false negatives.

Q: What is the difference between HOG and CNN face detection models?

HOG (Histogram of Oriented Gradients) is faster and works well on CPU. CNN (Convolutional Neural Network) is more accurate but significantly slower without GPU acceleration. HOG is the default and recommended for most use cases. Use CNN when you need higher accuracy and have a CUDA-capable GPU.

Q: How do I improve recognition accuracy?

Lower the tolerance (e.g., 0.5 instead of the default 0.6) to reduce false positives. Use num_jitters greater than 1 when computing encodings for more stable results. Provide multiple training images per person when building a classifier. Ensure images are well-lit and faces are reasonably frontal.

Q: Does face_recognition work with video files?

Yes. Use OpenCV (cv2) to read video frames, convert each frame from BGR to RGB (frame[:, :, ::-1]), and pass it to any face_recognition function. See the "Recognize Faces in a Video File" recipe above.

Q: Can I use a GPU for face detection?

Yes. Install dlib with CUDA support (requires NVIDIA GPU, CUDA toolkit, and cuDNN). Then use model="cnn" for face_locations() or use batch_face_locations() for GPU-batched processing.

Q: What image formats are supported?

Any format supported by Pillow, including JPEG, PNG, BMP, GIF, TIFF, and WebP. The load_image_file() function handles format conversion automatically.

Q: How do I handle images with no faces?

face_locations() returns an empty list [] if no faces are found. face_encodings() also returns an empty list. Always check the length before indexing:

encodings = face_recognition.face_encodings(image)
if len(encodings) > 0:
    encoding = encodings[0]
else:
    print("No faces found")

Q: What is the face encoding format?

Each face encoding is a 128-dimensional numpy array of float values. Encodings can be serialized with pickle or numpy.save() for storage. The euclidean distance between two encodings indicates how similar the faces are — distances below 0.6 typically indicate the same person.

Q: Can I use face_recognition on a Raspberry Pi?

Yes, on Raspberry Pi 2 or later. Install with pip install face_recognition (compilation will take a long time). Use low resolutions (320x240) and the picamera module for camera input. Performance will be slower than desktop systems.

Q: Does it work with OpenCV images?

Yes, but OpenCV uses BGR color order while face_recognition expects RGB. Convert with: rgb_image = cv2_image[:, :, ::-1]

For full architecture details, deployment options, and configuration reference, see the Admin Guide.