Performing 1:N face matching on device#
Applies to LUNA ID for Android only.
This guide demonstrates how to perform 1:N face matching directly on an Android device using LUNA ID. The example shows how to search for a face in a pre-existing database of facial descriptors.
Overview#
The findFaceInDatabase function compares a facial image against a database of enrolled face descriptors. It returns the first match that exceeds a specified similarity threshold, providing a boolean result, similarity score, and the index of the matched face.
Function specification#
Method signature#
fun findFaceInDatabase(
image: Bitmap,
descriptorDb: List<ByteArray>,
scoreThreshold: Float = 0.7f
): Triple<Boolean, Float, Int>
Parameters#
| Parameter | Type | Description |
|---|---|---|
image |
Bitmap |
The facial image to identify. |
descriptorDb |
List<ByteArray> |
Database of enrolled face descriptors. |
scoreThreshold |
Float |
Minimum similarity score for a valid match (default: 0.7f). |
Return value#
Returns Triple<Boolean, Float, Int> containing:
| Component | Type | Description |
|---|---|---|
| First | Boolean |
true if a match exceeds the threshold, otherwise false. |
| Second | Float |
Similarity score of the best match. |
| Third | Int |
Index of the matched descriptor in the database, or -1 if no match found. |
Implementation#
import android.graphics.Bitmap
import ru.visionlabs.sdk.lunacore.utils.LunaUtils
/**
* Searches for the given face (image) in a database of face descriptors.
*
* @param image The input face image (Bitmap) to identify.
* @param descriptorDb The database of face descriptors. Each entry is a ByteArray descriptor.
* @param scoreThreshold Similarity score threshold to consider a match. Must be > 0.7f by spec.
*
* @return Triple(found, score, index)
* - found: true if a descriptor scoring strictly above [scoreThreshold] was found.
* - score: the score returned by LunaUtils.matchDescriptors for the winning candidate.
* If no match is found, this is the best score observed (or 0f if nothing was compared).
* - index: zero-based index of the matched descriptor in [descriptorDb], or -1 if not found.
*/
fun findFaceInDatabase(
image: Bitmap,
descriptorDb: List<ByteArray>,
scoreThreshold: Float = 0.7f
): Triple<Boolean, Float, Int> {
// 1. Extract the query descriptor from the input image
// If descriptor extraction fails or returns empty, we cannot proceed.
val queryDescriptor: ByteArray = LunaUtils.getDescriptor(image)
if (queryDescriptor.isEmpty()) {
return Triple(false, 0f, -1)
}
// 2. Iterate over the database and compare descriptors
// We keep track of the best score/index in case nothing crosses the threshold.
var bestScore = 0f
var bestIndex = -1
for (i in descriptorDb.indices) {
val candidate = descriptorDb[i]
if (candidate.isEmpty()) continue // Skip empty/invalid entries
// Compare using the provided native-backed matcher.
val score = LunaUtils.matchDescriptors(queryDescriptor, candidate)
// Early exit: as soon as the score is strictly greater than the threshold,
// we consider the face found and return immediately.
if (score > scoreThreshold) {
return Triple(true, score, i)
}
// Track the best score seen so far (useful to return diagnostics when not found).
if (score > bestScore) {
bestScore = score
bestIndex = i
}
}
// 3. No candidate passed the threshold; return the best observed score and its index (-1 if none)
return Triple(false, bestScore, bestIndex)
}
Usage example#
val (found, score, index) = findFaceInDatabase(faceBitmap, userRepository.getUsers())
Log.i("FaceSearch", "Face found: $found, score: $score, index: $index")