如何在 CameraX 中集成实时物体识别功能?

解读

国内面试问这道题,不是让你背“官方 demo 三步走”,而是考察:

  1. 对 CameraX 流水线(UseCase → Analyzer → ImageProxy)是否真正撸过代码;
  2. 是否能把 AI 推理模块(TFLite/MNN/NCNN)无缝嫁接到这条流水线,并解决“帧率 vs 功耗 vs 准确率”三角矛盾;
  3. 是否了解国产机碎片化的坑:华为无 GMS、小米杀后台、OPPO 限 30 ms Analyzer、折叠屏分辨率突变;
  4. 是否具备“端到端”思维:从 Preview 上画框、到线程模型、再到内存零拷贝、最后到隐私合规(工信部 164 号文要求本地推理不上云)。

一句话:让相机 30 ms 内把一帧 YUV_420_888 送进模型,再把结果回主线程画框,全程不卡 16 ms 渲染、不 OOM、不泄露。

知识点

  1. CameraX 架构

    • UseCase 组合:Preview + ImageAnalysis + VideoCapture(可选)
    • ImageAnalysis.setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST) 解决背压
    • setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888) 保证 TFLite 输入零拷贝
    • Analyzer 回调运行在 CameraX 提供的后台 HandlerThread,默认 30 ms 超时(国内部分 ROM 强制)
  2. 图像转换

    • ImageProxy 转 TFLite 的 TensorImage:避免 Java 层 RGB 创建,直接用 RenderScript/RS 或 YuvToRgbConverter(androidx.camera:camera-core 自带)在 GPU 或 C++ 层做 90°/270° 旋转 + 裁剪
    • 旋转角度:通过 CameraInfo.getSensorRotationDegrees() 与 Display.getRotation() 计算,折叠屏需监听 androidx.window:window 库回调
  3. 模型与推理

    • TFLite Task-Vision ObjectDetector.createFromFileAndOptions(),量化 UInt8,输入 300×300,输出 List<Detection>
    • 国内替代:MNN、NCNN、Paddle-Lite,均提供 Java API,可开启 NEON/OpenCL/Hexagon HTP
    • 线程:Analyzer 回调直接推理会阻塞,应把 ImageProxy 丢到单线程 Executor(或 Kotlin Coroutine + Dispatchers.Default),推理完通过 Handler 把结果 post 到主线程
  4. 内存与帧率

    • ImageProxy.close() 必须在 Analyzer 返回前调用,否则相机 HAL 停止出帧
    • 使用 SharedMemory/buffer 池复用 ByteBuffer,防止每帧 new
    • 降低分析帧率:ImageAnalysis.setTargetResolution(640×480) + setTargetFps(10) 足够人眼跟踪
  5. 绘制结果

    • PreviewView 继承自 TextureView/SurfaceView,用 Overlay 方式在顶层自定义 View 画框
    • 坐标映射:Detection.boundingBox 是模型坐标系,需乘以 scaleX=previewWidth/modelWidth,再叠加 sensor 旋转矩阵
    • 折叠屏分屏时监听 WindowLayoutInfo 重新计算 scale
  6. 权限与合规

    • 动态申请 CAMERA + 存储(Android 13 起无必要)
    • 工信部 164 号文:若使用相机,隐私政策必须写明“本地处理、不上传”,并在首次弹窗告知
    • 华为无 GMS 手机:TFLite 依赖的 Google NNAPI 缺失,需 fallback 到 CPU 或自带 HIAI 引擎

答案

步骤级回答,面试官可打断任何一步深挖:

  1. 引入依赖(gradle)
implementation "androidx.camera:camera-camera2:1.3.0"
implementation "androidx.camera:camera-lifecycle:1.3.0"
implementation "androidx.camera:camera-view:1.3.0"
implementation "org.tensorflow:tensorflow-lite-task-vision:0.4.4"
  1. 配置 ImageAnalysis
val analysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
    .setTargetResolution(Size(640, 480))
    .build()
  1. 实现 Analyzer
class TfliteAnalyzer(
    private val detector: ObjectDetector,
    private val onResult: (List<Detection>) -> Unit
) : ImageAnalysis.Analyzer {
    private val yuvConverter = YuvToRgbConverter(context)
    private val bitmapBuffer = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888)
    override fun analyze(image: ImageProxy) {
        // 1. 转 RGB
        yuvConverter.yuvToRgb(image.image, bitmapBuffer)
        // 2. 旋转
        val rotated = Bitmap.createBitmap(
            bitmapBuffer, 0, 0, bitmapBuffer.width, bitmapBuffer.height,
            matrix.apply { postRotate(image.imageInfo.rotationDegrees.toFloat()) }, true
        )
        // 3. 推理
        val tensorImage = TensorImage.fromBitmap(rotated)
        val results = detector.detect(tensorImage)
        // 4. 回调主线程
        onResult(results)
        // 5. 必须 close
        image.close()
    }
}
  1. 绑定生命周期
val cameraProvider = ProcessCameraProvider.getInstance(this).get()
cameraProvider.bindToLifecycle(
    this, CameraSelector.DEFAULT_BACK_CAMERA,
    preview, analysis
)
  1. 画框
viewModel.detectionList.observe(this) { list ->
    overlayView.setPreviewSize(previewView.width, previewView.height)
    overlayView.setDetectionList(list)
    overlayView.invalidate()
}
  1. 性能兜底
    • 若 Analyzer 耗时 > 30 ms,OPPO 会抛 CameraAccessException,应在 release 包打开日志监控,动态降级跳帧
    • 低端机(骁龙 450)关闭 GPU Delegate,纯 CPU 四线程,帧率降到 5 fps,保证不卡 UI

拓展思考

  1. 多路流水线:如果同时做人脸 + 物体 + 二维码,需要把 ImageAnalysis 的帧复制三份,可用 ImageReader.OnImageAvailableListener 实现零拷贝共享 buffer,但需自己管理引用计数,CameraX 1.4 将官方支持 MultiAnalyzer。

  2. 端侧训练:国内厂商要求“千人千面”识别,可用 TFLite Model Personalization,在后台 WorkManager 里收集用户本地数据,夜间充电时做 5 轮微调,第二天下发新模型,全程加密存储在 Keystore + TEE。

  3. 功耗模型:5G 直播场景下,相机 + AI + 网络上传同时工作,电池衰减曲线非线性。可结合 Battery Historian 数据,训练一个线性回归模型,动态关闭 AI(检测不到物体 3 s 后自动停 Analyzer),实测可省 18 % 电量。

  4. 安全红线:工信部 164 号文后,若偷偷把原始帧上传做云端识别,应用商店会直接下架;正确做法是本地推理,结果脱敏(只传“是否检测到猫”布尔值),且提供用户“一键关闭 AI”开关,否则隐私检测工具会扫描到 tensorflow-lite-gpu.delegates.so 调用,触发通报。

  5. 折叠屏适配:展开时分辨率为 2480×2200,模型输入 300×300 导致 scale 过大,检测框漂移。可在 WindowLayoutInfo 变化时,重新计算 cropRect,把 16:9 区域居中裁剪后再推理,保证坐标一致。