Build a Face Recognition Memory System
Build a real-time face recognition app that remembers people you meet.
Introduction
30 Second Summary
You meet people all the time, but remembering every name, face, and conversation is impossible. What if your computer could remember everyone you have ever met?
In this project, you will build a face recognition memory system using Python. Your webcam becomes a face scanner that identifies people you have previously registered. Every person gets a profile with their name, face data, and notes from your conversations.
What You'll Build
A local people memory engine powered by DeepFace and OpenCV that detects and recognizes faces from your webcam in real time.
By the end of this project, you'll have:
- A face registration module that captures a face from your webcam and stores its vector embedding in a database.
- A real-time recognition loop that identifies known faces and displays their names on the video feed.
- A SQLite database that persistently stores names, face embeddings, personal notes, and timestamps.
- A secret mission that adds automatic facial attribute analysis to estimate age, gender, and emotion for every person you recognize.
Are there any prerequisites?
This project assumes basic Python knowledge, including variables, functions, loops, and dictionaries. You do not need any prior experience with computer vision or machine learning.
Before We Start
Before We Start...
Set Up Your Python Environment
Face recognition in Python relies on specialized libraries that handle the heavy lifting of detecting and identifying faces. Your project needs two key libraries: DeepFace for face recognition and OpenCV for webcam access.
Setting these up in a virtual environment keeps your project's dependencies isolated from other Python projects on your machine. By the end of this step, you'll have everything installed and verified with a quick webcam test.
In this step, get ready to:
- Install Python and set up a virtual environment.
- Install DeepFace and OpenCV.
- Verify your webcam and libraries with a test script.
Install Python and create your project folder
Python 3.10 or higher is required for this project. Let's check if you already have it installed.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
- Check your Python version by running this command:
macOS
python3 --version
Windows
python --version
I see a version number
You should see something like Python 3.10.x or higher. If your version is 3.10 or above, you're good to go.
Command not found
- Download Python from python.org/downloads.
- Download the latest Python 3.10+ installer for your operating system.
- Run the installer. On Windows, make sure to check Add Python to PATH before clicking Install Now.
- Close and reopen your terminal, then run the version check command again to confirm Python is installed.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir larp-ai
cd larp-ai
- Confirm the folder exists by running:
ls
The output should be empty since your folder is brand new.
A virtual environment is a self-contained Python setup that keeps this project's libraries separate from anything else on your machine.
- Create a virtual environment by running this command:
macOS
python3 -m venv venv
Windows
python -m venv venv
- Activate the virtual environment by running:
macOS
source venv/bin/activate
Windows
venv\Scripts\activate
You should see (venv) appear at the beginning of your terminal prompt. This confirms the virtual environment is active.
Don't see (venv) in your prompt?
- Make sure you're inside the larp-ai folder where the venv folder was created.
- On Windows, if you get a script execution error, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned in PowerShell first, then try the activate command again.
- help me activate my Python virtual environment
Install DeepFace and OpenCV
With your virtual environment active, you can install the two libraries your project depends on. DeepFace handles face detection and recognition. OpenCV handles webcam access and image processing.
- Install both libraries by running this command:
pip install deepface opencv-python
What is this installing?
This command installs deepface and opencv-python, but DeepFace automatically pulls in several large dependencies behind the scenes.
- TensorFlow powers the deep learning models that recognize faces.
- NumPy handles the math behind face embeddings.
The install may take a few minutes because of these large dependencies.
Once the install finishes, you should see Successfully installed followed by a list of package names near the bottom of the terminal output.
Install failing or hanging?
- Make sure (venv) is showing in your terminal prompt. If it's not, the virtual environment is not active.
- If you see a permissions error, confirm you're installing inside the virtual environment, not system-wide.
- TensorFlow is a large download. If the install seems stuck, give it a few more minutes on a slower connection.
- help me troubleshoot my pip install for deepface and opencv-python
What about TensorFlow GPU warnings?
When you use TensorFlow later, you may see warnings about a missing NVIDIA GPU. These are harmless. TensorFlow falls back to CPU processing, which works perfectly for this project.
Create and run a test script
Before building anything, it's worth confirming that your webcam and both libraries work together. A quick test script will verify everything in one shot.
- Create a new file called test_setup.py inside your larp-ai folder.
- Open test_setup.py in your code editor and paste the following code:
import cv2
from deepface import DeepFace
# Test that OpenCV is installed and print its version
print("Testing OpenCV...")
print(f"OpenCV version: {cv2.__version__}")
# Test that the webcam can open and capture a frame
print("\nTesting webcam...")
cap = cv2.VideoCapture(0)
if cap.isOpened():
ret, frame = cap.read()
if ret:
print(f"Webcam works! Frame size: {frame.shape}")
else:
print("Error: Could not read frame from webcam.")
cap.release()
else:
print("Error: Could not open webcam.")
# Test that DeepFace is importable and ready to use
print("\nTesting DeepFace (this may download models on first run)...")
try:
print("DeepFace imported successfully.")
print("Setup complete! You are ready to build larp.ai.")
except Exception as e:
print(f"Error: {e}")
What does this script do?
- It imports both libraries and prints the OpenCV version to confirm the install worked.
- cv2.VideoCapture(0) opens your default webcam. If it can read a frame, your camera is connected and accessible.
- The DeepFace import at the top confirms the library and all its dependencies loaded correctly.
- Save test_setup.py.
- Run the test script in your terminal by running this command:
macOS
python3 test_setup.py
Windows
python test_setup.py
You should see output that includes your OpenCV version, a confirmation that your webcam works with its frame size, and the message Setup complete! You are ready to build larp.ai. at the end.
First run might be slow
The first time you import DeepFace, TensorFlow loads its internal modules which can take 10-30 seconds. This is normal and only happens on the first run.
Seeing an error about your webcam?
- If you see Error: Could not open webcam, make sure no other app (Zoom, FaceTime, another browser tab) is using your camera.
- On macOS, check that your terminal app has camera permission in System Settings > Privacy & Security > Camera.
- If you see a ModuleNotFoundError, confirm your virtual environment is active (you should see (venv) in your prompt) and re-run the pip install command from the previous substep.
- help me fix my test_setup.py errors
Awesome, I've got everything!
Great. Double check you've saved test_setup.py and you're ready to move on.
I'd like to double check the full code
Here is the complete test_setup.py file:
import cv2
from deepface import DeepFace
print("Testing OpenCV...")
print(f"OpenCV version: {cv2.__version__}")
print("\nTesting webcam...")
cap = cv2.VideoCapture(0)
if cap.isOpened():
ret, frame = cap.read()
if ret:
print(f"Webcam works! Frame size: {frame.shape}")
else:
print("Error: Could not read frame from webcam.")
cap.release()
else:
print("Error: Could not open webcam.")
print("\nTesting DeepFace (this may download models on first run)...")
try:
print("DeepFace imported successfully.")
print("Setup complete! You are ready to build larp.ai.")
except Exception as e:
print(f"Error: {e}")
Your environment is ready. Next up, you'll build the SQLite database that stores every person your system recognizes.
Build the People Database
Your Python environment is ready with DeepFace and OpenCV installed. Now it's time to build the brain behind your memory system.
Before your app can recognize anyone, it needs a place to store what it knows. In this step, you'll create a SQLite database that holds each person's name, face embedding, interaction notes, and timestamps.
In this step, get ready to:
- Create a PeopleDatabase class that sets up SQLite tables for people and interactions.
- Add methods to store, search, and retrieve people and their notes.
- Build a face matching method using cosine similarity.
Create the database class and tables
SQLite is a lightweight database built right into Python. No server installation needed.
You'll define two tables: a people table to store each person's name and face data, and an interactions table to log notes about your conversations with them.
- Create a new file called database.py inside your larp-ai folder.
- Copy the following code into database.py to set up the imports, the class, and the table schema:
import sqlite3
import json
import numpy as np
from datetime import datetime
class PeopleDatabase:
def __init__(self, db_path="larp_memory.db"):
# Store the path and open a connection to the SQLite database
self.db_path = db_path
self.conn = sqlite3.connect(db_path)
# Row factory lets us access columns by name instead of index
self.conn.row_factory = sqlite3.Row
self._create_tables()
def _create_tables(self):
cursor = self.conn.cursor()
# People table stores each person's name, face embedding, and timestamps
cursor.execute("""
CREATE TABLE IF NOT EXISTS people (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
embedding TEXT NOT NULL,
notes TEXT DEFAULT '',
first_seen TEXT NOT NULL,
last_seen TEXT NOT NULL
)
""")
# Interactions table logs individual notes tied to a person
cursor.execute("""
CREATE TABLE IF NOT EXISTS interactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
person_id INTEGER NOT NULL,
note TEXT NOT NULL,
timestamp TEXT NOT NULL,
FOREIGN KEY (person_id) REFERENCES people(id)
)
""")
self.conn.commit()
What does this code do?
- The imports bring in sqlite3 for the database, json for converting embeddings to storable text, NumPy for math on arrays, and datetime for timestamps.
- The __init__ method opens a connection to a local database file called larp_memory.db. Setting row_factory = sqlite3.Row lets you access columns by name (like a dictionary) instead of by position number.
- The people table stores each person's name, face embedding (as JSON text), notes, and when they were first and last seen.
- The interactions table links notes to specific people using a foreign key. This keeps your conversation history organized separately from the person's core record.
- Save database.py.
- Verify the class imports cleanly by running this command in your terminal:
python -c "from database import PeopleDatabase; db = PeopleDatabase(); print('Database created!'); db.conn.close()"
You should see Database created! printed in your terminal. A new file called larp_memory.db also appears in your project folder.
Seeing a ModuleNotFoundError?
- Make sure your virtual environment is activated. You should see (venv) at the beginning of your terminal prompt.
- Make sure you're running the command from inside your larp-ai folder where database.py lives.
- If you see an error about numpy, it should have been installed automatically with DeepFace. Run pip install numpy to install it manually.
Help me debug this ModuleNotFoundError when importing my PeopleDatabase class.
Add methods to store and retrieve people
The database structure exists, but it has no way to read or write data yet. You'll add methods that create person records, log interaction notes, and query everything back.
- In database.py, add the add_person method inside the PeopleDatabase class, directly below the _create_tables method:
def add_person(self, name, embedding, note=""):
# Record the current time as both first_seen and last_seen
now = datetime.now().isoformat()
# Convert the embedding list to a JSON string for storage
embedding_json = json.dumps(embedding)
cursor = self.conn.cursor()
cursor.execute(
"INSERT INTO people (name, embedding, notes, first_seen, last_seen) VALUES (?, ?, ?, ?, ?)",
(name, embedding_json, note, now, now)
)
self.conn.commit()
person_id = cursor.lastrowid
# If a note was provided, also log it as an interaction
if note:
self.add_note(person_id, note)
return person_id
What does this method do?
- Face embeddings are lists of 512 numbers, which can't be stored directly in SQLite. json.dumps() converts the list into a JSON string so it fits in a text column.
- The method records the current timestamp as both first_seen and last_seen. The last_seen value will update every time the person is recognized later.
- If you pass in a note at registration time, it gets stored both on the person record and as the first entry in the interactions table.
- Save database.py.
You won't see a change yet. The next chunk adds query methods that let you read back the data you insert.
- Now add the four query and interaction methods below add_person:
def get_all_people(self):
# Retrieve basic info for every person in the database
cursor = self.conn.cursor()
cursor.execute("SELECT id, name, first_seen, last_seen FROM people")
return cursor.fetchall()
def add_note(self, person_id, note):
now = datetime.now().isoformat()
cursor = self.conn.cursor()
# Insert the note into the interactions table
cursor.execute(
"INSERT INTO interactions (person_id, note, timestamp) VALUES (?, ?, ?)",
(person_id, note, now)
)
# Update the person's last_seen timestamp
cursor.execute(
"UPDATE people SET last_seen = ? WHERE id = ?",
(now, person_id)
)
self.conn.commit()
def get_person_history(self, name):
# Join people and interactions to get all notes for a person
cursor = self.conn.cursor()
cursor.execute(
"SELECT p.name, p.first_seen, p.last_seen, i.note, i.timestamp "
"FROM people p LEFT JOIN interactions i ON p.id = i.person_id "
"WHERE p.name LIKE ? ORDER BY i.timestamp DESC",
(f"%{name}%",)
)
return cursor.fetchall()
def search_person(self, name):
# Search for people whose name partially matches the query
cursor = self.conn.cursor()
cursor.execute(
"SELECT id, name, first_seen, last_seen FROM people WHERE name LIKE ?",
(f"%{name}%",)
)
return cursor.fetchall()
What do these methods do?
- get_all_people() returns every person in the database with their name and timestamps. This powers the "list everyone" feature.
- add_note() inserts a note into the interactions table and also updates the person's last_seen timestamp. Every interaction is a fresh sighting.
- get_person_history() uses a SQL JOIN to pull a person's record together with all their interaction notes, sorted by most recent first.
- search_person() uses a LIKE query with wildcards so you can search for partial names. Typing "Sar" would match "Sarah".
- Save database.py.
- Verify the CRUD methods work by running this quick test in your terminal:
python -c "
from database import PeopleDatabase
db = PeopleDatabase()
pid = db.add_person('Test Person', [0.1] * 512, 'Met at the office')
print(f'Added person with ID: {pid}')
people = db.get_all_people()
for p in people:
print(f' {p[chr(110)+chr(97)+chr(109)+chr(101)]}')
db.conn.close()
"
You should see Added person with ID: 1 followed by Test Person printed in your terminal.
Seeing an IndentationError?
- Every method must be indented one level inside the PeopleDatabase class. Check that each def line is indented by exactly 4 spaces from the class definition.
- If you see NameError: name 'self' is not defined, your method is likely outside the class. Make sure it sits between the class header and the end of the file, at the correct indentation level.
Help me fix an indentation or syntax error in my PeopleDatabase class.
Build the face matching method
The database can store and retrieve people, but it can't yet answer the most important question: "Have I seen this face before?"
Face matching works by comparing vector embeddings. Each face gets converted into a list of 512 numbers. To check if two faces belong to the same person, you measure the cosine similarity between their embeddings. The closer the distance is to zero, the more similar the faces are.
- In database.py, add the find_match method below search_person:
def find_match(self, embedding, threshold=0.40):
# Load all stored people and their embeddings
cursor = self.conn.cursor()
cursor.execute("SELECT id, name, embedding FROM people")
rows = cursor.fetchall()
best_match = None
best_distance = float("inf")
# Convert the input embedding to a NumPy array for math
target = np.array(embedding)
for row in rows:
# Parse the stored embedding from JSON back to an array
stored = np.array(json.loads(row["embedding"]))
# Compute cosine distance (0 = identical, 1 = completely different)
distance = 1 - np.dot(target, stored) / (
np.linalg.norm(target) * np.linalg.norm(stored)
)
if distance < best_distance:
best_distance = distance
best_match = row
# Only return a match if the distance is below the threshold
if best_match and best_distance < threshold:
return {
"id": best_match["id"],
"name": best_match["name"],
"distance": best_distance
}
return None
How does cosine distance work?
Cosine distance measures how different two vectors are by looking at the angle between them. A distance of 0 means the vectors point in the exact same direction (identical faces). A distance of 1 means they point in completely different directions (totally different faces).
The threshold of 0.40 is slightly lenient to account for real-world webcam conditions like varying lighting and angles. If you get too many false matches, lower it. If the system fails to recognize people, raise it.
- Save database.py.
You won't see a change yet. The next chunk adds the final utility methods to complete the class.
- Add the last two utility methods below find_match to complete the class:
def update_last_seen(self, person_id):
# Refresh the last_seen timestamp for a recognized person
now = datetime.now().isoformat()
cursor = self.conn.cursor()
cursor.execute(
"UPDATE people SET last_seen = ? WHERE id = ?",
(now, person_id)
)
self.conn.commit()
def close(self):
self.conn.close()
What do these methods do?
- update_last_seen() refreshes the timestamp every time the recognition system spots someone. This lets you track when you last encountered each person.
- close() cleanly shuts down the database connection. You'll call this whenever your app exits.
- Save database.py.
- Delete the test database from the previous test so you start fresh. Run this command:
rm larp_memory.db
- Test the full database module, including face matching, by running this command:
python -c "
from database import PeopleDatabase
db = PeopleDatabase()
print('Adding two people...')
db.add_person('Alice', [0.1] * 512, 'Met at the coffee shop')
db.add_person('Bob', [0.9] * 512, 'Works in engineering')
print('\nAll people:')
for p in db.get_all_people():
print(f' {p[1]} (ID: {p[0]})')
print('\nTesting face match with Alice-like embedding...')
match = db.find_match([0.1] * 512)
print(f' Match: {match}')
print('\nTesting with unknown embedding...')
no_match = db.find_match([0.5] * 512)
print(f' Match: {no_match}')
db.close()
print('\nAll tests passed!')
"
What should I see?
You should see Alice and Bob listed, then a match result showing Alice's name with a very low distance (close to 0). The unknown embedding test should return None because [0.5] * 512 is too far from either stored embedding to match.
Match returning None when it shouldn't?
- Check that your find_match method uses json.loads(row["embedding"]) to parse the stored embedding back from JSON text into a list.
- Verify the cosine distance formula uses 1 - np.dot(...) (one minus the dot product divided by the norms). A typo here produces wrong distances.
- Make sure you deleted the old larp_memory.db file before running this test. Old test data could skew the results.
Help me debug my find_match method. It's returning None even for identical embeddings.
Awesome, I've got everything!
Double check you've saved database.py and that all tests passed.
I'd like to double check the full code
Here is the complete database.py file. Compare yours against it to make sure nothing is missing.
import sqlite3
import json
import numpy as np
from datetime import datetime
class PeopleDatabase:
def __init__(self, db_path="larp_memory.db"):
self.db_path = db_path
self.conn = sqlite3.connect(db_path)
self.conn.row_factory = sqlite3.Row
self._create_tables()
def _create_tables(self):
cursor = self.conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS people (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
embedding TEXT NOT NULL,
notes TEXT DEFAULT '',
first_seen TEXT NOT NULL,
last_seen TEXT NOT NULL
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS interactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
person_id INTEGER NOT NULL,
note TEXT NOT NULL,
timestamp TEXT NOT NULL,
FOREIGN KEY (person_id) REFERENCES people(id)
)
""")
self.conn.commit()
def add_person(self, name, embedding, note=""):
now = datetime.now().isoformat()
embedding_json = json.dumps(embedding)
cursor = self.conn.cursor()
cursor.execute(
"INSERT INTO people (name, embedding, notes, first_seen, last_seen) VALUES (?, ?, ?, ?, ?)",
(name, embedding_json, note, now, now)
)
self.conn.commit()
person_id = cursor.lastrowid
if note:
self.add_note(person_id, note)
return person_id
def get_all_people(self):
cursor = self.conn.cursor()
cursor.execute("SELECT id, name, first_seen, last_seen FROM people")
return cursor.fetchall()
def add_note(self, person_id, note):
now = datetime.now().isoformat()
cursor = self.conn.cursor()
cursor.execute(
"INSERT INTO interactions (person_id, note, timestamp) VALUES (?, ?, ?)",
(person_id, note, now)
)
cursor.execute(
"UPDATE people SET last_seen = ? WHERE id = ?",
(now, person_id)
)
self.conn.commit()
def get_person_history(self, name):
cursor = self.conn.cursor()
cursor.execute(
"SELECT p.name, p.first_seen, p.last_seen, i.note, i.timestamp "
"FROM people p LEFT JOIN interactions i ON p.id = i.person_id "
"WHERE p.name LIKE ? ORDER BY i.timestamp DESC",
(f"%{name}%",)
)
return cursor.fetchall()
def search_person(self, name):
cursor = self.conn.cursor()
cursor.execute(
"SELECT id, name, first_seen, last_seen FROM people WHERE name LIKE ?",
(f"%{name}%",)
)
return cursor.fetchall()
def find_match(self, embedding, threshold=0.40):
cursor = self.conn.cursor()
cursor.execute("SELECT id, name, embedding FROM people")
rows = cursor.fetchall()
best_match = None
best_distance = float("inf")
target = np.array(embedding)
for row in rows:
stored = np.array(json.loads(row["embedding"]))
distance = 1 - np.dot(target, stored) / (
np.linalg.norm(target) * np.linalg.norm(stored)
)
if distance < best_distance:
best_distance = distance
best_match = row
if best_match and best_distance < threshold:
return {
"id": best_match["id"],
"name": best_match["name"],
"distance": best_distance
}
return None
def update_last_seen(self, person_id):
now = datetime.now().isoformat()
cursor = self.conn.cursor()
cursor.execute(
"UPDATE people SET last_seen = ? WHERE id = ?",
(now, person_id)
)
self.conn.commit()
def close(self):
self.conn.close()
Your people database is fully built and tested. Next up, you'll connect your webcam to capture real faces and register them into this database.
Register Faces from Your Webcam
Your people database is ready to store names, embeddings, and notes. Now it needs actual face data to work with.
In this step, you will build a registration script that opens your webcam, captures a face on command, and saves it to the database. This is the entry point for your memory system.
In this step, get ready to:
- Capture a face from your webcam and extract its 512-dimensional embedding.
- Save the face data to your people database with a name and optional note.
- Add error handling for failed face detections and register your first faces.
Capture a face and extract its embedding
To recognize someone later, your system needs a numerical fingerprint of their face. DeepFace can look at a photo and produce a list of 512 numbers (called an embedding) that uniquely represent that face.
What is a face embedding?
A face embedding is a list of 512 numbers that describe a face's unique features. Two photos of the same person produce similar lists. Two photos of different people produce very different lists.
Your system uses the Facenet512 model to generate these embeddings. It measures 98.4% accuracy on standard benchmarks, which actually surpasses human-level accuracy (97.5%).
- Create a file called register.py inside your larp-ai folder and add the following code:
import cv2
from deepface import DeepFace
from database import PeopleDatabase
def register_person():
# Connect to the people database
db = PeopleDatabase()
# Open the default webcam
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
return
print("Webcam is open. Press SPACE to capture your face, or Q to quit.")
while True:
# Read one frame from the webcam
ret, frame = cap.read()
if not ret:
print("Error: Could not read frame.")
break
# Display the live feed in a window
cv2.imshow("Register Face - Press SPACE to capture", frame)
key = cv2.waitKey(1) & 0xFF
# When SPACE is pressed, capture the current frame
if key == ord(" "):
print("Capturing face...")
try:
# Save the frame to a temporary image file for DeepFace
temp_path = "temp_capture.jpg"
cv2.imwrite(temp_path, frame)
# Extract the 512-dimensional face embedding
result = DeepFace.represent(
img_path=temp_path,
model_name="Facenet512",
detector_backend="ssd"
)
if len(result) == 0:
print("No face detected. Please try again.")
continue
embedding = result[0]["embedding"]
print(f"Face captured! Embedding dimension: {len(embedding)}")
What does this code do?
- cv2.VideoCapture(0) opens your default webcam. The 0 refers to the first camera on your machine.
- The while True loop reads frames continuously and displays them in a window, creating a live video feed.
- When you press SPACE, the frame is saved to a temporary .jpg file. DeepFace reads image files rather than raw frames.
- DeepFace.represent() uses the Facenet512 model to turn the face into a 512-number embedding. The SSD detector backend is used because it processes around 9 FPS on a standard laptop, keeping the experience fast.
You won't be able to run this file yet. The next chunk adds the database save logic that completes the registration flow.
Save the person to your database
Once the embedding is captured, your script needs to close the webcam window and ask the user for a name and an optional note. These details get stored in your SQLite database alongside the embedding.
- In register.py, find the line that says print(f"Face captured! Embedding dimension: {len(embedding)}"), then add the following code directly below it:
# Close the webcam before asking for input
cap.release()
cv2.destroyAllWindows()
# Prompt for the person's name
name = input("Enter person's name: ").strip()
if not name:
print("Name cannot be empty. Registration cancelled.")
db.close()
return
# Prompt for an optional note about the person
note = input("Add a note (optional, press Enter to skip): ").strip()
# Save the person to the database
person_id = db.add_person(name, embedding, note)
print(f"Registered '{name}' with ID {person_id}.")
db.close()
return
What does this code do?
- cap.release() and cv2.destroyAllWindows() close the webcam and its display window before switching to terminal input.
- The script asks for a name (required) and a note (optional). If the name is empty, registration is cancelled.
- db.add_person() calls the method you built in Step 2 to insert the name, embedding, and note into the people table.
The file still needs error handling before you can run it. The next chunk completes the function.
Add error handling and register your first faces
DeepFace raises an exception when it cannot detect a face in the frame. Without handling this, your script would crash if you press SPACE while your face is not visible or poorly lit.
- In register.py, find the line that says db.close() followed by return at the end of the code you just added, then add the following code directly below it to close out the function:
except Exception as e:
# Handle cases where DeepFace can't detect a face
print(f"Could not detect a face: {e}")
print("Make sure your face is clearly visible. Try again.")
continue
elif key == ord("q"):
break
# Clean up webcam and database connections
cap.release()
cv2.destroyAllWindows()
db.close()
if __name__ == "__main__":
register_person()
What does this code do?
- The except block catches any DeepFace exception (like no face detected at a valid confidence level) and prints a helpful message instead of crashing.
- continue sends the loop back to the webcam feed so the user can try again without restarting the script.
- Pressing q breaks out of the loop and cleans up the webcam and database connections.
- The if __name__ == "__main__" block lets you run this file directly from the terminal.
Awesome, I've got everything!
Double check you have saved register.py before running it.
I'd like to double check the full code
import cv2
from deepface import DeepFace
from database import PeopleDatabase
def register_person():
db = PeopleDatabase()
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
return
print("Webcam is open. Press SPACE to capture your face, or Q to quit.")
while True:
ret, frame = cap.read()
if not ret:
print("Error: Could not read frame.")
break
cv2.imshow("Register Face - Press SPACE to capture", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord(" "):
print("Capturing face...")
try:
temp_path = "temp_capture.jpg"
cv2.imwrite(temp_path, frame)
result = DeepFace.represent(
img_path=temp_path,
model_name="Facenet512",
detector_backend="ssd"
)
if len(result) == 0:
print("No face detected. Please try again.")
continue
embedding = result[0]["embedding"]
print(f"Face captured! Embedding dimension: {len(embedding)}")
cap.release()
cv2.destroyAllWindows()
name = input("Enter person's name: ").strip()
if not name:
print("Name cannot be empty. Registration cancelled.")
db.close()
return
note = input("Add a note (optional, press Enter to skip): ").strip()
person_id = db.add_person(name, embedding, note)
print(f"Registered '{name}' with ID {person_id}.")
db.close()
return
except Exception as e:
print(f"Could not detect a face: {e}")
print("Make sure your face is clearly visible. Try again.")
continue
elif key == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
db.close()
if __name__ == "__main__":
register_person()
First-run model download
The first time you run any script that uses DeepFace.represent(), DeepFace automatically downloads the Facenet512 model weights (~95 MB). This only happens once. Expect a short delay on your first capture.
- Save register.py.
- Run the registration script by running this command:
python register.py
- Your webcam window should open with the title Register Face - Press SPACE to capture.
- Position your face in the center of the frame and press SPACE to capture.
- You should see Face captured! Embedding dimension: 512 printed in your terminal.
- Enter your name when prompted, and optionally add a note like This is me, the project creator.
- You should see a confirmation like Registered 'YourName' with ID 1. printed in the terminal.
Webcam not opening or no face detected?
- If the webcam window does not appear, make sure no other application (like Zoom or Teams) is using your camera. Close those apps and try again.
- If you see Could not detect a face, make sure your face is well-lit and clearly visible in the frame. Avoid backlighting from a window behind you.
- If you see warnings about GPU or CUDA in the terminal output, these are harmless. TensorFlow prints these when no NVIDIA GPU is present, but your script still works on CPU.
- If you see a ModuleNotFoundError for database, make sure database.py from Step 2 is in the same folder as register.py.
My webcam or face detection isn't working in register.py. Help me debug.
Now register 2-3 more people to populate your database. You can ask a friend to sit in front of your webcam, or hold up a clear photo of someone on your phone screen.
- Run python register.py again to register a second person.
- Give them a different name and note (for example, Met at coffee shop).
- Repeat one more time for a third person if possible.
Pro tip: phone photos work
If you are working alone, pull up a clear, front-facing photo of a friend or family member on your phone. Hold the phone screen in front of your webcam and press SPACE. DeepFace can extract embeddings from photos on screens, which is a quick way to populate your database for testing.
Your database now has real face data. Next up, you will build a real-time recognition loop that matches live webcam faces against your stored embeddings and labels people on screen.
Build the Real-Time Recognition Loop
You can register people into your database, but your system cannot recognize them yet. The missing piece is a real-time loop that continuously scans your webcam feed, detects faces, and checks them against your stored embeddings.
In this step, you will build recognize.py. This script opens your webcam, detects every face in each frame, extracts its embedding, and matches it against your people database. Recognized faces get a green box with their name. Unknown faces get a red box.
In this step, get ready to:
- Detect faces in your webcam feed using the SSD detector.
- Match each detected face against your database using Facenet512 embeddings.
- Optimize performance with frame-skipping and FPS monitoring.
Set up the webcam loop and face detection
The recognition loop needs to do two things at once: display a smooth video feed and run face detection. Running DeepFace on every single frame would be extremely slow. The solution is a frame-skip mechanism that only processes every 10th frame and caches the results for the frames in between.
- Create a file called recognize.py inside your larp-ai folder and add the following code:
import cv2
import time
from deepface import DeepFace
from database import PeopleDatabase
def run_recognition():
# Connect to the people database
db = PeopleDatabase()
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
return
print("Starting recognition. Press Q to quit.")
# Frame-skip variables for smooth video
frame_count = 0
recognition_interval = 10
cached_results = []
fps_time = time.time()
fps = 0
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
# Calculate frames per second
current_time = time.time()
elapsed = current_time - fps_time
if elapsed > 0:
fps = frame_count / elapsed
# Only run detection every N frames for performance
if frame_count % recognition_interval == 0:
cached_results = []
try:
# Save frame to disk for DeepFace processing
temp_path = "temp_recognize.jpg"
cv2.imwrite(temp_path, frame)
# Detect all faces using the fast SSD backend
faces = DeepFace.extract_faces(
img_path=temp_path,
detector_backend="ssd",
enforce_detection=False
)
for face_obj in faces:
# Skip low-confidence detections
if face_obj["confidence"] < 0.5:
continue
# Extract bounding box coordinates
facial_area = face_obj["facial_area"]
x = facial_area["x"]
y = facial_area["y"]
w = facial_area["w"]
h = facial_area["h"]
What does this code do?
- recognition_interval = 10 tells the loop to only run face detection every 10th frame. The other 9 frames reuse the cached results, keeping video smooth at ~30 FPS while still updating recognition roughly every 0.3 seconds.
- DeepFace.extract_faces() uses the SSD (Single Shot Detector) backend, which processes ~9.2 FPS on a standard laptop. That makes it the fastest detector option in DeepFace.
- enforce_detection=False prevents DeepFace from crashing when no face is visible. Instead, it returns an empty list.
- The confidence check (< 0.5) filters out false positives where the detector sees a "face" in a random pattern.
Why save the frame to disk first?
DeepFace expects a file path as input, not a raw NumPy array. Writing the frame to temp_recognize.jpg ensures consistent behavior across all operating systems. This adds negligible overhead compared to the model inference time.
This code is not runnable on its own yet. The function body is incomplete. You will finish it in the next two substeps.
Add face recognition and database matching
Detecting a face gives you its position in the frame. To identify WHO that face belongs to, you need to extract its embedding and compare it against every embedding stored in your database using cosine similarity.
- In recognize.py, find the line that says h = facial_area["h"]. Add the following code directly below it, at the same indentation level as the facial_area line:
try:
# Extract the face embedding for recognition
result = DeepFace.represent(
img_path=temp_path,
model_name="Facenet512",
detector_backend="ssd",
enforce_detection=False
)
if len(result) > 0:
embedding = result[0]["embedding"]
# Check if this face matches anyone in the database
match = db.find_match(embedding)
if match:
# Known person: green box with name and distance
label = f"{match['name']} ({match['distance']:.2f})"
color = (0, 255, 0)
db.update_last_seen(match["id"])
else:
# Unknown person: red box
label = "Unknown"
color = (0, 0, 255)
# Cache the result for drawing on every frame
cached_results.append({
"x": x, "y": y, "w": w, "h": h,
"label": label, "color": color
})
except Exception:
pass
except Exception:
pass
What does this code do?
- DeepFace.represent() extracts a 512-dimensional vector embedding from the detected face using the Facenet512 model. This is the same model you used during registration.
- db.find_match(embedding) compares the new embedding against every stored embedding using cosine distance. If the closest match falls below the 0.40 threshold, it returns that person's info.
- Recognized faces get a green box ((0, 255, 0) in BGR) with the person's name and the cosine distance score. Lower distance means a closer match.
- db.update_last_seen() updates the person's last_seen timestamp every time they are recognized.
- The outer except Exception: pass silently handles frames where detection fails entirely, keeping the loop running.
Why is the distance shown on screen?
The cosine distance score helps you calibrate your system. A distance of 0.10 means a near-perfect match. A distance of 0.38 means the system is less sure. If you see false matches, you can lower the threshold in find_match() from 0.40 to something stricter like 0.30.
The function still needs its display logic and cleanup code. You will add those next.
Add the display loop and FPS counter
The detection and recognition results are cached, but nothing draws them on screen yet. This final section renders bounding boxes, labels, and an FPS counter on every frame.
- In recognize.py, find the last except Exception: block you just added (the outer one). Add the following code directly below its pass line, at the same indentation level as the if frame_count block:
# Draw cached results on every frame for smooth display
for result in cached_results:
cv2.rectangle(
frame,
(result["x"], result["y"]),
(result["x"] + result["w"], result["y"] + result["h"]),
result["color"],
2
)
cv2.putText(
frame,
result["label"],
(result["x"], result["y"] - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
result["color"],
2
)
# Show FPS counter in the top-left corner
cv2.putText(
frame,
f"FPS: {fps:.1f}",
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
(255, 255, 255),
2
)
cv2.imshow("larp.ai - Face Recognition", frame)
# Press 'q' to quit the recognition loop
if cv2.waitKey(1) & 0xFF == ord("q"):
break
# Clean up resources
cap.release()
cv2.destroyAllWindows()
db.close()
if __name__ == "__main__":
run_recognition()
What does this code do?
- The for result in cached_results loop runs on EVERY frame, not just every 10th. This is what makes the video feel smooth. The boxes and labels stay visible between detection passes.
- cv2.rectangle() draws the bounding box. cv2.putText() renders the label 10 pixels above the box so it does not overlap the face.
- The FPS counter uses the frame_count / elapsed calculation from the top of the loop. It displays in white at position (10, 30) so it stays in the top-left corner.
- cv2.waitKey(1) waits 1 millisecond for a keypress on each frame. Pressing q breaks out of the loop and triggers cleanup.
How does the frame-skip mechanism work?
Without frame-skipping, running DeepFace on every frame would drop your video to 1-2 FPS. The frame-skip pattern solves this by splitting the work into two speeds.
- Slow path (every 10th frame): Run face detection and recognition. Store the results in cached_results.
- Fast path (every frame): Draw the cached bounding boxes and labels on the current frame. This is just pixel drawing, which takes microseconds.
The result is a smooth video feed (~30 FPS visually) with recognition updates roughly every 0.3 seconds.
- Save recognize.py.
- Make sure you have registered at least one person using register.py from the previous step. If you have not, run it now and register yourself.
- Test the recognition loop by running this command:
python recognize.py
- You should see your webcam feed with green bounding boxes around recognized faces, showing the person's name and cosine distance. Unknown faces appear with a red box labeled Unknown. An FPS counter displays in the top-left corner.
- Press q to quit the recognition loop.
What should I expect on first run?
The first time you run recognition, DeepFace may download model weights (~95MB for Facenet512). This is a one-time download. You may also see TensorFlow warnings about GPU support. These are harmless and can be ignored.
Not seeing any bounding boxes?
- Make sure your face is clearly visible and well-lit. The SSD detector needs reasonable lighting to find faces.
- Check that the indentation in recognize.py is correct. Python is whitespace-sensitive. The for face_obj loop should be inside the try block, which is inside the if frame_count check.
- If you see Error: Could not open webcam, make sure no other application (like register.py or a video call app) is using the webcam.
- Help me debug my recognition loop.
Recognized faces showing as Unknown?
- The cosine distance threshold defaults to 0.40. If your lighting or angle is very different from when you registered, the distance may be too high. Try re-registering with similar conditions.
- Confirm your database has people in it by opening a Python shell and running PeopleDatabase().get_all_people(). You should see at least one entry.
- Why is my face showing as Unknown even though I registered?
Awesome, I've got everything!
Double check you have saved recognize.py and that the recognition loop is working with green boxes on recognized faces.
I'd like to double check the full code
import cv2
import time
from deepface import DeepFace
from database import PeopleDatabase
def run_recognition():
db = PeopleDatabase()
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
return
print("Starting recognition. Press Q to quit.")
frame_count = 0
recognition_interval = 10
cached_results = []
fps_time = time.time()
fps = 0
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
current_time = time.time()
elapsed = current_time - fps_time
if elapsed > 0:
fps = frame_count / elapsed
if frame_count % recognition_interval == 0:
cached_results = []
try:
temp_path = "temp_recognize.jpg"
cv2.imwrite(temp_path, frame)
faces = DeepFace.extract_faces(
img_path=temp_path,
detector_backend="ssd",
enforce_detection=False
)
for face_obj in faces:
if face_obj["confidence"] < 0.5:
continue
facial_area = face_obj["facial_area"]
x = facial_area["x"]
y = facial_area["y"]
w = facial_area["w"]
h = facial_area["h"]
try:
result = DeepFace.represent(
img_path=temp_path,
model_name="Facenet512",
detector_backend="ssd",
enforce_detection=False
)
if len(result) > 0:
embedding = result[0]["embedding"]
match = db.find_match(embedding)
if match:
label = f"{match['name']} ({match['distance']:.2f})"
color = (0, 255, 0)
db.update_last_seen(match["id"])
else:
label = "Unknown"
color = (0, 0, 255)
cached_results.append({
"x": x, "y": y, "w": w, "h": h,
"label": label, "color": color
})
except Exception:
pass
except Exception:
pass
for result in cached_results:
cv2.rectangle(
frame,
(result["x"], result["y"]),
(result["x"] + result["w"], result["y"] + result["h"]),
result["color"],
2
)
cv2.putText(
frame,
result["label"],
(result["x"], result["y"] - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
result["color"],
2
)
cv2.putText(
frame,
f"FPS: {fps:.1f}",
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
(255, 255, 255),
2
)
cv2.imshow("larp.ai - Face Recognition", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
db.close()
if __name__ == "__main__":
run_recognition()
Your recognition engine is live. It can detect faces, match them against your database, and label them in real time. Next up, you will build the memory query interface that ties registration, recognition, and your people database into one command-line menu.
Build the Memory Query Interface
You have a registration script, a recognition loop, and a database full of face embeddings. The problem is that each piece lives in a separate file.
In this step, you will create a single command-line menu that ties all your modules together into one cohesive tool. One script to register, recognize, search, and query your entire people memory.
In this step, get ready to:
- Create a command-line menu that connects registration, recognition, and database queries.
- Wire up search, history, and note-taking features to your people database.
- Run the full system end-to-end with real faces.
Create the menu skeleton
The menu is the front door to your entire memory system. It will import the functions you already built and let the user pick an action from a numbered list.
- Create a new file called memory.py inside your larp-ai folder.
- Copy the following code into memory.py to set up the menu skeleton:
from database import PeopleDatabase
from register import register_person
from recognize import run_recognition
def main():
# Display the app banner
print("=" * 50)
print(" larp.ai - Face Recognition Memory System")
print("=" * 50)
while True:
# Show the menu options
print("\nMenu:")
print(" 1. Register a new person")
print(" 2. Start recognition")
print(" 3. Search people by name")
print(" 4. List all people")
print(" 5. View interaction history")
print(" 6. Add a note to a person")
print(" 7. Quit")
choice = input("\nChoose an option (1-7): ").strip()
# Delegate to the register module
if choice == "1":
register_person()
# Delegate to the recognition module
elif choice == "2":
run_recognition()
# Exit the program
elif choice == "7":
print("Goodbye!")
break
else:
print("Invalid option. Please choose 1-7.")
if __name__ == "__main__":
main()
What does this code do?
- The three imports pull in your existing modules. register_person() comes from register.py and run_recognition() comes from recognize.py.
- The while True loop keeps the menu running until the user picks option 7 to quit.
- Options 1 and 2 delegate directly to the functions you built in earlier steps. Options 3 through 6 are placeholders for now. They will trigger the "Invalid option" message until you wire them up next.
- Save memory.py.
- Run the skeleton menu by running:
python memory.py
- You should see the banner and the numbered menu. Type 7 and press Enter to quit.
Not seeing the menu?
- Make sure your virtual environment is activated. You should see (venv) in your terminal prompt.
- If you see a ModuleNotFoundError, confirm that database.py, register.py, and recognize.py are all saved in the same larp-ai folder as memory.py.
- Help me debug a ModuleNotFoundError when running memory.py
Wire up the query and note options
Options 3 through 6 are the "memory" part of your system. They let you search for people, list everyone you have met, read interaction history, and attach notes.
- In memory.py, find the line that says elif choice == "7":.
- Copy the following code above that line to add the search and list options:
# Search for a person by name
elif choice == "3":
name = input("Enter name to search: ").strip()
db = PeopleDatabase()
results = db.search_person(name)
if results:
for person in results:
print(f"\n Name: {person['name']}")
print(f" First seen: {person['first_seen']}")
print(f" Last seen: {person['last_seen']}")
else:
print("No one found with that name.")
db.close()
# List all people in the database
elif choice == "4":
db = PeopleDatabase()
people = db.get_all_people()
if people:
print(f"\nYou have met {len(people)} people:")
for person in people:
print(f" [{person['id']}] {person['name']} (first: {person['first_seen'][:10]}, last: {person['last_seen'][:10]})")
else:
print("No one registered yet.")
db.close()
What does this code do?
- Option 3 uses search_person() with a LIKE query, so partial name matches work. Typing "sar" would find "Sarah".
- Option 4 calls get_all_people() and formats each person with their ID, name, and the dates they were first and last seen. The [:10] slice trims the ISO timestamp to just the date portion.
- Each option creates its own PeopleDatabase() instance and closes it when done. This keeps the database connection short-lived and avoids locking issues.
- Save memory.py.
- Run python memory.py and choose option 4 to list all people. You should see the people you registered in the previous step with their names and dates.
Seeing "No one registered yet"?
- This means your database file is empty. Make sure larp_memory.db exists in your larp-ai folder. If it does not exist, you need to register at least one person first using option 1.
- If the file exists but shows no people, you may have run register.py from a different directory. The database file is created in whichever folder the script runs from.
- Help me figure out why my database is empty
Now add the interaction history and note options. These let you record and review what you know about each person.
- In memory.py, find the line elif choice == "7": again.
- Copy the following code above that line, right after the option 4 block you just added:
# View all interaction notes for a person
elif choice == "5":
name = input("Enter person's name: ").strip()
db = PeopleDatabase()
history = db.get_person_history(name)
if history:
print(f"\nHistory for '{history[0]['name']}':")
for entry in history:
if entry["note"]:
print(f" [{entry['timestamp'][:16]}] {entry['note']}")
else:
print("No history found.")
db.close()
# Add a new note to an existing person
elif choice == "6":
db = PeopleDatabase()
name = input("Enter person's name: ").strip()
results = db.search_person(name)
if results:
person = results[0]
print(f"Adding note for: {person['name']}")
note = input("Enter note: ").strip()
if note:
db.add_note(person["id"], note)
print("Note added.")
else:
print("Empty note, skipping.")
else:
print("Person not found.")
db.close()
What does this code do?
- Option 5 calls get_person_history() which joins the people and interactions tables to show every note you have recorded about someone, sorted by most recent first.
- Option 6 first searches for the person to make sure they exist, then prompts for a note. The add_note() method also updates the person's last_seen timestamp automatically.
- Save memory.py.
- Run python memory.py and choose option 6 to add a note to one of your registered people. Then choose option 5 and type the same name. You should see the note you just added with a timestamp.
Option 5 shows "No history found"?
- Double-check that you typed the name exactly as it was registered. The search uses a partial match, but it is case-sensitive.
- If you registered the person without an initial note and have not added one through option 6 yet, there will be no interaction records to display.
- Help me debug why interaction history is not showing up
Awesome, I've got everything!
Double check that you have saved memory.py before moving on.
I'd like to double check the full code
from database import PeopleDatabase
from register import register_person
from recognize import run_recognition
def main():
print("=" * 50)
print(" larp.ai - Face Recognition Memory System")
print("=" * 50)
while True:
print("\nMenu:")
print(" 1. Register a new person")
print(" 2. Start recognition")
print(" 3. Search people by name")
print(" 4. List all people")
print(" 5. View interaction history")
print(" 6. Add a note to a person")
print(" 7. Quit")
choice = input("\nChoose an option (1-7): ").strip()
if choice == "1":
register_person()
elif choice == "2":
run_recognition()
elif choice == "3":
name = input("Enter name to search: ").strip()
db = PeopleDatabase()
results = db.search_person(name)
if results:
for person in results:
print(f"\n Name: {person['name']}")
print(f" First seen: {person['first_seen']}")
print(f" Last seen: {person['last_seen']}")
else:
print("No one found with that name.")
db.close()
elif choice == "4":
db = PeopleDatabase()
people = db.get_all_people()
if people:
print(f"\nYou have met {len(people)} people:")
for person in people:
print(f" [{person['id']}] {person['name']} (first: {person['first_seen'][:10]}, last: {person['last_seen'][:10]})")
else:
print("No one registered yet.")
db.close()
elif choice == "5":
name = input("Enter person's name: ").strip()
db = PeopleDatabase()
history = db.get_person_history(name)
if history:
print(f"\nHistory for '{history[0]['name']}':")
for entry in history:
if entry["note"]:
print(f" [{entry['timestamp'][:16]}] {entry['note']}")
else:
print("No history found.")
db.close()
elif choice == "6":
db = PeopleDatabase()
name = input("Enter person's name: ").strip()
results = db.search_person(name)
if results:
person = results[0]
print(f"Adding note for: {person['name']}")
note = input("Enter note: ").strip()
if note:
db.add_note(person["id"], note)
print("Note added.")
else:
print("Empty note, skipping.")
else:
print("Person not found.")
db.close()
elif choice == "7":
print("Goodbye!")
break
else:
print("Invalid option. Please choose 1-7.")
if __name__ == "__main__":
main()
Run the full system end-to-end
Every module is connected. Time to take the complete system for a spin.
- Run the memory system by running:
python memory.py
- Choose option 1 to register a new person. Point your webcam at yourself or at a photo of a friend on your phone.
- Press Space to capture the face.
- Enter a name and an optional note when prompted.
- Register at least 2 different people so you have multiple entries to query.
Pro tip: use a phone
If you do not have a second person nearby, pull up a clear photo of someone's face on your phone and hold it in front of your webcam. DeepFace works with photos displayed on screens.
- Choose option 2 to start the recognition loop.
- Face the webcam and verify that your name appears above the green bounding box. The label includes a distance score. Lower numbers mean a closer match.
- Press Q to quit the recognition window and return to the menu.
- Choose option 6 to add a note. Enter the name of someone you registered and type a note like Met at the coffee shop, likes hiking.
- Choose option 5 and enter the same name. You should see the note with its timestamp.
- Choose option 3 and search by a partial name. Confirm that the matching person shows up with their first-seen and last-seen dates.
- Choose option 4 to list all people. You should see every registered person with their ID and dates.
Recognition not matching your face?
- Make sure the lighting is similar to when you registered. Large lighting changes can increase the cosine distance between embeddings enough to fail the threshold.
- If the label says "Unknown" even though you are registered, the match distance may be above the 0.40 threshold. Try re-registering with your current lighting conditions.
- Help me troubleshoot face recognition not matching correctly
Secret mission
Add Facial Attribute Analysis
What if your memory system could tell you someone's estimated age and mood the moment they are recognized? DeepFace can analyze more than just identity. Extend your project to automatically detect age, gender, and emotion for every face.
Before you go
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Python virtual environment (venv folder inside your larp-ai project directory).
- Project files: database.py, register.py, recognize.py, memory.py, test_setup.py.
- SQLite database file (larp_memory.db) containing your registered people and interaction history.
- Temporary image files (temp_capture.jpg, temp_recognize.jpg).
- Downloaded DeepFace model weights (~95MB for Facenet512, stored in your home directory under .deepface).
Keep everything running
No action needed. Choose this if you are still actively building or experimenting with your face recognition system.
- Your virtual environment stays ready. Just activate it with venv\Scripts\activate (Windows) or source venv/bin/activate (macOS/Linux) whenever you return.
- Your larp_memory.db database keeps all your registered people and notes.
- The downloaded model weights are cached and will not need to be downloaded again.
Pause - I'll come back to this later
Shut down any running processes to free up memory. Your project files and database will still be there when you come back.
- Close any open OpenCV webcam windows by pressing q or closing the window.
- Deactivate your virtual environment by running deactivate in your terminal.
- Delete the temporary image files temp_capture.jpg and temp_recognize.jpg from your project folder if you want to tidy up.
- Your database, project files, and model weights will remain intact for next time.
Delete - I don't want to use this again
Remove all project resources and start fresh.
- Close any open OpenCV windows and deactivate your virtual environment.
- Delete the entire larp-ai project folder. This removes all Python files, the virtual environment, the SQLite database, and temporary images.
- To remove the cached DeepFace model weights, delete the .deepface folder in your home directory.
Mission accomplished
That's a Wrap
Nice work! You just built a fully functional face recognition memory system powered by DeepFace and OpenCV. Your webcam can now detect and identify anyone you have previously registered, all backed by a persistent SQLite database that stores every person and every interaction.
You've learned how to:
- Build a real-time face recognition pipeline using DeepFace and OpenCV, with frame-skip caching to keep the video feed smooth while running recognition in the background.
- Store Facenet512 face embeddings and interaction history in a SQLite database, matching faces by computing cosine similarity across 512-dimensional vectors.
- Create a command-line memory interface to register new people, recognize returning faces, search by name, and query interaction notes.
- Add automatic facial attribute analysis (estimated age, gender, emotion) to enrich each person's profile every time they are recognized.
Ready to quiz yourself?