Documentation Index
Fetch the complete documentation index at: https://mintlify.com/shubham0204/OnDevice-Face-Recognition-Android/llms.txt
Use this file to discover all available pages before exploring further.
APIs for interacting with the ObjectBox database to manage persons and their associated face records.
PersonDB
Database access layer for managing person records.
@Single
class PersonDB {
private val personBox = ObjectBoxStore.store.boxFor(PersonRecord::class.java)
fun addPerson(person: PersonRecord): Long
fun removePerson(personID: Long)
fun getCount(): Long
fun getAll(): Flow<MutableList<PersonRecord>>
}
Methods
addPerson
Add a new person to the database.
Returns: Long - The auto-generated person ID
val person = PersonRecord(
personName = "John Doe",
numImages = 0,
addTime = System.currentTimeMillis()
)
val personID = personDB.addPerson(person)
removePerson
Remove a person and all associated face records from the database.
The ID of the person to remove
personDB.removePerson(personID = 123)
This operation only removes the PersonRecord. You must also call ImagesVectorDB.removeFaceRecordsWithPersonID() to delete the associated face embeddings.
getCount
Get the total number of persons in the database.
Returns: Long - The count of person records
val totalPersons = personDB.getCount()
println("Database contains $totalPersons persons")
getAll
Get a Flow of all person records, automatically updated when the database changes.
Returns: Flow<MutableList<PersonRecord>> - Reactive flow of person records
personDB.getAll().collect { persons ->
persons.forEach { person ->
println("${person.personName}: ${person.numImages} images")
}
}
The Flow uses Dispatchers.IO internally, so database queries happen on a background thread.
Usage example
Complete workflow for managing persons:
// Inject PersonDB
@Single
class PersonUseCase(
private val personDB: PersonDB
) {
// Add a new person
fun addPerson(name: String, numImages: Long): Long {
return personDB.addPerson(
PersonRecord(
personName = name,
numImages = numImages,
addTime = System.currentTimeMillis()
)
)
}
// Remove a person
fun removePerson(id: Long) {
personDB.removePerson(id)
}
// Get all persons
fun getAll(): Flow<List<PersonRecord>> = personDB.getAll()
}
PersonUseCase
Use case layer for person management operations, providing a clean API for ViewModels.
@Single
class PersonUseCase(
private val personDB: PersonDB
) {
fun addPerson(name: String, numImages: Long): Long
fun removePerson(id: Long)
fun getAll(): Flow<List<PersonRecord>>
fun getCount(): Long
}
Methods
addPerson
Add a new person with the current timestamp.
Initial number of face images (typically 0)
Returns: Long - The auto-generated person ID
val personID = personUseCase.addPerson(
name = "Alice Johnson",
numImages = 0
)
removePerson
Remove a person from the database.
personUseCase.removePerson(id = personID)
getAll
Get a reactive Flow of all persons.
Returns: Flow<List<PersonRecord>> - Flow emitting updated person lists
val personsFlow = personUseCase.getAll()
getCount
Get the total count of persons.
Returns: Long - Number of persons in database
val count = personUseCase.getCount()
ImageVectorUseCase
Use case for face recognition operations, coordinating face detection, embedding generation, and vector search.
@Single
class ImageVectorUseCase(
private val faceDetector: BaseFaceDetector,
private val faceSpoofDetector: FaceSpoofDetector,
private val imagesVectorDB: ImagesVectorDB,
private val faceNet: FaceNet
) {
data class FaceRecognitionResult(
val personName: String,
val boundingBox: Rect,
val spoofResult: FaceSpoofDetector.FaceSpoofResult? = null
)
suspend fun addImage(personID: Long, personName: String, imageUri: Uri): Result<Boolean>
suspend fun getNearestPersonName(frameBitmap: Bitmap, flatSearch: Boolean): Pair<RecognitionMetrics?, List<FaceRecognitionResult>>
fun removeAllImagesForPerson(personID: Long)
}
Data classes
FaceRecognitionResult
Recognition result for a detected face.
The recognized person’s name, or “NOT RECOGNIZED” if no match found
Face bounding box coordinates in the frame
spoofResult
FaceSpoofDetector.FaceSpoofResult?
default:"null"
Spoof detection result if enabled, null otherwise
Methods
addImage
Add a face image for enrollment.
URI of the image to process
Returns: Result<Boolean> - Success/failure with error details
val result = imageVectorUseCase.addImage(
personID = 123,
personName = "John Doe",
imageUri = selectedImageUri
)
result.onSuccess {
println("Face enrolled successfully")
}.onFailure { error ->
println("Enrollment failed: ${error.message}")
}
The method performs:
- Face detection on the image
- FaceNet embedding generation
- Storage in vector database with person metadata
getNearestPersonName
Recognize faces in a camera frame.
The camera frame to analyze
Whether to use precise flat search (true) or HNSW approximate search (false)
Returns: Pair<RecognitionMetrics?, List<FaceRecognitionResult>> - Metrics and recognition results for all detected faces
val (metrics, results) = imageVectorUseCase.getNearestPersonName(
frameBitmap = cameraFrame,
flatSearch = false
)
results.forEach { result ->
if (result.personName != "NOT RECOGNIZED") {
println("Recognized: ${result.personName}")
println("Bounding box: ${result.boundingBox}")
result.spoofResult?.let { spoof ->
if (spoof.isSpoof) {
println("WARNING: Spoof detected!")
}
}
}
}
The pipeline:
- Detect all faces in frame
- Generate embeddings for each face
- Search vector database for matches
- Run spoof detection
- Return results with metrics
Use flatSearch = false for real-time recognition (faster). Use flatSearch = true when accuracy is critical and you have time for linear search.
removeAllImagesForPerson
Remove all face embeddings for a person.
The person ID whose face records should be deleted
imageVectorUseCase.removeAllImagesForPerson(personID = 123)
This only removes FaceImageRecord entries. To fully remove a person, also call PersonUseCase.removePerson().
Complete enrollment workflow
// 1. Create person record
val personID = personUseCase.addPerson(
name = "Alice",
numImages = 0
)
// 2. Enroll multiple face images
imageUris.forEach { uri ->
imageVectorUseCase.addImage(
personID = personID,
personName = "Alice",
imageUri = uri
).onFailure { error ->
println("Failed to enroll image: ${error.message}")
}
}
// 3. Update person record with image count
val updatedPerson = PersonRecord(
personID = personID,
personName = "Alice",
numImages = imageUris.size.toLong(),
addTime = System.currentTimeMillis()
)
Complete recognition workflow
// Get camera frame
val bitmap = cameraPreview.getBitmap()
// Perform recognition
val (metrics, results) = imageVectorUseCase.getNearestPersonName(
frameBitmap = bitmap,
flatSearch = false
)
// Process results
results.forEach { result ->
val isRecognized = result.personName != "NOT RECOGNIZED"
val isReal = result.spoofResult?.isSpoof == false
when {
!isRecognized -> drawBox(result.boundingBox, Color.RED, "Unknown")
!isReal -> drawBox(result.boundingBox, Color.YELLOW, "Spoof")
else -> drawBox(result.boundingBox, Color.GREEN, result.personName)
}
}
// Display metrics
metrics?.let {
println("Total pipeline time: ${
it.timeFaceDetection +
it.timeFaceEmbedding +
it.timeVectorSearch +
it.timeFaceSpoofDetection
}ms")
}
Source: domain/PersonUseCase.kt, domain/ImageVectorUseCase.kt, data/PersonDB.kt