FaceDetector¶
Face detector based on insightface RetinaFace. It detects faces, extracts 512-dim ArcFace embeddings, supports GPU auto-detection (CUDA / DirectML) with CPU fallback, and includes a built-in blur and size quality filter.
Constructor Arguments¶
| Argument | Type | Default | Description |
|---|---|---|---|
confidence |
float |
0.50 |
Detection confidence threshold; faces below this are discarded |
device |
str |
"auto" |
Inference device: "cpu", "cuda", or "auto" (auto-detect best GPU) |
det_size |
int |
640 |
Model input size: 320 (fast), 480 (balanced), 640 (accurate) |
quality_filter |
bool |
True |
Enable blur-based quality filtering |
min_face_size |
int |
80 |
Minimum face size in pixels |
Methods¶
detect(frame)¶
Detect faces without extracting embeddings.
Parameters:
- frame (np.ndarray): BGR image (H, W, 3).
Returns:
- List[DetectionResult]
detect_with_embeddings(frame)¶
Detect faces and extract 512-dim embeddings.
Parameters:
- frame (np.ndarray): BGR image (H, W, 3).
Returns:
- List[DetectionWithEmbedding] sorted by confidence descending.
reload_model(det_size=None)¶
Reload the model at runtime with a new input size.
Parameters:
- det_size (int | None): New size, or None to keep current.
Basic Detection¶
import cv2
from face_hub import FaceDetector
detector = FaceDetector(device="auto", det_size=640)
frame = cv2.imread("photo.jpg")
results = detector.detect(frame)
print(f"Found {len(results)} face(s)")
for face in results:
x1, y1, x2, y2 = face.bbox.to_tuple()
print(f" box=({x1},{y1},{x2},{y2}), confidence={face.confidence:.2f}")
# Draw bounding box
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
Detection + Embeddings¶
faces = detector.detect_with_embeddings(frame)
for face in faces:
if face.quality_pass and face.has_embedding:
print(f"Embedding shape: {face.embedding.shape}") # (512,)
print(f"Embedding dtype: {face.embedding.dtype}") # float32
print(f"Is L2-normalized: {np.linalg.norm(face.embedding):.4f} ≈ 1.0")
Register a New Person from a Photo¶
import cv2
from face_hub import FaceDetector, FaceDatabase
detector = FaceDetector(device="cpu")
db = FaceDatabase()
def register_from_photo(name, photo_path):
"""Register a person from a single photo"""
frame = cv2.imread(photo_path)
if frame is None:
raise FileNotFoundError(f"Cannot read: {photo_path}")
faces = detector.detect_with_embeddings(frame)
if not faces:
raise ValueError(f"No face detected in {photo_path}")
# Pick the most confident face
face = faces[0]
if not face.has_embedding:
raise ValueError(f"Failed to extract embedding from {photo_path}")
ok, msg = db.add_person(name, photo_path, face.embedding)
if not ok:
raise ValueError(f"Database error: {msg}")
print(f"✅ {name} registered (confidence: {face.confidence:.0%})")
return True
# Register multiple people
for name, path in [("Alice", "photos/alice.jpg"), ("Bob", "photos/bob.jpg")]:
try:
register_from_photo(name, path)
except Exception as e:
print(f"❌ Failed to register {name}: {e}")
Switch Detection Resolution at Runtime¶
# Start with fast detection for live preview
detector = FaceDetector(det_size=320)
# Switch to high accuracy for a critical frame
detector.reload_model(det_size=640)
results = detector.detect_with_embeddings(important_frame)
# Switch back to fast mode
detector.reload_model(det_size=320)
Device Selection¶
from face_hub import FaceDetector
# Auto-detect best available: CUDA > DirectML > CPU
detector = FaceDetector(device="auto")
# Force CPU (useful for reproducible benchmarks or GPU-free servers)
detector_cpu = FaceDetector(device="cpu")
# Try GPU with explicit fallback
try:
detector = FaceDetector(device="cuda")
except Exception as e:
print(f"CUDA unavailable, falling back to CPU: {e}")
detector = FaceDetector(device="cpu")
Quality Filtering¶
# Stricter quality: skip faces smaller than 100 px, disable blur filter
detector = FaceDetector(
confidence=0.60,
min_face_size=100,
quality_filter=False,
)
for face in detector.detect_with_embeddings(frame):
if face.quality_pass:
print("✅ Good quality face")
else:
print("❌ Face too small or blurry — skipped")
Recommended Settings by Scenario¶
| Scenario | det_size |
confidence |
min_face_size |
|---|---|---|---|
| Real-time video (speed) | 320 | 0.50 | 80 |
| Balanced | 480 | 0.50 | 80 |
| Photo registration (accuracy) | 640 | 0.60 | 100 |
| High-security access | 640 | 0.65 | 100 |
# Real-time camera preview
live_detector = FaceDetector(device="auto", det_size=320)
# Photo registration / ID verification
photo_detector = FaceDetector(device="auto", det_size=640, confidence=0.60)
Performance Optimization¶
# If GPU memory is limited, reduce input size
low_memory_detector = FaceDetector(device="cuda", det_size=320)
# Disable quality filter for maximum speed
fast_detector = FaceDetector(device="auto", det_size=320, quality_filter=False)
# High quality for registration
quality_detector = FaceDetector(
device="auto",
det_size=640,
confidence=0.55,
min_face_size=100,
quality_filter=True,
)
Custom Detector¶
Implement DetectorProtocol to use your own model:
from typing import List
import numpy as np
from face_hub import DetectorProtocol, DetectionWithEmbedding, DetectionResult, BBox
class MyYoloDetector:
def detect_with_embeddings(self, frame: np.ndarray) -> List[DetectionWithEmbedding]:
boxes = self.yolo_model(frame) # your detector
results = []
for box in boxes:
roi = frame[box.y1:box.y2, box.x1:box.x2]
embedding = self.embedder(roi) # your embedder
results.append(DetectionWithEmbedding(
bbox=BBox(box.x1, box.y1, box.x2, box.y2),
confidence=box.conf,
embedding=embedding,
quality_pass=True,
))
return results
def detect(self, frame: np.ndarray) -> List[DetectionResult]:
return [
DetectionResult(
bbox=BBox(box.x1, box.y1, box.x2, box.y2),
confidence=box.conf,
)
for box in self.yolo_model(frame)
]
Notes¶
- The first call to
FaceDetector()downloads thebuffalo_linsightface model (~200 MB) if it is not already cached. - The model is cached locally; subsequent instantiations load from disk.
- Embeddings are L2-normalized 512-dim
float32vectors, suitable for cosine similarity matching. detect()anddetect_with_embeddings()both return results sorted by confidence descending.- When using
device="auto"on Windows, DirectML is preferred over CUDA for broader compatibility. reload_model()is useful for switching resolution without recreating the detector instance.