案例研究

SmileID 如何借助 CameraX 将身份验证功能扩展到 18,000 多种 Android 设备类型

阅读用时:4 分钟
查看 Donovan McMurray 的个人资料
Donovan McMurray 开发者关系工程师

用于银行和 SIM 卡注册的身份验证应用需要清晰、高分辨率的人脸图像,以便检测欺骗和深度伪造。对于 Android 相机开发者来说,一个常见的挑战是:如何捕获足够清晰的帧以进行活体检测和防欺骗检查,同时避免低内存设备崩溃。

对于非洲领先的身份验证 SDK SmileID 而言,多样化的硬件生态系统让这一挑战变得更加艰巨。Android 在 SmileID 的目标设备市场中占据 85% 的市场份额,其中包括处理器性能严重受限的入门级智能手机、冷门的区域型号,以及加固型定制销售终端 (POS)。

harun_quote_card_green.png

在采用 CameraX 之前,SmileID 面临着标准但艰巨的相机工程障碍:预览画面拉伸、传感器方向旋转、内存不足 (OOM) 崩溃以及自定义原始设备制造商 (OEM) 错误。通过将其核心 Android SDK 迁移到 CameraX,SmileID 将这些挑战转化为重大业务优势。

据 SmileID 称,将其 Android SDK 迁移到 CameraX 后,取得了以下成果:

指标CameraX 迁移的影响
SDK 采用量增长到第 2 年,这一比例提高到 20 倍(在使用 CameraX 之前,这一比例为 2-3 倍)
验证通过率使用 SmileID SDK 的应用数量增加了 20%
拍摄分辨率自拍照/活体检测像素提高 4 倍,且网络开销为零
设备型号兼容性支持 18,000 多种 Android 设备类型
已完成的验证总数处理了超过 1.6 亿次身份验证

挑战:硬件碎片化和旧版 API 带来的痛点

为了确认用户身份,SmileID 的 SDK 必须捕获高分辨率自拍照帧,以便进行设备端活体检测和基于云的 Deepfake 检测。

在旧版 Camera1 和低级别 Camera2 API 实现下,由于以下几个原因,很难始终如一地实现这一点:

  • 原始设备制造商(OEM) 自定义和异常:不同制造商的设备报告的物理摄像头感应方向不同,导致预览画面旋转或人脸捕获方向不正确。
  • 宽高比失真:在低端设备上请求特定帧大小通常会导致相机预览画面垂直或水平拉伸,从而导致用户面部失真,使生物识别验证失效。
  • 内存不足 (OOM) 崩溃:在 RAM 较低(通常为 1 GB 或更低)的设备上处理原始高分辨率图片,经常会导致致命的内存分配崩溃。

由于这些阻碍,SmileID 的 SDK 采用率年同比增长率仅为 2-3 倍,并且许多验证不得不退回到通过高延迟 REST API 上传静态照片,而不是在设备上实时运行活体检测。

解决方案:使用 CameraX 实现一致的流水线

SmileID 使用 CameraX 从头开始重新设计了其移动 SDK。CameraX 专为消除底层设备生态系统的复杂性而构建,可提供一致且原生感知生命周期的开发者界面。

SmileID 利用 CameraX 的核心用例(特别是 Preview 和 ImageAnalysis)构建了一个强大的双用途相机流水线:

  • 预览:呈现比例完美的实时摄像头画面预览,以引导用户。
  • ImageAnalysis:在本地实时持续处理帧,以执行自动活体检测和人脸入框检测,并提取经过验证的自拍照帧。

为了在不导致 OOM 崩溃的情况下捕获超清晰细节以进行 Deepfake 检测,SmileID 直接在其 ImageAnalysis 配置中添加了 CameraX 的现代 ResolutionSelector API。它允许开发者请求目标分辨率,并在设备不支持该确切尺寸时选择后备规则。例如,下图和示例配置说明了 ResolutionSelector 如何围绕 1200x900 目标评估候选尺寸:

resolution_selector_diagram_vertical.png

以下是相应的示例代码,展示了如何在您自己的应用中将 ResolutionSelector 与 STRATEGY_KEEP_ONLY_LATEST 配对(使用 FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER 优先考虑清晰度,或使用 FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER 限制内存用量):

import android.util.Size
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.core.resolutionselector.AspectRatioStrategy
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.core.resolutionselector.ResolutionStrategy
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.lifecycle.LifecycleOwner

