Overview
After detecting a face, Iris uses face recognition to determine identity by:- Extracting a unique embedding (128-dimensional vector) from the face
- Comparing embeddings using cosine similarity
- Matching against a threshold to determine if two faces belong to the same person
Model:
face_recognition_sface_2021dec.onnx (December 2021 version)SFace Model
SFace is a lightweight face recognition network trained on millions of facial images to learn discriminative features.Key Characteristics
Input Size
112x112 pixels (aligned face crop)
Output
128-dimensional feature vector
Architecture
Convolutional neural network (CNN)
Training Data
MS-Celeb-1M, VGGFace2, Asian-Celeb
Why 128 Dimensions?
The embedding size balances:- Discriminative power: Enough dimensions to distinguish billions of faces
- Computation speed: Small enough for fast cosine similarity calculations
- Memory efficiency: Only 512 bytes per face (128 floats × 4 bytes)
Initialization
The recognizer is created when FaceEngine starts (face.rs:14-16):
face.rs
The recognizer runs on CPU by default. For GPU acceleration, set
target_id to an appropriate value based on your OpenCV build.Embedding Extraction
The process of converting a face image into a feature vector happens inface.rs:32-36:
Step 1: Face Alignment
face.rs
- Rotated to be upright
- Scaled to 112x112 pixels
- Cropped to include only the face region
Step 2: Feature Extraction
face.rs
- Each dimension captures abstract facial characteristics
- Similar faces produce vectors that point in similar directions in 128D space
- The vector is L2-normalized (unit length), enabling cosine similarity comparison
Embedding Properties
What Information Is Encoded?
The 128D embedding captures: ✅ Identity-specific features: Eye shape, nose structure, face geometry✅ Unique patterns: Facial proportions, distinctive characteristics
✅ Invariant representations: Robust to lighting, expression, minor aging ❌ Not stored: Skin color, gender, exact age (these vary too much)
❌ Not reversible: Cannot reconstruct the original image from embedding
Face Matching Algorithm
The comparison logic is inmain.rs:103-111:
main.rs
Cosine Similarity
Thematch_() function computes:
- A and B are the two face embeddings
- · is the dot product
- |A| is the vector magnitude (length)
Why Cosine Similarity?
Why Cosine Similarity?
Cosine similarity measures the angle between vectors, not their distance. This makes it:
- Scale-invariant: Only direction matters, not magnitude
- Fast to compute: Single dot product operation
- Robust: Works well for normalized feature vectors
The 0.363 Threshold
The critical line:if score > 0.363
What Does This Mean?
| Score Range | Interpretation | Action |
|---|---|---|
| > 0.500 | Very high confidence match | Same person |
| 0.363 - 0.500 | Likely match | Same person (threshold) |
| 0.300 - 0.363 | Uncertain | Different person |
| < 0.300 | Low similarity | Different person |
The 0.363 threshold is OpenCV’s recommended value for the SFace model, balancing false positives and false negatives.
Adjusting the Threshold
- Stricter (Lower FPR)
- Lenient (Lower FNR)
- Default (Balanced)
Probability Calculation
The API returns a “probability” to clients (main.rs:107):
main.rs
- Score 0.363 → 36% probability
- Score 0.500 → 50% probability
- Score 0.800 → 80% probability
- Score 0.950 → 95% probability
Recognition Accuracy
Benchmark Performance
On standard datasets (LFW, CFP-FP):- True Positive Rate: 99.1% at threshold 0.363
- False Positive Rate: 0.9% at threshold 0.363
- Equal Error Rate (EER): ~1%
Real-World Factors
Accuracy degrades with: ❌ Aging: Faces change over 5+ years❌ Occlusion: Masks, sunglasses, hats
❌ Lighting: Extreme shadows or backlighting
❌ Image quality: Low resolution, blur, compression artifacts
❌ Pose variation: Profiles vs. frontal views
Multi-Face Comparison
The API compares one target against multiple candidates (main.rs:92-113):
main.rs
Key Behaviors
- Independent comparisons: Each candidate is evaluated separately
- Parallel tolerance: Failed image downloads don’t stop processing
- Threshold filtering: Only matches above 0.363 are returned
- Sorted results: Best matches appear first in the response
Optimization Techniques
Mutex Locking Strategy
main.rs
Why Unsafe Pointers?
Why Unsafe Pointers?
OpenCV’s Rust bindings require mutable access to internal state during inference. The
unsafe block extracts raw pointers while the Mutex guard is held, ensuring:- Thread safety: Only one request processes recognition at a time
- Memory safety: Pointers are valid only within the guard’s lifetime
- Performance: Avoids unnecessary cloning of large model weights
unsafe because the Mutex guarantees exclusive access.Embedding Reuse
If you’re comparing one face against a large database:- Extract target embedding once
- Compare against all stored embeddings
- Sort by similarity score
main.rs:73-85.
Security Considerations
Spoofing Attacks
SFace is vulnerable to: ⚠️ Photo attacks: Holding a printed photo of someone⚠️ Screen attacks: Showing a face on a phone screen
⚠️ Deep fakes: AI-generated fake faces
Privacy Implications
Embeddings cannot reconstruct original images, but:- They uniquely identify individuals
- They can be used for tracking across databases
- They should be treated as biometric data under GDPR/CCPA
Iris’s stateless design helps with privacy—no embeddings are stored permanently. See Stateless Design for details.
Comparison with Other Models
| Model | Accuracy | Speed | Size | Best For |
|---|---|---|---|---|
| SFace | 99.1% | ⚡⚡⚡ Fast | 27 MB | Production APIs, edge devices |
| ArcFace | 99.8% | ⚡⚡ Medium | 90 MB | High accuracy needs |
| FaceNet | 99.6% | ⚡⚡ Medium | 128 MB | Research, offline processing |
| DeepFace | 97.4% | ⚡ Slow | 140 MB | Legacy applications |