Architecture overview
The app uses a three-layer architecture with clear separation of concerns:Layer responsibilities
Presentation layer
Handles all UI and user interactions using Jetpack Compose:-
Screens: Full-screen composables for different app features
DetectScreen: Real-time face recognition with cameraAddFaceScreen: Enroll new faces from galleryFaceListScreen: View and manage stored faces
-
ViewModels: State management and business logic coordination
DetectScreenViewModel: Manages camera frames and recognition resultsAddFaceScreenViewModel: Handles image selection and enrollmentFaceListScreenViewModel: Lists persons and their face records
-
Components: Reusable UI elements
FaceDetectionOverlay: Custom view for drawing bounding boxesAppAlertDialog,AppProgressDialog: Standard dialogs
Domain layer
Contains core business logic and ML model wrappers:Use cases
Orchestrate complex operations across multiple components:ImageVectorUseCase - Primary use case for face operations:
addImage(): Enroll a face from URIgetNearestPersonName(): Recognize faces in frameremoveImages(): Delete face records
PersonUseCase - Manages person records:
- Create/delete person entries
- Track number of images per person
- Handle person metadata
Model wrappers
FaceNet - Embedding generation:
- Loads TFLite model (
facenet_512.tflite) - Configures GPU/NNAPI acceleration
- Normalizes input images
- Returns 512D embeddings
BaseFaceDetector - Abstract detector interface:
getCroppedFace(): Detect single face from URIgetAllCroppedFaces(): Detect multiple faces from Bitmap- Handles EXIF orientation correction
- Validates bounding boxes
FaceSpoofDetector - Anti-spoofing detection:
- Runs two MiniFASNet models at different scales
- Performs RGB to BGR conversion
- Combines predictions with softmax
Data layer
Manages persistence and data models:Databases
ImagesVectorDB - Face embedding storage:
- Wraps ObjectBox for vector operations
- Supports ANN (HNSW) and flat search
- Implements cosine similarity
- Parallelizes linear search across 4 threads
PersonDB - Person record storage:
- Maintains person metadata
- Links to face image records via
personID - Tracks enrollment timestamps
ObjectBoxStore - Database initialization:
- Singleton store instance
- Initialized in
MainApplication.onCreate()
Data models
Dependency injection
The app uses Koin for dependency injection with annotations:@Single are automatically injected:
FaceNetFaceSpoofDetectorImagesVectorDBPersonDBImageVectorUseCasePersonUseCase
The choice between MLKit and Mediapipe face detectors is configured at compile-time in
AppModule.kt by setting isMLKit.Component interactions
Enrollment flow
Recognition flow
Thread management
The app uses Kotlin coroutines for concurrency:- Main dispatcher: UI updates, user interactions
- IO dispatcher: File operations, database queries, face detection
- Default dispatcher: CPU-intensive tasks (embedding generation, similarity calculation)
ML model inference (FaceNet, spoof detection) runs on
Dispatchers.Default to avoid blocking I/O operations.Configuration points
Key architectural decisions can be configured:- Face detector choice:
AppModule.kt- MLKit vs Mediapipe - FaceNet model:
FaceNet.kt- 128D vs 512D embeddings - Search method:
FaceDetectionOverlay.kt- ANN vs flat search - Similarity threshold:
ImageVectorUseCase.kt- Recognition sensitivity - GPU acceleration:
FaceNet.kt- GPU, NNAPI, or CPU-only