Human Age Estimation with OpenCV
Detect faces and predict age groups using pre-trained deep learning models.
Introduction
30 Second Summary
Every time you unlock your phone with your face or see a social media filter guess your age, a machine learning model is analyzing your facial features in real time. Age estimation is one of the most visible applications of deep learning in everyday technology.
In this project, you will build a real-time Human Age Estimation system that uses pre-trained deep learning models to detect faces and predict the age group of each person from images or live webcam video.
What You'll Build
A Python application that loads pre-trained Caffe CNN models through OpenCV's DNN module to detect faces and classify them into one of 8 age groups.
By the end of this project, you'll have:
- A working face detection and age prediction pipeline that processes static images and outputs predicted age ranges.
- A real-time webcam application that annotates live video frames with age predictions.
- Hands-on understanding of how pre-trained deep learning models are loaded and used for inference without training from scratch.
- Secret Mission: Add gender detection alongside age prediction for a complete demographic analyzer.
Are there any prerequisites?
You should be comfortable with basic Python concepts like variables, functions, and loops. No prior machine learning or computer vision experience is needed.
Set Up Your Python Environment
Your age estimation project relies on two Python packages: OpenCV for computer vision and NumPy for numerical operations.
In this step, you'll load your project into VS Code and confirm that both packages are importable and ready to use.
In this step, get ready to:
- Load your project folder in VS Code.
- Verify that OpenCV and NumPy are importable in your virtual environment.
Load your project in VS Code
VS Code needs to have your project folder open so that the integrated terminal starts in the right directory and you can create files in the correct location.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Select the age-estimation folder on your Desktop, then click Open.
- Open the integrated terminal by pressing Ctrl+` (Windows) or Cmd+` (macOS).
- Confirm your virtual environment is active by checking for (venv) at the start of your terminal prompt.
What does (venv) mean?
The (venv) prefix tells you that your virtual environment is active. This means any Python packages you use come from this isolated environment, not your system-wide Python installation.
Don't see (venv) in your terminal?
Your virtual environment might not have activated automatically. Re-activate it by running the appropriate command for your operating system.
On macOS or Linux, run:
source venv/bin/activate
On Windows, run:
venv\Scripts\activate
You should see (venv) appear at the start of your prompt after running this.
Still stuck? Help me activate my virtual environment in VS Code
Confirm your packages are ready
Before writing any code, you need to confirm that OpenCV and NumPy are importable. A quick import test catches any installation issues now, before they cause confusing errors later.
- Verify OpenCV is working by running this command in your terminal:
python -c "import cv2; print(cv2.__version__)"
You should see 4.13.0.92 printed in your terminal.
- Verify NumPy is working by running this command:
python -c "import numpy; print(numpy.__version__)"
You should see a version number printed (e.g., 2.2.3 or similar).
Why these two packages?
OpenCV's DNN module handles loading pre-trained deep learning models and running inference on images. NumPy provides the array structures that OpenCV uses internally to represent images and model outputs.
Together, they give you everything you need to build the age detection pipeline without heavyweight frameworks like TensorFlow or PyTorch.
Seeing a ModuleNotFoundError?
This usually means your virtual environment is not active, or the packages were installed outside of it. Check that you see (venv) in your terminal prompt, then reinstall by running:
pip install opencv-python numpy
Then try the import commands again.
Still having trouble? Help me fix my OpenCV installation
Your environment is confirmed and ready to go. Next up, you'll download the pre-trained deep learning models that power the face detection and age prediction.
Download Pre-Trained Models
Your environment is ready, but your age estimation system needs a brain. Deep learning models learn patterns from thousands of training images, and those learned patterns are saved as model files.
Instead of training a model from scratch (which would take hours and a GPU), you will download pre-trained models that are ready to use immediately. This is how most real-world ML applications work: load a proven model and run inference on new data.
In this step, get ready to:
- Download the face detection model files.
- Download the age prediction model files.
- Verify all four model files are accessible in your project directory.
Download the face detection model files
The face detection model locates faces in an image before age prediction can happen. It uses a TensorFlow-based SSD (Single Shot Detector) with a ResNet-10 backbone.
This model needs two files: a .pb file containing the trained weights, and a .pbtxt file describing the network structure.
- Download the face detection config file by running this command in your terminal:
curl -O https://raw.githubusercontent.com/spmallick/learnopencv/master/AgeGender/opencv_face_detector.pbtxt
- Download the face detection weights file by running:
curl -O https://raw.githubusercontent.com/spmallick/learnopencv/master/AgeGender/opencv_face_detector_uint8.pb
What are these files?
The opencv_face_detector.pbtxt file is the network configuration. It describes how the layers of the neural network are connected.
The opencv_face_detector_uint8.pb file contains the trained weights. These are the numerical values the model learned from training data that allow it to recognize faces.
- Confirm the files downloaded by running:
ls opencv_face_detector*
You should see opencv_face_detector.pbtxt and opencv_face_detector_uint8.pb in the output.
Don't see the files?
Make sure your terminal is in the age-estimation folder. Run pwd to check your current directory.
If curl is not available on Windows, use the browser links below in the tip callout to download manually instead.
Help me troubleshoot my model file downloads.
Download the age prediction model files
The age prediction model takes a detected face and classifies it into one of 8 age groups. It uses a Caffe-based CNN trained on the Adience dataset by Levi and Hassner (CVPR 2015).
Like the face detector, this model also needs two files: a .prototxt config file and a .caffemodel weights file.
- Download the age prediction config file by running:
curl -O https://raw.githubusercontent.com/spmallick/learnopencv/master/AgeGender/age_deploy.prototxt
- Download the age prediction weights file by running:
curl -L -o age_net.caffemodel https://github.com/GilLevi/AgeGenderDeepLearning/raw/master/models/age_net.caffemodel
Why is this download different?
The age_net.caffemodel file is hosted on the original authors' GitHub repository and uses Git LFS (Large File Storage). The -L flag tells curl to follow redirects, which is required for LFS-hosted files.
This file is about 44MB, so the download may take a moment depending on your connection speed.
- Confirm both age model files downloaded by running:
ls age_*
You should see age_deploy.prototxt and age_net.caffemodel in the output.
Seeing a very small age_net.caffemodel?
If the file is only a few KB instead of ~44MB, the download may have saved an HTML redirect page instead of the actual model. Delete it and try again with the -L flag included.
You can check the file size by running ls -lh age_net.caffemodel and confirming it is approximately 44MB.
Help me fix my age_net.caffemodel download.
Manual download alternative
If curl is giving you trouble, you can download the files directly in your browser. Visit the learnopencv AgeGender folder at github.com/spmallick/learnopencv/tree/master/AgeGender and download each file individually.
Make sure you save the files directly into your age-estimation folder.
Verify all model files are accessible
Before writing any code, you need to confirm all four files are in the right place. OpenCV will look for these files in the same directory as your Python script.
- List all model files in your project folder by running:
ls *.pb *.pbtxt *.prototxt *.caffemodel
You should see all four files listed:
- opencv_face_detector_uint8.pb
- opencv_face_detector.pbtxt
- age_deploy.prototxt
- age_net.caffemodel
What does each file do?
- opencv_face_detector_uint8.pb + opencv_face_detector.pbtxt = the face detection model. It finds where faces are in an image.
- age_net.caffemodel + age_deploy.prototxt = the age prediction model. It classifies each face into one of 8 age groups: (0-2), (4-6), (8-12), (15-20), (25-32), (38-43), (48-53), (60-100).
Missing one or more files?
Run pwd to confirm you are inside the age-estimation folder. If the files were saved elsewhere, move them into this folder.
If you downloaded manually via your browser, check your Downloads folder and move the files into age-estimation.
Help me locate my missing model files.
Your models are ready to go. Next up, you will write Python code that loads the face detection model and finds faces in an image.
Build the Face Detection Function
Before your system can estimate anyone's age, it needs to find faces in the image first.
In this step, you'll create detect_age.py and build a face detection function using OpenCV's DNN module. The model you downloaded in the previous step is a TensorFlow SSD network with a ResNet-10 backbone that can reliably locate faces in an image.
In this step, get ready to:
- Load the face detection model with OpenCV's DNN module.
- Build a function that detects faces and returns bounding box coordinates.
- Test face detection on a sample image with bounding boxes drawn.
Load the face detection model
A deep learning inference pipeline always starts the same way: load the pre-trained model into memory, then feed it preprocessed input. You'll set up the script's imports, define file paths for all four models, and load the face detection network.
- In VS Code, create a new file called detect_age.py inside your age-estimation folder.
- Add the following imports, model paths, constants, and model loading code:
import cv2
import numpy as np
import argparse
# File paths for models
FACE_PROTO = "opencv_face_detector.pbtxt"
FACE_MODEL = "opencv_face_detector_uint8.pb"
AGE_PROTO = "age_deploy.prototxt"
AGE_MODEL = "age_net.caffemodel"
# Constants
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
AGE_LIST = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
# Load networks
face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO)
What does this code do?
The file paths point to the four model files you downloaded in the previous step. FACE_PROTO and FACE_MODEL are for face detection. AGE_PROTO and AGE_MODEL are for age prediction (which you'll use in the next step).
MODEL_MEAN_VALUES are the average pixel values from the training dataset. Subtracting these from input images centers the data the same way the model was trained. AGE_LIST maps the model's numeric outputs to human-readable age ranges.
cv2.dnn.readNet() loads the face detection network into memory. It automatically detects the model format from the file extensions (.pb for TensorFlow weights, .pbtxt for the config).
- Save detect_age.py.
Seeing a 'file not found' error?
Make sure all four model files are in the same folder as detect_age.py (the age-estimation folder root). Check the filenames match exactly, including the underscores.
Help me fix a file not found error when loading my face detection model.
Create the face detection function
Neural networks can't process raw images directly. They expect a specific input format called a blob. A blob is a 4-dimensional array (batch, channels, height, width) created by resizing the image, subtracting mean values, and optionally swapping color channels.
- Below the face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO) line, add the detect_faces function:
def detect_faces(frame, confidence_threshold=0.7):
"""Detect faces in an image and return bounding boxes."""
frame_height = frame.shape[0]
frame_width = frame.shape[1]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], True, False)
face_net.setInput(blob)
detections = face_net.forward()
face_boxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > confidence_threshold:
x1 = int(detections[0, 0, i, 3] * frame_width)
y1 = int(detections[0, 0, i, 4] * frame_height)
x2 = int(detections[0, 0, i, 5] * frame_width)
y2 = int(detections[0, 0, i, 6] * frame_height)
face_boxes.append([x1, y1, x2, y2])
return face_boxes
What does this function do?
- cv2.dnn.blobFromImage() resizes the image to 300x300 pixels and subtracts the mean values [104, 117, 123] from each color channel. This normalizes the input to match what the model expects.
- face_net.setInput(blob) feeds the preprocessed blob into the network, and face_net.forward() runs the actual inference to produce detections.
- The loop iterates through all detections, filtering out any with a confidence score below 0.7 (70%). This prevents false positives.
- The model outputs coordinates as percentages (0 to 1), so they're multiplied by the image dimensions to get actual pixel positions.
- Save detect_age.py.
Seeing a syntax error?
Make sure there is a blank line between the face_net = cv2.dnn.readNet(...) line and the def detect_faces line. Python requires consistent indentation throughout the function body.
Help me fix a syntax error in my detect_faces function.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on to test it.
ⓧ I'd like to double check the full code
import cv2
import numpy as np
import argparse
# File paths for models
FACE_PROTO = "opencv_face_detector.pbtxt"
FACE_MODEL = "opencv_face_detector_uint8.pb"
AGE_PROTO = "age_deploy.prototxt"
AGE_MODEL = "age_net.caffemodel"
# Constants
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
AGE_LIST = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
# Load networks
face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO)
def detect_faces(frame, confidence_threshold=0.7):
"""Detect faces in an image and return bounding boxes."""
frame_height = frame.shape[0]
frame_width = frame.shape[1]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], True, False)
face_net.setInput(blob)
detections = face_net.forward()
face_boxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > confidence_threshold:
x1 = int(detections[0, 0, i, 3] * frame_width)
y1 = int(detections[0, 0, i, 4] * frame_height)
x2 = int(detections[0, 0, i, 5] * frame_width)
y2 = int(detections[0, 0, i, 6] * frame_height)
face_boxes.append([x1, y1, x2, y2])
return face_boxes
Test face detection on a sample image
Your function is ready. Now you'll run it on a real image to confirm it finds faces correctly and draws bounding boxes around them.
- Find any photo of a person's face (a selfie, a team photo, or any image from the internet with a clearly visible face).
- Save it as test.jpg inside your age-estimation folder (the same folder as detect_age.py).
Choose a clear photo
The face detection model works best with well-lit, front-facing photos. A simple headshot or group photo with visible faces gives the best results for testing.
- Add the following test code at the bottom of detect_age.py, below the detect_faces function:
# --- Quick test (delete this section after testing) ---
image = cv2.imread("test.jpg")
if image is None:
print("Error: Could not read image 'test.jpg'")
else:
face_boxes = detect_faces(image)
if not face_boxes:
print("No faces detected.")
else:
print(f"Detected {len(face_boxes)} face(s).")
for box in face_boxes:
cv2.rectangle(image, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
cv2.imshow("Face Detection Test", image)
print("Press any key to close the window...")
cv2.waitKey(0)
cv2.destroyAllWindows()
What does the test code do?
- cv2.imread() loads your test image from disk.
- cv2.rectangle() draws a green bounding box around each detected face.
- cv2.imshow() opens a window displaying the annotated image. Press any key to close it.
- Save detect_age.py.
- Run the script by entering this command in your terminal:
python detect_age.py
You should see a message like Detected 1 face(s). in your terminal, and a window should pop up displaying your image with green rectangles around any detected faces.
- Press any key to close the image window.
No faces detected?
Make sure your test image has a clearly visible, front-facing human face. Images that are very dark, blurry, or have faces at extreme angles may not be detected.
If you see an error about reading the image, confirm test.jpg is saved in the same folder as your script and that the filename matches exactly (including the extension).
Help me debug why no faces are being detected in my image.
- Once you've confirmed face detection works, delete the test code block (everything below the comment # --- Quick test) from detect_age.py.
- Save detect_age.py.
Why delete the test code?
In the next step, you'll add the age prediction pipeline and a proper command-line interface that replaces this quick test. Removing it now keeps your file clean for the next additions.
Your face detection function is working. Next up, you'll build the age prediction pipeline that takes those detected faces and estimates each person's age group.
Build the Age Prediction Pipeline
Your face detection function can now locate faces in any image. It is time to add the intelligence that makes this project exciting: predicting the age of each detected face.
The age prediction model takes a cropped face image and classifies it into one of 8 age ranges. You will feed each detected face through this model, then build a complete pipeline that annotates images with predicted ages and saves the results.
In this step, get ready to:
- Load the age prediction network.
- Build a function that predicts the age of each detected face.
- Create a complete pipeline that annotates and saves results.
Load the age prediction model
Just like the face detection model, the age prediction model needs to be loaded into memory before you can use it for inference.
- In detect_age.py, find the line that says face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO).
- Add this line directly below it to load the age network:
age_net = cv2.dnn.readNet(AGE_MODEL, AGE_PROTO)
What does this line do?
This loads the Caffe age prediction model into an OpenCV network object. The cv2.dnn.readNet() function auto-detects the model format from the file extensions (.caffemodel for weights, .prototxt for the architecture).
Build the predict_age function
Each face bounding box from the detection step is just a set of coordinates. To predict a person's age, you need to crop that face out of the image, preprocess it into the format the model expects, and run a forward pass through the network.
- Below your detect_faces() function, add the predict_age() function by copying this code:
def predict_age(frame, face_boxes, padding=20):
"""Predict age for each detected face."""
results = []
for box in face_boxes:
# Extract face with padding
x1 = max(0, box[0] - padding)
y1 = max(0, box[1] - padding)
x2 = min(frame.shape[1] - 1, box[2] + padding)
y2 = min(frame.shape[0] - 1, box[3] + padding)
face = frame[y1:y2, x1:x2]
if face.size == 0:
continue
# Create blob and predict age
blob = cv2.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
age_net.setInput(blob)
age_preds = age_net.forward()
age = AGE_LIST[age_preds[0].argmax()]
confidence = age_preds[0].max()
results.append({
'box': box,
'age': age,
'confidence': confidence
})
return results
What does this code do?
This function loops through each detected face bounding box and predicts the person's age group.
- The padding=20 adds 20 pixels around the bounding box. Including some surrounding context (hair, ears) improves prediction accuracy.
- The max() and min() calls prevent coordinates from going outside the image boundaries.
- The blobFromImage() call resizes the face to 227x227 pixels and subtracts MODEL_MEAN_VALUES from each color channel. This is how the model was trained, so you must replicate that preprocessing.
- The swapRB=False flag keeps the BGR channel order unchanged, since the Caffe model expects BGR input.
- The argmax() finds which of the 8 age ranges got the highest probability score.
What is a blob?
A blob (Binary Large Object) in deep learning is a 4-dimensional array shaped as (batch, channels, height, width). Neural networks cannot directly accept raw images. They need standardized input tensors with consistent dimensions and normalized pixel values.
The cv2.dnn.blobFromImage() function handles resizing, mean subtraction, and reshaping all in one call.
- Save detect_age.py.
You will not see a change yet. The next chunk adds the annotation and testing code that puts this function to use.
Seeing a syntax error?
- Make sure the function definition starts at the same indentation level as detect_faces() (no extra indentation).
- Check that you have a blank line between the end of detect_faces() and the start of predict_age().
Help me debug a syntax error in my predict_age function.
Combine face detection and age prediction into a complete pipeline
The individual functions are ready. Now you will connect them into a pipeline that reads an image, detects faces, predicts ages, draws annotations, and saves the result.
- Below your predict_age() function, add the annotate_image() function by copying this code:
def annotate_image(frame, results):
"""Draw bounding boxes and age labels on the image."""
for result in results:
box = result['box']
label = f"Age: {result['age']}"
# Draw green rectangle around the face
cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
# Put yellow text label above the box
cv2.putText(frame, label, (box[0], box[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2, cv2.LINE_AA)
return frame
What does this code do?
- For each predicted face, it draws a green rectangle ((0, 255, 0) in BGR) around the detected face using the bounding box coordinates.
- It places a yellow text label ((0, 255, 255) in BGR) just above the box showing the predicted age range.
- The cv2.LINE_AA flag enables anti-aliased text rendering for smoother labels.
- Save detect_age.py.
You will not see a change yet. The next chunk adds the main entry point that ties everything together.
- Below the annotate_image() function, add the main block by copying this code:
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Age Detection from Image")
parser.add_argument("--image", required=True, help="Path to input image")
args = parser.parse_args()
# Read image
image = cv2.imread(args.image)
if image is None:
print(f"Error: Could not read image '{args.image}'")
exit(1)
# Detect faces
face_boxes = detect_faces(image)
if not face_boxes:
print("No faces detected.")
else:
print(f"Detected {len(face_boxes)} face(s).")
# Predict age
results = predict_age(image, face_boxes)
# Print results
for i, result in enumerate(results):
print(f" Face {i+1}: Age {result['age']} (confidence: {result['confidence']:.2f})")
# Annotate and display
annotated = annotate_image(image.copy(), results)
cv2.imshow("Age Detection", annotated)
print("\nPress any key to close the window...")
cv2.waitKey(0)
cv2.destroyAllWindows()
# Save output
cv2.imwrite("output_age_detection.jpg", annotated)
print("Output saved to 'output_age_detection.jpg'")
What does this code do?
- The argparse setup lets you pass an image path from the command line using the --image flag.
- It reads the image with cv2.imread(), then runs the full pipeline: detect faces, predict ages, print results to the terminal, and annotate the image.
- The cv2.imshow() call opens a window displaying the annotated image. It waits until you press any key before closing.
- Finally, it saves the annotated image as output_age_detection.jpg in your project folder.
- Save detect_age.py.
✔️ Awesome, I've got everything!
Great. Double check you have saved detect_age.py before moving on to test it.
ⓧ I'd like to double check the full code
Here is the complete detect_age.py file at this point:
import cv2
import numpy as np
import argparse
# File paths for models
FACE_PROTO = "opencv_face_detector.pbtxt"
FACE_MODEL = "opencv_face_detector_uint8.pb"
AGE_PROTO = "age_deploy.prototxt"
AGE_MODEL = "age_net.caffemodel"
# Constants
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
AGE_LIST = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
# Load networks
face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO)
age_net = cv2.dnn.readNet(AGE_MODEL, AGE_PROTO)
def detect_faces(frame, confidence_threshold=0.7):
"""Detect faces in an image and return bounding boxes."""
frame_height = frame.shape[0]
frame_width = frame.shape[1]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], True, False)
face_net.setInput(blob)
detections = face_net.forward()
face_boxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > confidence_threshold:
x1 = int(detections[0, 0, i, 3] * frame_width)
y1 = int(detections[0, 0, i, 4] * frame_height)
x2 = int(detections[0, 0, i, 5] * frame_width)
y2 = int(detections[0, 0, i, 6] * frame_height)
face_boxes.append([x1, y1, x2, y2])
return face_boxes
def predict_age(frame, face_boxes, padding=20):
"""Predict age for each detected face."""
results = []
for box in face_boxes:
x1 = max(0, box[0] - padding)
y1 = max(0, box[1] - padding)
x2 = min(frame.shape[1] - 1, box[2] + padding)
y2 = min(frame.shape[0] - 1, box[3] + padding)
face = frame[y1:y2, x1:x2]
if face.size == 0:
continue
blob = cv2.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
age_net.setInput(blob)
age_preds = age_net.forward()
age = AGE_LIST[age_preds[0].argmax()]
confidence = age_preds[0].max()
results.append({
'box': box,
'age': age,
'confidence': confidence
})
return results
def annotate_image(frame, results):
"""Draw bounding boxes and age labels on the image."""
for result in results:
box = result['box']
label = f"Age: {result['age']}"
cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
cv2.putText(frame, label, (box[0], box[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2, cv2.LINE_AA)
return frame
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Age Detection from Image")
parser.add_argument("--image", required=True, help="Path to input image")
args = parser.parse_args()
image = cv2.imread(args.image)
if image is None:
print(f"Error: Could not read image '{args.image}'")
exit(1)
face_boxes = detect_faces(image)
if not face_boxes:
print("No faces detected.")
else:
print(f"Detected {len(face_boxes)} face(s).")
results = predict_age(image, face_boxes)
for i, result in enumerate(results):
print(f" Face {i+1}: Age {result['age']} (confidence: {result['confidence']:.2f})")
annotated = annotate_image(image.copy(), results)
cv2.imshow("Age Detection", annotated)
print("\nPress any key to close the window...")
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("output_age_detection.jpg", annotated)
print("Output saved to 'output_age_detection.jpg'")
Now test the complete pipeline. You need an image that contains at least one face.
- Save any photo that contains a face as test.jpg in your age-estimation folder. You can download one from the internet or use a photo of yourself.
- Run the age detection script by running this command:
python detect_age.py --image test.jpg
You should see terminal output showing how many faces were detected and the predicted age range for each one. A window opens displaying the annotated image with green boxes and yellow age labels.
- Press any key to close the image window.
You should also see a new file called output_age_detection.jpg in your project folder containing the annotated image.
Not seeing any faces detected?
- Make sure your test image has a clearly visible face. The model works best with well-lit, roughly frontal faces.
- Verify your image file path is correct. If test.jpg is in a subfolder, include the relative path (e.g., images/test.jpg).
- Check the terminal output. If you see an error about reading the image, the file path or format may be wrong.
Help me debug why my age detection script is not finding any faces.
Why does the model predict age ranges instead of exact ages?
Even humans struggle to guess someone's exact age from a photo. The Levi and Hassner model treats age as a classification problem with 8 buckets rather than a regression problem. Classifying into ranges like (25-32) is more robust and produces more meaningful results than trying to predict a single number.
Your age prediction pipeline is working on static images. Next up, you will connect this pipeline to your webcam for real-time age detection on live video.
Add Real-Time Webcam Detection
Your static image pipeline works. You can point it at any photo and get age predictions back. But the real power of computer vision shows up when it runs on live video.
In this step, you will create a second script that opens your webcam and runs the face detection and age prediction pipeline on every single frame. The result is a live video feed with age labels drawn on top of detected faces in real time.
In this step, get ready to:
- Open a webcam video stream using OpenCV.
- Process each frame through the face detection and age prediction pipeline.
- Display annotated video and handle graceful exit with keyboard input.
Create the webcam script and open the video stream
A webcam feed is just a rapid sequence of image frames. OpenCV treats each frame the same way it treats a static image, so you can reuse the same detection and prediction functions you already built.
- In VS Code, create a new file called detect_age_webcam.py in your age-estimation folder.
- Add the imports, constants, model loading, and detection functions by pasting the following code:
import cv2
import numpy as np
# File paths for models
FACE_PROTO = "opencv_face_detector.pbtxt"
FACE_MODEL = "opencv_face_detector_uint8.pb"
AGE_PROTO = "age_deploy.prototxt"
AGE_MODEL = "age_net.caffemodel"
# Constants
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
AGE_LIST = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
# Load networks
face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO)
age_net = cv2.dnn.readNet(AGE_MODEL, AGE_PROTO)
def detect_faces(frame, confidence_threshold=0.7):
"""Detect faces in a frame and return bounding boxes."""
frame_height = frame.shape[0]
frame_width = frame.shape[1]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], True, False)
face_net.setInput(blob)
detections = face_net.forward()
face_boxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > confidence_threshold:
x1 = int(detections[0, 0, i, 3] * frame_width)
y1 = int(detections[0, 0, i, 4] * frame_height)
x2 = int(detections[0, 0, i, 5] * frame_width)
y2 = int(detections[0, 0, i, 6] * frame_height)
face_boxes.append([x1, y1, x2, y2])
return face_boxes
def predict_age(frame, face_boxes, padding=20):
"""Predict age for each detected face."""
results = []
for box in face_boxes:
x1 = max(0, box[0] - padding)
y1 = max(0, box[1] - padding)
x2 = min(frame.shape[1] - 1, box[2] + padding)
y2 = min(frame.shape[0] - 1, box[3] + padding)
face = frame[y1:y2, x1:x2]
if face.size == 0:
continue
blob = cv2.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
age_net.setInput(blob)
age_preds = age_net.forward()
age = AGE_LIST[age_preds[0].argmax()]
confidence = age_preds[0].max()
results.append({
'box': box,
'age': age,
'confidence': confidence
})
return results
# Open webcam
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
exit(1)
print("Age Detection - Webcam Mode")
print("Press 'q' to quit.")
What does this code do?
The top half of this file is identical to detect_age.py. It loads the same models and defines the same detect_faces() and predict_age() functions. The new part is at the bottom.
- cv2.VideoCapture(0) opens your default webcam. The 0 refers to the first camera device on your system.
- cap.isOpened() checks whether the webcam was successfully accessed. If not, the script exits with a clear error message.
Why a separate file instead of adding to detect_age.py?
Keeping the webcam version in its own file means you can run either mode independently. The static-image script stays clean for batch processing, while the webcam script focuses on real-time use.
Process each frame through the detection pipeline
With the webcam open, you need a loop that continuously grabs frames, runs detection, predicts ages, and draws the results onto each frame before moving to the next one.
- Add the frame processing loop below the print("Press 'q' to quit.") line by pasting this code:
while True:
ret, frame = cap.read()
if not ret:
print("Error: Could not read frame.")
break
# Detect faces and predict age
face_boxes = detect_faces(frame)
if face_boxes:
results = predict_age(frame, face_boxes)
# Annotate frame
for result in results:
box = result['box']
label = f"Age: {result['age']}"
cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
cv2.putText(frame, label, (box[0], box[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2, cv2.LINE_AA)
What does this code do?
- cap.read() grabs one frame from the webcam. It returns a boolean (ret) and the frame itself. If the read fails, the loop breaks.
- Each frame goes through the same detect_faces() and predict_age() pipeline you built for static images.
- For every detected face, a green rectangle and yellow age label are drawn directly on the frame using cv2.rectangle() and cv2.putText().
Display the annotated video and handle graceful exit
The annotated frame needs to be shown on screen, and the loop needs a way to stop cleanly when you press a key.
- Add the display, quit handling, and cleanup code below the annotation block (still inside the while True loop and after it) by pasting this code:
cv2.imshow("Age Detection - Press 'q' to quit", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
print("Webcam closed.")
What does this code do?
- cv2.imshow() displays the annotated frame in a window. Because it runs inside the loop, each frame replaces the previous one, creating a video effect.
- cv2.waitKey(1) waits 1 millisecond for a keypress. The & 0xFF mask isolates the lowest 8 bits for cross-platform compatibility. When the result matches ord('q'), the loop breaks.
- cap.release() frees the webcam so other apps can use it. cv2.destroyAllWindows() closes the display window.
- Save detect_age_webcam.py.
- Run the webcam script by entering this command in your terminal:
python detect_age_webcam.py
- You should see a window titled Age Detection - Press 'q' to quit showing your webcam feed. When your face appears, a green rectangle and a yellow age label should appear on top of it.
- Press q on your keyboard to close the window. You should see Webcam closed. printed in the terminal.
Your computer may ask for camera permission
On macOS, a system dialog may appear asking whether to allow Terminal (or VS Code) to access your camera. Click OK or Allow to grant access. On Windows, camera permissions are usually granted automatically for desktop apps.
Webcam not opening or showing a black screen?
- Make sure no other application (Zoom, FaceTime, Teams) is using your webcam. Only one app can access it at a time.
- If you denied camera permission earlier, go to your system privacy settings and grant camera access to Terminal or VS Code, then try again.
- If you see the error Error: Could not open webcam. and you are using an external webcam, try unplugging and reconnecting it.
Help me troubleshoot my webcam not working with OpenCV VideoCapture.
✔️ Awesome, I've got everything!
Great. Double check you have saved detect_age_webcam.py and confirmed the webcam detection is working.
ⓧ I'd like to double check the full code
Here is the complete detect_age_webcam.py file for comparison:
import cv2
import numpy as np
# File paths for models
FACE_PROTO = "opencv_face_detector.pbtxt"
FACE_MODEL = "opencv_face_detector_uint8.pb"
AGE_PROTO = "age_deploy.prototxt"
AGE_MODEL = "age_net.caffemodel"
# Constants
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
AGE_LIST = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
# Load networks
face_net = cv2.dnn.readNet(FACE_MODEL, FACE_PROTO)
age_net = cv2.dnn.readNet(AGE_MODEL, AGE_PROTO)
def detect_faces(frame, confidence_threshold=0.7):
"""Detect faces in a frame and return bounding boxes."""
frame_height = frame.shape[0]
frame_width = frame.shape[1]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], True, False)
face_net.setInput(blob)
detections = face_net.forward()
face_boxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > confidence_threshold:
x1 = int(detections[0, 0, i, 3] * frame_width)
y1 = int(detections[0, 0, i, 4] * frame_height)
x2 = int(detections[0, 0, i, 5] * frame_width)
y2 = int(detections[0, 0, i, 6] * frame_height)
face_boxes.append([x1, y1, x2, y2])
return face_boxes
def predict_age(frame, face_boxes, padding=20):
"""Predict age for each detected face."""
results = []
for box in face_boxes:
x1 = max(0, box[0] - padding)
y1 = max(0, box[1] - padding)
x2 = min(frame.shape[1] - 1, box[2] + padding)
y2 = min(frame.shape[0] - 1, box[3] + padding)
face = frame[y1:y2, x1:x2]
if face.size == 0:
continue
blob = cv2.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
age_net.setInput(blob)
age_preds = age_net.forward()
age = AGE_LIST[age_preds[0].argmax()]
confidence = age_preds[0].max()
results.append({
'box': box,
'age': age,
'confidence': confidence
})
return results
# Open webcam
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Could not open webcam.")
exit(1)
print("Age Detection - Webcam Mode")
print("Press 'q' to quit.")
while True:
ret, frame = cap.read()
if not ret:
print("Error: Could not read frame.")
break
# Detect faces and predict age
face_boxes = detect_faces(frame)
if face_boxes:
results = predict_age(frame, face_boxes)
# Annotate frame
for result in results:
box = result['box']
label = f"Age: {result['age']}"
cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
cv2.putText(frame, label, (box[0], box[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2, cv2.LINE_AA)
cv2.imshow("Age Detection - Press 'q' to quit", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
print("Webcam closed.")
Secret mission
Add Gender Detection
Your age estimation system predicts how old someone looks. But what if you could also predict gender? The same research team (Levi and Hassner) trained a gender classification model using the exact same architecture and preprocessing. In this secret mission, you will download the gender model, load it alongside the age model, and build a complete demographic analyzer that predicts both age and gender from faces in images and live webcam video.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- The age-estimation/ project folder on your Desktop (Python scripts, model files, output images).
- A Python virtual environment (venv) inside the project folder.
- Pre-trained model files (face detection, age prediction, gender prediction) totalling approximately 90MB.
Keep everything running
No action needed. Choose this if you're still actively experimenting with the age estimation scripts or want to try new images.
- Your project folder stays on your Desktop with all scripts and models ready to use.
- To run any script again, activate your virtual environment first with source venv/bin/activate (macOS/Linux) or venv\Scripts\activate (Windows).
Pause - I'll come back to this later
Shut down any running processes to free up memory. Your files stay on disk for later.
- If your webcam script is still running, press q in the video window to stop it.
- Deactivate your virtual environment by running:
deactivate
- Close the VS Code window if you don't need it open.
- Everything stays on your Desktop. When you come back, reactivate the virtual environment and pick up where you left off.
Delete - I don't want to use this again
Remove all project resources and start fresh. This deletes your scripts, virtual environment, and all downloaded model files.
- If your webcam script is still running, press q in the video window to stop it.
- Deactivate your virtual environment if it is still active:
deactivate
- Delete the entire project folder by running this command (macOS/Linux):
cd ~/Desktop
rm -rf age-estimation
On Windows, run this instead:
cd ~\Desktop
rmdir /s /q age-estimation
- Confirm the folder is gone by running ls (macOS/Linux) or dir (Windows). You should no longer see age-estimation in the output.
Nice Work!
Nice Work!
You just built a real-time Human Age Estimation system powered by deep learning. From loading pre-trained models to processing live webcam video, you now have hands-on experience with a production-style ML inference pipeline.
You've learned how to:
- Build a face detection system using OpenCV's DNN module with a pre-trained TensorFlow SSD model.
- Create an age prediction pipeline using a pre-trained Caffe CNN model that classifies faces into 8 age groups.
- Preprocess images for deep learning inference using blob creation and mean subtraction, then deploy models without training from scratch.
- Build a complete demographic analyzer that predicts both age and gender from faces in real-time.
Ready to quiz yourself?