Skip to content

Configuring the camera#

Applies to LUNA ID for Android only.

LUNA ID for Android uses Google's CameraX library to provide flexible and reliable camera control, enabling you to customize key video capture parameters for optimal face detection and analysis.

Key camera parameters#

The following parameters are part of ShowCameraParams and define how the camera operates during a face capture session:

@Serializable(with = CameraSelectorSerializer::class)
val cameraSelector: CameraSelector = defaultCameraSelector(),

@Serializable(with = ResolutionSelectorSerializer::class)
val previewResolutionSelector: ResolutionSelector = defaultPreviewResolutionSelector(),

@Serializable(with = ResolutionSelectorSerializer::class)
val analysisResolutionSelector: ResolutionSelector = defaultAnalysisResolutionSelector(),

@Serializable(with = QualitySelectorSerializer::class)
val videoQualitySelector: QualitySelector = defaultVideoQualitySelector(),
Parameter Description
cameraSelector Specifies which physical camera to use: front (DEFAULT_FRONT_CAMERA) or rear (DEFAULT_BACK_CAMERA).
previewResolutionSelector Sets the resolution of the video stream displayed on the device screen (UI preview).
analysisResolutionSelector Defines the resolution of frames sent to the LUNA ID detector for facial analysis (for example, liveness, attribute estimation).
videoQualitySelector Determines the quality of the recorded video output. Possible values: SD, HD, FHD, UHD.

Default configuration#

LUNA ID applies the following defaults for camera operation:

Parameter Default value
Video quality SD (~640x480 pixels)
Analysis resolution 640×480 pixels
Review resolution 640×480 pixels
Default camera Front-facing
const val DEFAULT_ANALYSIS_FRAME_WIDTH = 640
const val DEFAULT_ANALYSIS_FRAME_HEIGHT = 480
const val DEFAULT_PREVIEW_FRAME_WIDTH = 640
const val DEFAULT_PREVIEW_FRAME_HEIGHT = 480

val DEFAULT_ANALYSIS_ASPECT_RATIO_STRATEGY = AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY
val DEFAULT_PREVIEW_ASPECT_RATIO_STRATEGY = AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY
val DEFAULT_VIDEO_QUALITY: Quality = Quality.SD

These values are used in the corresponding functions:

private fun defaultAnalysisResolutionSelector(): ResolutionSelector =
    ResolutionSelector.Builder()
        .setResolutionStrategy(
            ResolutionStrategy(
                Size(DEFAULT_ANALYSIS_FRAME_WIDTH, DEFAULT_ANALYSIS_FRAME_HEIGHT),
                ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER
            )
        )
        .setAspectRatioStrategy(DEFAULT_ANALYSIS_ASPECT_RATIO_STRATEGY)
        .build()

private fun defaultPreviewResolutionSelector(): ResolutionSelector =
    ResolutionSelector.Builder()
        .setResolutionStrategy(
            ResolutionStrategy(
                Size(DEFAULT_PREVIEW_FRAME_WIDTH, DEFAULT_PREVIEW_FRAME_HEIGHT),
                ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER
            )
        )
        .setAspectRatioStrategy(ShowCameraParams.DEFAULT_PREVIEW_ASPECT_RATIO_STRATEGY)
        .build()

private fun defaultVideoQualitySelector() =
    QualitySelector.from(ShowCameraParams.DEFAULT_VIDEO_QUALITY)

private fun defaultCameraSelector(): CameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA

Note: The FALLBACK_RULE_CLOSEST_HIGHER strategy ensures that if the requested resolution is not supported by the device, the system selects the closest higher available resolution.

Pre-initializing camera availability#

On certain devices, particularly embedded systems like POS terminals, it may be necessary to pre-initialize the camera provider to ensure timely access. You can proactively load the list of available cameras, for example, within the MainActivity scope:

CoroutineScope(Dispatchers.IO).launch {
    (this@MainActivity.application as App)
        .availableCameraTypes
        .update { getAvailableCameraTypes(this@MainActivity) }
}

Getting available camera types#

Use this function to retrieve available camera types:

@SuppressLint("RestrictedApi1")
@ExperimentalCamera2Interop
suspend fun getAvailableCameraTypes(context: Context): List<Int> = withContext(Dispatchers.IO) {
    val provider = ProcessCameraProvider.getInstance(context).get()
    provider
        .availableCameraInfos
        .mapNotNull { info ->
            val characteristics = Camera2CameraInfo.from(info).cameraCharacteristicsMap
            Log.i("FacePayViewModel", "getAvailableCameraTypes: $characteristics")

            val lensFacing = characteristics.values.firstOrNull()?.get(CameraCharacteristics.LENS_FACING)
            when (lensFacing) {
                CameraCharacteristics.LENS_FACING_BACK -> 1
                CameraCharacteristics.LENS_FACING_FRONT -> 0
                else -> null
            }
        }
        .distinct()
}

This populates an observable state (availableCameraTypes) with the supported camera directions (front or back).

Launching the camera with dynamic selection#

Once camera availability is known, you can launch LunaID.showCamera() using the detected camera type:

val cameras = (context.applicationContext as App).availableCameraTypes

cameras.filterNotNull().first { availableCameras ->
    val cameraSelector = getSelectorFor(availableCameras.first())

    val showCameraParams = settings.showCameraParams.copy(
        borderDistanceStrategy = BorderDistancesStrategy.WithCustomView(R.id.faceCaptureOverlay),
        cameraSelector = cameraSelector,
        checkSecurity = false,
    )

    LunaID.showCamera(
        context = context,
        params = showCameraParams,
        interactions = Interactions.Builder().build(),
        commands = Commands.Builder().build()
    )
    true
}

fun getSelectorFor(type: Int): CameraSelector =
    CameraSelector.Builder()
        .requireLensFacing(type)
        .build()