Real-time face detection with OpenCV's YuNet

Real-time face detection is a powerful tool for a variety of applications, from security monitoring to interactive user experiences. In this post, we explore how to set up a streaming face detection pipeline in Python using OpenCV's YuNet detector, a state-of-the-art solution that offers excellent performance on CPU hardware.
Why real-time face detection?
Detecting faces in real time enables various applications, from security systems to interactive installations. Modern face detection algorithms like YuNet provide robust detection capabilities while maintaining high performance, making them suitable for production environments.
Setting up your environment
Before implementing face detection, set up your development environment:
# Install required packages
pip install opencv-python numpy
# Download YuNet model
git clone https://github.com/opencv/opencv_zoo.git
cd opencv_zoo/models/face_detection_yunet/
Building a basic streaming face detection pipeline
Here's a complete example that captures video from your webcam and processes each frame using YuNet:
import cv2
import numpy as np
# Initialize YuNet face detector
face_detector = cv2.FaceDetectorYN.create(
'face_detection_yunet_2023mar.onnx',
"",
(320, 320),
0.9, # score threshold
0.3, # nms threshold
5000 # top k
)
# Initialize webcam
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
# Set input size
height, width, _ = frame.shape
face_detector.setInputSize((width, height))
# Detect faces
_, faces = face_detector.detect(frame)
# Draw results
if faces is not None:
for face in faces:
box = face[0:4].astype(np.int32)
cv2.rectangle(frame, (box[0], box[1]),
(box[0] + box[2], box[1] + box[3]),
(0, 255, 0), 2)
cv2.imshow('Real-time Face Detection', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
This script initializes the YuNet detector, captures video frames, and draws bounding boxes around detected faces in real-time.
Advanced usage: handling multiple faces and parameter adjustments
YuNet offers several parameters to fine-tune detection performance:
# Advanced configuration
face_detector = cv2.FaceDetectorYN.create(
'face_detection_yunet_2023mar.onnx',
"",
(320, 320),
0.7, # Lower threshold for higher sensitivity
0.4, # Adjusted NMS threshold
10000 # Increased top k for more faces
)
# Error handling and face processing
try:
_, faces = face_detector.detect(frame)
if faces is not None:
for face in faces:
confidence = face[4]
if confidence > 0.7: # Additional confidence filter
box = face[0:4].astype(np.int32)
landmarks = face[5:15].astype(np.int32).reshape(-1, 2)
# Draw face box
cv2.rectangle(frame, (box[0], box[1]),
(box[0] + box[2], box[1] + box[3]),
(0, 255, 0), 2)
# Draw landmarks
for landmark in landmarks:
cv2.circle(frame, landmark, 2, (0, 255, 255), -1)
except Exception as e:
print(f"Detection error: {e}")
This enhanced version includes landmark detection and proper error handling.
Optimizing performance
To achieve optimal performance in real-time applications:
- Resize input frames:
scale = 0.5
resized = cv2.resize(frame, None, fx=scale, fy=scale)
face_detector.setInputSize((resized.shape[1], resized.shape[0]))
- Process frames asynchronously:
from concurrent.futures import ThreadPoolExecutor
def process_frame(frame):
_, faces = face_detector.detect(frame)
return faces
with ThreadPoolExecutor(max_workers=2) as executor:
future = executor.submit(process_frame, frame)
faces = future.result()
- Implement frame skipping when needed:
frame_count = 0
process_every = 2 # Process every second frame
while True:
ret, frame = cap.read()
frame_count += 1
if frame_count % process_every == 0:
# Process frame
_, faces = face_detector.detect(frame)
Practical use case: security monitoring
Here's a practical example of a security monitoring system that logs face detections:
from datetime import datetime
import json
class FaceMonitor:
def __init__(self, log_file='face_detections.json'):
self.log_file = log_file
self.face_detector = cv2.FaceDetectorYN.create(
'face_detection_yunet_2023mar.onnx',
"",
(320, 320),
0.9,
0.3,
5000
)
def log_detection(self, num_faces):
entry = {
'timestamp': datetime.now().isoformat(),
'faces_detected': num_faces
}
with open(self.log_file, 'a') as f:
json.dump(entry, f)
f.write('\n')
def monitor(self, frame):
_, faces = self.face_detector.detect(frame)
if faces is not None:
self.log_detection(len(faces))
return faces
Performance considerations
YuNet offers impressive performance metrics:
- CPU performance: 30-50 FPS on modern processors
- Memory usage: Approximately 30MB
- Detection accuracy: 95%+ on standard benchmarks
These metrics make it suitable for most real-time applications without requiring specialized hardware.
Conclusion
Implementing real-time face detection has become more accessible with modern tools like OpenCV's YuNet detector. The combination of high performance and accuracy makes it an excellent choice for various applications. For those interested in exploring automated image analysis solutions at scale, check out Transloadit's AI service documentation.