Skip to content

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")