How to Cache Face Encodings for Faster Lookups in Python
Cache face encodings by computing 128-dimensional vectors once, serializing them with pickle, and loading them at startup to avoid expensive neural network recomputation.
When building face recognition systems with the ageitgey/face_recognition library, the biggest performance bottleneck is computing face encodings—the 128-dimensional vectors that represent facial features. This step runs a deep neural network via dlib and becomes prohibitively slow when processing large known-face databases. By caching these encodings to disk, you transform a computationally expensive operation into a fast NumPy array comparison.
Why Cache Face Encodings?
The face_recognition library generates encodings through the face_encodings() function in face_recognition/api.py (lines 203–215). This function invokes a dlib face recognition model that performs forward propagation through a deep neural network to produce a 128-dimensional vector for each detected face.
When you need to identify faces against a database of known individuals, recomputing these vectors for every lookup creates O(n) neural network invocations. By computing each known person's encoding once and persisting it, you reduce subsequent lookups to vectorized NumPy operations using face_distance() (lines 63–75 in api.py), which calculates Euclidean distance via np.linalg.norm.
Step-by-Step: How to Cache Face Encodings
Generate Encodings Once
Process your training images through face_encodings() to extract 128-dimensional vectors. This function accepts an image array and optional face locations, returning a list of encodings.
import face_recognition
image = face_recognition.load_image_file("person.jpg")
face_locations = face_recognition.face_locations(image)
encodings = face_recognition.face_encodings(image, known_face_locations=face_locations)
Serialize with Pickle
Persist the encodings using Python's standard pickle module or numpy.save. The KNN example in examples/face_recognition_knn.py (lines 104–107) demonstrates this pattern by saving a trained classifier to disk.
import pickle
# Save encodings dictionary: {name: [encoding, ...]}
with open("known_faces_cache.pkl", "wb") as f:
pickle.dump(known_encodings_dict, f)
Load at Startup
At application initialization, deserialize the cache into memory. This makes the encoding database available for instant comparison without neural network overhead. The KNN example shows the loading pattern at lines 130–132.
def load_cache(cache_path="known_faces_cache.pkl"):
if os.path.exists(cache_path):
with open(cache_path, "rb") as f:
return pickle.load(f)
return {}
Perform Fast Comparisons
With cached encodings loaded, use face_distance() to compute Euclidean distances between the unknown encoding and all known vectors. This function (lines 63–75 in api.py) performs a vectorized NumPy operation that is orders of magnitude faster than neural network inference.
import face_recognition
# known_encodings: list of 128-dim vectors from cache
# unknown_encoding: single 128-dim vector from new image
distances = face_recognition.face_distance(known_encodings, unknown_encoding)
best_match_index = distances.argmin()
if distances[best_match_index] < 0.6:
name = known_names[best_match_index]
Complete Working Example
This script implements a complete caching workflow, mirroring the patterns found in examples/face_recognition_knn.py and the core API implementation in face_recognition/api.py.
import os
import pickle
import pathlib
import face_recognition
CACHE_PATH = pathlib.Path("known_faces_cache.pkl")
def build_cache(known_dir):
"""
Scan directory where each subfolder is a person.
Compute one encoding per image and store dict:
{person_name: [encoding, ...]}
"""
cache = {}
for person in os.listdir(known_dir):
person_path = os.path.join(known_dir, person)
if not os.path.isdir(person_path):
continue
encodings = []
for img_file in os.listdir(person_path):
if img_file.startswith("."):
continue
img_path = os.path.join(person_path, img_file)
img = face_recognition.load_image_file(img_path)
faces = face_recognition.face_locations(img)
if len(faces) == 1:
enc = face_recognition.face_encodings(img, known_face_locations=faces)[0]
encodings.append(enc)
if encodings:
cache[person] = encodings
with open(CACHE_PATH, "wb") as f:
pickle.dump(cache, f)
print(f"Cache built with {len(cache)} people")
def load_cache():
"""Return dict {person: [encodings]} from disk, or empty dict."""
if CACHE_PATH.is_file():
with open(CACHE_PATH, "rb") as f:
return pickle.load(f)
return {}
def find_match(unknown_img_path, cache, tolerance=0.6):
"""
Encode unknown image and compare against cached encodings.
Returns (person_name, distance) or ('unknown', None).
"""
img = face_recognition.load_image_file(unknown_img_path)
faces = face_recognition.face_locations(img)
if not faces:
return None
unknown_enc = face_recognition.face_encodings(img, known_face_locations=faces)[0]
best_match = ("unknown", None)
for name, enc_list in cache.items():
distances = face_recognition.face_distance(enc_list, unknown_enc)
min_dist = distances.min()
if min_dist <= tolerance and (best_match[1] is None or min_dist < best_match[1]):
best_match = (name, float(min_dist))
return best_match
# Example usage
if __name__ == "__main__":
known_dir = "knn_examples/train"
if not CACHE_PATH.is_file():
build_cache(known_dir)
cache = load_cache()
result = find_match("test_image.jpg", cache)
print(f"Match: {result}")
Key Source Files and Implementation Details
Understanding the underlying implementation helps optimize your caching strategy:
-
face_recognition/api.py(lines 203–215): Containsface_encodings(), which converts face images into 128-dimensional vectors using thedlibface recognition model. This is the expensive operation to cache. -
face_recognition/api.py(lines 63–75): Implementsface_distance(), which computes Euclidean distance between encodings usingnp.linalg.norm. This is the fast operation you want to perform on cached data. -
face_recognition/api.py(lines 217–226): Containscompare_faces(), a convenience wrapper aroundface_distance()that applies a tolerance threshold. -
examples/face_recognition_knn.py(lines 104–107 and 130–132): Demonstrates the pickle serialization pattern for persisting trained models, which applies directly to encoding caches. -
examples/web_service_example.py: Shows real-time usage where pre-computed encodings enable instant comparison against uploaded images.
Summary
- Face encodings are 128-dimensional vectors generated by expensive neural network inference in
dlibviaface_recognition.face_encodings(). - Cache these vectors to disk using
pickleornumpy.saveafter the initial computation to avoid redundant processing. - Load encodings at startup to enable fast, vectorized distance calculations using
face_recognition.face_distance(), which performs simple NumPy operations rather than neural network inference. - Invalidate caches by regenerating the pickle file when adding or removing known individuals from your database.
- Reference implementation patterns exist in
examples/face_recognition_knn.pyand the core API inface_recognition/api.py.
Frequently Asked Questions
How much faster is cached face recognition compared to real-time encoding?
Cached face recognition is orders of magnitude faster. Computing a face encoding requires running a deep neural network through dlib, which takes hundreds of milliseconds per face. Once cached, face_recognition.face_distance() performs a vectorized NumPy Euclidean distance calculation across thousands of encodings in microseconds.
What is the best file format for caching face encodings?
Python's standard pickle module is the most common approach, as demonstrated in examples/face_recognition_knn.py (lines 104–107). Alternatively, use numpy.save() or numpy.savez() to store the 128-dimensional arrays if you need cross-language compatibility. Both methods preserve the exact floating-point precision required for accurate face matching.
How do I handle cache invalidation when adding new people?
When your known-face database changes, regenerate the cache by re-running your encoding script against the updated directory structure. Delete the old pickle file and rebuild it using face_recognition.face_encodings() for the new images. This follows the same pattern as the KNN example's training flow, where the model is retrained and resaved when the training directory changes.
Can I cache unknown face encodings during runtime?
Yes, for long-running services processing many images, cache unknown face encodings using functools.lru_cache or a custom dictionary keyed by image hash. This prevents recomputing encodings for identical images submitted multiple times. However, ensure the cache has size limits to prevent memory exhaustion in high-throughput applications.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →