Face Detection and Recognition for Drones
Face detection and recognition enable security drones to identify individuals, monitor restricted areas, and assist in search operations. This tutorial covers the complete pipeline from face detection to identity verification.
Face Recognition Pipeline
From pixel capture to identity verification, the face recognition pipeline transforms visual data into actionable intelligence.
**Real-world analogy:** Face recognition from a drone is like a security guard with a photo album. They see a face, compare it to their memory, and determine if the person is authorized. The challenge is doing this accurately from a moving platform at varying distances.
Face Detection
from dataclasses import dataclass
from typing import List, Tuple
@dataclass
class FaceDetection:
"""Single face detection."""
bbox: Tuple[int, int, int, int] # x1, y1, x2, y2
confidence: float
landmarks: np.ndarray # 5 facial landmarks
embedding: np.ndarray = None
class FaceDetector:
"""Face detection optimized for drone imagery."""
def __init__(self, confidence_threshold=0.7, nms_threshold=0.4):
self.confidence_threshold = confidence_threshold
self.nms_threshold = nms_threshold
# Simplified anchor boxes for face detection
self.anchors = self._generate_anchors()
def _generate_anchors(self):
"""Generate anchor boxes for face detection."""
anchors = []
scales = [16, 32, 64, 128]
ratios = [1.0, 0.75, 1.33]
for scale in scales:
for ratio in ratios:
w = scale * np.sqrt(ratio)
h = scale / np.sqrt(ratio)
anchors.append((w, h))
return anchors
def detect_faces(self, image):
"""Detect faces in image."""
h, w = image.shape[:2]
# Simulate face detection (in production, use RetinaFace or MTCNN)
detections = []
num_faces = np.random.randint(0, 5)
for _ in range(num_faces):
# Random face position
x1 = np.random.randint(0, w - 100)
y1 = np.random.randint(0, h - 100)
face_w = np.random.randint(40, min(150, w - x1))
face_h = int(face_w * 1.2) # Faces are typically taller than wide
confidence = np.random.uniform(0.6, 0.99)
if confidence > self.confidence_threshold:
# Generate 5 landmarks (eyes, nose, mouth corners)
landmarks = np.array([
[x1 + face_w * 0.3, y1 + face_h * 0.35], # Left eye
[x1 + face_w * 0.7, y1 + face_h * 0.35], # Right eye
[x1 + face_w * 0.5, y1 + face_h * 0.55], # Nose
[x1 + face_w * 0.3, y1 + face_h * 0.7], # Left mouth
[x1 + face_w * 0.7, y1 + face_h * 0.7], # Right mouth
])
detections.append(FaceDetection(
bbox=(x1, y1, x1 + face_w, y1 + face_h),
confidence=confidence,
landmarks=landmarks
))
# Apply NMS
return self._non_max_suppression(detections)
def _non_max_suppression(self, detections):
"""Apply Non-Maximum Suppression."""
if not detections:
return []
# Sort by confidence
sorted_dets = sorted(detections, key=lambda d: d.confidence, reverse=True)
keep = []
while sorted_dets:
current = sorted_dets.pop(0)
keep.append(current)
remaining = []
for det in sorted_dets:
iou = self._compute_iou(current.bbox, det.bbox)
if iou < self.nms_threshold:
remaining.append(det)
sorted_dets = remaining
return keep
def _compute_iou(self, box1, box2):
"""Compute Intersection over Union."""
x1 = max(box1[0], box2[0])
y1 = max(box1[1], box2[1])
x2 = min(box1[2], box2[2])
y2 = min(box1[3], box2[3])
intersection = max(0, x2 - x1) * max(0, y2 - y1)
area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
union = area1 + area2 - intersection
return intersection / union if union > 0 else 0
def align_face(self, image, detection, target_size=(112, 112)):
"""Align face using landmarks."""
# Get eye positions
left_eye = detection.landmarks[0]
right_eye = detection.landmarks[1]
# Compute rotation angle
dx = right_eye[0] - left_eye[0]
dy = right_eye[1] - left_eye[1]
angle = np.degrees(np.arctan2(dy, dx))
# Compute center between eyes
eye_center = ((left_eye[0] + right_eye[0]) / 2,
(left_eye[1] + right_eye[1]) / 2)
# Simple alignment (in production, use affine transformation)
aligned = np.random.randint(0, 255, (*target_size, 3), dtype=np.uint8)
return aligned, angle
# Example: Face detection
np.random.seed(42)
detector = FaceDetector(confidence_threshold=0.7)
print("Face Detection for Drone Security")
print("=" * 50)
# Simulate drone frame
frame = np.random.randint(0, 255, (720, 1280, 3), dtype=np.uint8)
# Detect faces
detections = detector.detect_faces(frame)
print(f"\nDetected {len(detections)} face(s):")
for i, det in enumerate(detections):
print(f"\n Face {i+1}:")
print(f" BBox: {det.bbox}")
print(f" Confidence: {det.confidence:.1%}")
print(f" Landmarks: 5 points detected")
# Align face
aligned, angle = detector.align_face(frame, det)
print(f" Alignment angle: {angle:.1f}°")
Face Embedding and Recognition
from typing import Dict, List, Optional
class FaceRecognizer:
"""Face recognition using deep embeddings."""
def __init__(self, embedding_dim=512, similarity_threshold=0.6):
self.embedding_dim = embedding_dim
self.similarity_threshold = similarity_threshold
self.database = {} # name -> embedding
def compute_embedding(self, aligned_face):
"""Compute face embedding (simulated ArcFace/AdaFace)."""
# In production, this would use a trained model
embedding = np.random.randn(self.embedding_dim).astype(np.float32)
embedding = embedding / np.linalg.norm(embedding) # L2 normalize
return embedding
def register_face(self, name, embedding):
"""Register a new face in the database."""
self.database[name] = embedding
def recognize(self, embedding):
"""Recognize face against database."""
if not self.database:
return None, 0.0
best_match = None
best_similarity = -1
for name, db_embedding in self.database.items():
similarity = self._cosine_similarity(embedding, db_embedding)
if similarity > best_similarity:
best_similarity = similarity
best_match = name
if best_similarity >= self.similarity_threshold:
return best_match, best_similarity
else:
return "Unknown", best_similarity
def _cosine_similarity(self, embedding1, embedding2):
"""Compute cosine similarity between embeddings."""
dot_product = np.dot(embedding1, embedding2)
norm1 = np.linalg.norm(embedding1)
norm2 = np.linalg.norm(embedding2)
return dot_product / (norm1 * norm2 + 1e-8)
def verify(self, embedding1, embedding2):
"""Verify if two embeddings are from the same person."""
similarity = self._cosine_similarity(embedding1, embedding2)
return similarity >= self.similarity_threshold, similarity
def search_database(self, query_embedding, top_k=5):
"""Search database for similar faces."""
if not self.database:
return []
similarities = []
for name, db_embedding in self.database.items():
similarity = self._cosine_similarity(query_embedding, db_embedding)
similarities.append((name, similarity))
# Sort by similarity
similarities.sort(key=lambda x: x[1], reverse=True)
return similarities[:top_k]
# Example: Face recognition
np.random.seed(42)
recognizer = FaceRecognizer(embedding_dim=512, similarity_threshold=0.6)
print("Face Recognition System")
print("=" * 50)
# Register known faces
known_persons = ['Alice', 'Bob', 'Carol', 'David']
print("\nRegistering known faces:")
for name in known_persons:
embedding = np.random.randn(512).astype(np.float32)
embedding = embedding / np.linalg.norm(embedding)
recognizer.register_face(name, embedding)
print(f" Registered: {name}")
# Test recognition
print("\nTesting recognition:")
for _ in range(5):
# Generate query embedding
query = np.random.randn(512).astype(np.float32)
query = query / np.linalg.norm(query)
# Recognize
name, confidence = recognizer.recognize(query)
print(f" Recognized: {name} (similarity: {confidence:.2%})")
# Verify same person
print("\nVerification test:")
emb1 = np.random.randn(512).astype(np.float32)
emb2 = emb1 + np.random.normal(0, 0.1, 512) # Similar embedding
is_match, similarity = recognizer.verify(emb1, emb2)
print(f" Same person: {is_match} (similarity: {similarity:.2%})")
Anti-Spoofing and Liveness Detection
from dataclasses import dataclass
from typing import List
@dataclass
class LivenessResult:
"""Liveness detection result."""
is_live: bool
confidence: float
spoof_type: Optional[str] # 'photo', 'video', 'mask', None
class LivenessDetector:
"""Detect face spoofing attempts."""
def __init__(self):
self.texture_threshold = 0.5
self.depth_threshold = 0.6
self.motion_threshold = 0.4
def check_texture_analysis(self, face_image):
"""Analyze texture patterns for spoofing detection."""
# Real faces have specific texture patterns
# Spoofed faces (photos, screens) have different textures
h, w = face_image.shape[:2]
# Compute Local Binary Pattern (simplified)
lbp_features = np.random.uniform(0, 1, 10)
# Real faces typically have higher texture variance
texture_score = np.mean(lbp_features)
return texture_score
def check_depth_analysis(self, face_image, depth_map=None):
"""Check depth information for 3D face detection."""
if depth_map is None:
# Simulate depth estimation
depth_map = np.random.uniform(0.3, 0.8, face_image.shape[:2])
# Real faces have consistent depth profile
# Photos have flat depth
depth_variance = np.var(depth_map)
depth_score = min(depth_variance * 5, 1.0)
return depth_score
def check_motion_analysis(self, frame_sequence):
"""Analyze temporal motion for liveness."""
if len(frame_sequence) < 3:
return 0.5
# Check for natural micro-movements
frame_diffs = []
for i in range(1, len(frame_sequence)):
diff = np.mean(np.abs(frame_sequence[i].astype(float) -
frame_sequence[i-1].astype(float)))
frame_diffs.append(diff)
# Natural faces have subtle, consistent motion
motion_variance = np.var(frame_diffs)
motion_mean = np.mean(frame_diffs)
# Score based on motion patterns
if motion_mean > 0 and motion_variance < 10:
motion_score = 0.8 # Natural motion
elif motion_mean < 1:
motion_score = 0.3 # Too still (photo)
else:
motion_score = 0.5 # Uncertain
return motion_score
def detect_liveness(self, face_image, frame_sequence=None, depth_map=None):
"""Complete liveness detection."""
# Texture analysis
texture_score = self.check_texture_analysis(face_image)
# Depth analysis
depth_score = self.check_depth_analysis(face_image, depth_map)
# Motion analysis
if frame_sequence:
motion_score = self.check_motion_analysis(frame_sequence)
else:
motion_score = 0.5
# Combined score
combined_score = (texture_score * 0.4 +
depth_score * 0.3 +
motion_score * 0.3)
# Determine liveness
is_live = combined_score > 0.5
# Detect spoof type
spoof_type = None
if not is_live:
if texture_score < 0.3:
spoof_type = 'photo'
elif motion_score < 0.3:
spoof_type = 'video_replay'
elif depth_score < 0.3:
spoof_type = 'mask'
return LivenessResult(
is_live=is_live,
confidence=combined_score,
spoof_type=spoof_type
)
# Example: Liveness detection
np.random.seed(42)
liveness_detector = LivenessDetector()
print("Liveness Detection for Drone Security")
print("=" * 50)
# Test different scenarios
scenarios = [
('Real face', 0.8),
('Photo attack', 0.3),
('Video replay', 0.4),
('Mask attack', 0.35),
]
for scenario_name, base_score in scenarios:
# Simulate face image
face_image = np.random.randint(0, 255, (112, 112, 3), dtype=np.uint8)
# Simulate frame sequence
frame_sequence = [face_image + np.random.randint(-5, 5, face_image.shape, dtype=np.uint8)
for _ in range(5)]
result = liveness_detector.detect_liveness(face_image, frame_sequence)
print(f"\n{scenario_name}:")
print(f" Live: {result.is_live}")
print(f" Confidence: {result.confidence:.1%}")
print(f" Spoof type: {result.spoof_type}")
Hands-On Project: Security Drone Face System
Build a complete security drone face recognition system.
from collections import defaultdict
from datetime import datetime
class SecurityDroneFaceSystem:
"""Complete face recognition system for security drones."""
def __init__(self):
self.face_detector = FaceDetector(confidence_threshold=0.7)
self.face_recognizer = FaceRecognizer(embedding_dim=512)
self.liveness_detector = LivenessDetector()
# Security configuration
self.watchlist = set()
self.authorized_persons = set()
self.alert_log = []
self.access_log = []
# Patrol state
self.patrol_zones = {}
self.detected_faces_history = defaultdict(list)
def register_authorized(self, name, embedding):
"""Register authorized person."""
self.authorized_persons.add(name)
self.face_recognizer.register_face(name, embedding)
def add_to_watchlist(self, name, reason):
"""Add person to security watchlist."""
self.watchlist.add(name)
self.alert_log.append({
'type': 'watchlist_add',
'name': name,
'reason': reason,
'timestamp': datetime.now().isoformat()
})
def process_frame(self, frame, zone_id='default'):
"""Process frame for face recognition."""
results = {
'faces_detected': 0,
'recognized': [],
'alerts': [],
'zone': zone_id
}
# Detect faces
detections = self.face_detector.detect_faces(frame)
results['faces_detected'] = len(detections)
for det in detections:
# Check liveness
liveness = self.liveness_detector.detect_liveness(frame)
if not liveness.is_live:
results['alerts'].append({
'type': 'spoof_detected',
'confidence': liveness.confidence,
'spoof_type': liveness.spoof_type
})
continue
# Compute embedding
aligned, _ = self.face_detector.align_face(frame, det)
embedding = self.face_recognizer.compute_embedding(aligned)
# Recognize
name, confidence = self.face_recognizer.recognize(embedding)
# Log detection
face_info = {
'name': name,
'confidence': confidence,
'bbox': det.bbox,
'timestamp': datetime.now().isoformat(),
'zone': zone_id
}
results['recognized'].append(face_info)
self.detected_faces_history[name].append(face_info)
# Check watchlist
if name in self.watchlist:
results['alerts'].append({
'type': 'watchlist_match',
'name': name,
'confidence': confidence
})
# Check authorization
if name in self.authorized_persons:
self.access_log.append({
'name': name,
'zone': zone_id,
'timestamp': datetime.now().isoformat(),
'status': 'authorized'
})
elif name != "Unknown":
results['alerts'].append({
'type': 'unauthorized_person',
'name': name,
'confidence': confidence
})
return results
def generate_security_report(self):
"""Generate security report."""
report = {
'total_detections': sum(len(detections)
for detections in self.detected_faces_history.values()),
'unique_persons': len(self.detected_faces_history),
'watchlist_alerts': len([a for a in self.alert_log
if a['type'] == 'watchlist_add']),
'access_events': len(self.access_log),
'zones_monitored': len(set(d['zone'] for d in self.access_log)),
'persons_by_frequency': {}
}
# Count detections per person
for name, detections in self.detected_faces_history.items():
report['persons_by_frequency'][name] = len(detections)
# Sort by frequency
report['persons_by_frequency'] = dict(
sorted(report['persons_by_frequency'].items(),
key=lambda x: x[1], reverse=True)
)
return report
def check_zone_security(self, zone_id):
"""Check security status of a zone."""
zone_detections = []
for name, detections in self.detected_faces_history.items():
for det in detections:
if det['zone'] == zone_id:
zone_detections.append(det)
zone_alerts = [a for a in self.alert_log
if a.get('zone') == zone_id]
return {
'zone': zone_id,
'total_detections': len(zone_detections),
'unique_persons': len(set(d['name'] for d in zone_detections)),
'alerts': len(zone_alerts),
'status': 'secure' if not zone_alerts else 'alert'
}
# Example: Security drone operation
np.random.seed(42)
security_system = SecurityDroneFaceSystem()
print("Security Drone Face Recognition System")
print("=" * 55)
# Register authorized personnel
print("\nRegistering authorized personnel:")
authorized = ['Alice (Security)', 'Bob (Manager)', 'Carol (Engineer)']
for name in authorized:
embedding = np.random.randn(512).astype(np.float32)
embedding = embedding / np.linalg.norm(embedding)
security_system.register_authorized(name, embedding)
print(f" Registered: {name}")
# Add to watchlist
print("\nAdding to watchlist:")
security_system.add_to_watchlist('Unknown Suspicious', 'Reported trespasser')
print(" Added: Unknown Suspicious")
# Simulate patrol
print("\nSimulating patrol across zones:")
zones = ['Zone_A', 'Zone_B', 'Zone_C', 'Zone_A']
for i, zone in enumerate(zones):
frame = np.random.randint(0, 255, (720, 1280, 3), dtype=np.uint8)
results = security_system.process_frame(frame, zone)
print(f"\n Frame {i+1} - {zone}:")
print(f" Faces detected: {results['faces_detected']}")
print(f" Recognized: {[f['name'] for f in results['recognized']]}")
if results['alerts']:
print(f" ALERTS: {[a['type'] for a in results['alerts']]}")
# Generate report
report = security_system.generate_security_report()
print(f"\nSecurity Report:")
print(f" Total detections: {report['total_detections']}")
print(f" Unique persons: {report['unique_persons']}")
print(f" Access events: {report['access_events']}")
print(f" Zones monitored: {report['zones_monitored']}")
print(f" Persons by frequency: {report['persons_by_frequency']}")
# Check zone status
for zone in ['Zone_A', 'Zone_B']:
status = security_system.check_zone_security(zone)
print(f"\n {zone}: {status['status']} "
f"({status['total_detections']} detections)")
print("\nSecurity drone system operational!")
Key Takeaways
- Face detection identifies and locates faces in drone footage
- Alignment normalizes faces for consistent recognition
- Embeddings create unique 512-dimensional face signatures
- Liveness detection prevents spoofing attacks
- Security integration enables automated monitoring and alerts
Next, we'll explore video analysis for action recognition and anomaly detection.