fun configureCameraX(cameraProvider: ProcessCameraProvider, lifecycleOwner: LifecycleOwner, 
                     surfaceProvider: Preview.SurfaceProvider, 
                     executor: java.util.concurrent.Executor, analyzer: ImageAnalysis.Analyzer) {
    // 1. Define a resolution strategy requesting high quality (for example, 1200x900px) 
    // and selecting the closest higher (or, if unavailable, lower) resolution.
    val resolutionSelector = ResolutionSelector.Builder()
        .setResolutionStrategy(
            ResolutionStrategy(
                Size(1200, 900),
                ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
            )
        )
        .setAspectRatioStrategy(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
        .build()

    // 2. Configure the ImageAnalysis use case with the resolution selector
    // and a backpressure strategy that keeps the UI responsive on slow processors.
    val imageAnalysis = ImageAnalysis.Builder()
        .setResolutionSelector(resolutionSelector)        
        // Drops older frames if the device's CPU is still busy analyzing the previous one
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .build()

    // 3. Set up the preview stream and attach the Analyzer and SurfaceProvider
    imageAnalysis.setAnalyzer(executor, analyzer)
    val preview = Preview.Builder().build().also { it.surfaceProvider = surfaceProvider }

    // 4. Bind both use cases concurrently to the lifecycle provider
    try {
        cameraProvider.unbindAll()
        cameraProvider.bindToLifecycle(
            lifecycleOwner,
            CameraSelector.DEFAULT_FRONT_CAMERA,
            preview,
            imageAnalysis
        )
    } catch (exc: Exception) {
        // Handle binding failure safely
    }
}

通过请求高密度像素帧并在本地进行处理,SmileID 将自拍照分辨率提高了 4 倍。SmileID 还在设备上压缩了剪裁后的人脸区域,因此网络上传大小与之前的系统保持一致。这种架构可确保为机器学习模型提供更清晰的数据,同时不会增加用户的移动数据网络费用。

CameraX 如何解决硬件差异

采用 CameraX 作为标准意味着 SmileID 可以依靠两层兼容性工程来确保其 SDK 在各种设备上稳定运行:

1. Google 的相机和媒体测试实验室

CameraX 的兼容性通过持续的实体硬件验证来保障。在 Google 专用的相机和媒体测试实验室中,自动化测试套件每天都会在由 190 多种实体 Android 设备型号组成的庞大轮换矩阵中运行。这包括代表不同芯片组、操作系统版本和硬件功能的设备。

为了在热门设备上实现更一致的行为,Google 的相机和媒体测试实验室会自动捕获库回归,以免影响您的生产代码。

2. 针对设备特性的自动解决方法

不同的 Android 设备具有不同的物理限制。有些设备可能无法设置自定义曝光设置,而有些设备在使用特定宽高比时可能会报告反转的图像数据。为了防止开发者根据设备制造商和型号使用复杂的条件块污染其代码库,CameraX 在 camera-camera2 (DeviceQuirksLoader.kt) 中维护内部设备 quirk 类,例如 DeviceQuirks 和 CameraQuirks。当 CameraX 检测到存在已知问题的设备时,会自动注入量身定制的解决方法。对于 SmileID 而言,这意味着他们的 SDK 可以在 18,000 多种 Android 设备类型上可靠运行,而无需在代码库中添加针对特定设备的补丁。

验证次数增加到 1.6 亿次以上

通过迁移到 CameraX,SmileID 为外部应用开发者简化了 SDK 集成。

通过消除宽高比失真、预览卡顿和突发 OOM 崩溃,使用其 SDK 的服务的验证通过率提高了 20%。对于非洲各地的金融机构和移动货币应用而言,通过率提高 20% 意味着数十万合法用户成功入驻,而不是流失。

juma_quote_card_blue.png

开发者也注意到了这一点。由于基于 CameraX 的 SDK 易于集成,第一年的采用率激增了 15 倍,第二年更是攀升至 20 倍。迄今为止,SmileID 已使用 CameraX 成功处理了超过 1.6 亿次身份验证。

利用 CameraX 技能打造兼容性故事

开发相机应用并不一定很难。为了帮助您的团队采用 SmileID 所使用的 CameraX 模式,Android 推出了适用于 AI 编码智能体的 CameraX 技能。

当您在开发环境中使用 CameraX Skill 时,Gemini 等 AI 编码智能体会应用当前的 CameraX 模式进行迁移、生命周期绑定、ConcurrentCamera 和 MlKitAnalyzer。无论是设置双摄像头画面、使用 PreviewView 或 CameraXViewfinder 配置预览,还是管理实时机器学习 (ML) 分析,CameraX 技能都能引导智能体生成可用于生产用途的精确代码。

  • 探索 CameraX Skill:访问 Android 开发者技能目录,或使用 Android CLI 运行 android skills add camerax,将该技能添加到您的 AI 编码工具中。
  • CameraX 使用入门:参阅官方 CameraX 文档,让您的相机体验在任何设备上都能顺畅运行。
作者:
继续阅读