第1章:CameraX图像分析——从像素中读懂世界

图像分析,说白了就是让摄像头“看懂”每一帧画面。

我个人觉得,这是CameraX最实用的功能之一。你想想看,扫码、人脸检测、物体识别、实时滤镜……这些功能背后,都离不开图像分析。今天我们就来聊聊,怎么用CameraX的ImageAnalysis,高效地处理每一帧图像数据。

1.1 ImageAnalysis 是什么?

ImageAnalysis是CameraX提供的一个用例(UseCase)。它的任务很简单:把摄像头采集到的每一帧图像,交给你的分析器去处理

你可以把它理解成一个“图像流水线”。摄像头不断生产图像帧,ImageAnalysis负责把帧送到你的手中。你只需要实现一个Analyzer接口,就能拿到每一帧的数据。

核心要点:

  • ImageAnalysis是异步的,不会阻塞相机预览
  • 你可以控制帧率,避免处理不过来
  • 默认输出格式是YUV_420_888,这是Android上最通用的格式

1.2 YUV_420_888 格式——绕不开的坎

嗯,这里要注意。YUV_420_888这个格式,很多新手第一次看到会懵。我刚开始做摄像头开发时,也被它折磨过。

简单说,YUV是一种颜色编码方式。它把亮度(Y)和颜色信息(U、V)分开存储。而420表示:每4个像素共享一组UV数据。888表示每个分量用8位存储。

为什么Android要用这个格式?因为省带宽。摄像头传感器原生输出的就是YUV数据,直接传给CPU或GPU处理,比转成RGB快得多。

我的经验:

如果你要做图像处理,尽量在YUV空间里操作。实在需要RGB,再转。我曾经在项目里每帧都转RGB,结果帧率直接掉到15fps……后来改成YUV处理,轻松跑满30fps。

1.3 自定义Analyzer——写一个自己的分析器

好了,理论说完了,我们直接上代码。

// 1. 创建ImageAnalysis用例
val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))  // 设置分析分辨率
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) // 只保留最新帧
    .build()

// 2. 实现自定义Analyzer
class MyAnalyzer : ImageAnalysis.Analyzer {
    
    private var lastAnalyzedTime = 0L
    
    override fun analyze(imageProxy: ImageProxy) {
        val currentTime = System.currentTimeMillis()
        
        // 控制帧率:每秒只处理10帧
        if (currentTime - lastAnalyzedTime < 100) {
            imageProxy.close()
            return
        }
        lastAnalyzedTime = currentTime
        
        // 获取YUV_420_888格式的Image
        val image = imageProxy.image
        if (image == null) {
            imageProxy.close()
            return
        }
        
        // 处理YUV数据
        val yBuffer = image.planes[0].buffer  // Y平面
        val uBuffer = image.planes[1].buffer  // U平面
        val vBuffer = image.planes[2].buffer  // V平面
        
        val ySize = yBuffer.remaining()
        val uSize = uBuffer.remaining()
        val vSize = vBuffer.remaining()
        
        // 这里可以放你的图像处理逻辑
        // 比如:人脸检测、二维码识别、颜色分析……
        
        // 重要:处理完后必须关闭
        imageProxy.close()
    }
}

// 3. 绑定到生命周期
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    cameraSelector,
    preview,
    imageAnalysis
)

警告:

千万不要忘记调用 imageProxy.close()!否则CameraX会认为你还在处理,不再给你新的帧。我曾经因为这个bug,排查了整整一个下午……

1.4 YUV数据怎么用?——实战技巧

拿到YUV数据后,最常见的操作就是转成Bitmap或者直接做像素级处理。我分享几个实用技巧:

操作 方法 性能
YUV转Bitmap 使用RenderScript或YuvImage 中等
YUV转NV21 手动重组U、V平面
直接分析Y平面 只取planes[0] 最快

我个人习惯:如果只是做亮度分析或边缘检测,只取Y平面就够了。这样省去了处理UV的麻烦,速度也快。

1.5 避坑指南——我踩过的那些坑

  • 帧率控制:我曾经在低端机上跑全帧率分析,结果手机烫得能煎鸡蛋。后来加了帧率限制,问题解决。
  • 内存泄漏:ImageProxy如果不及时close,会导致内存暴涨。记得用try-finally保证关闭。
  • 旋转问题:摄像头采集的图像默认是传感器方向的。如果你需要正立的图像,记得用 imageProxy.imageInfo.rotationDegrees 做旋转。

一句话总结:

ImageAnalysis + YUV_420_888 = 高效图像处理。掌握好帧率控制和内存管理,你就能写出流畅的实时分析应用。

CameraX 图像分析核心流程 摄像头传感器 ImageAnalysis 用例 YUV_420_888 格式 Y平面分析(最快) YUV转Bitmap 自定义Analyzer

这张图展示了整个流程:摄像头采集 → ImageAnalysis分发 → YUV_420_888格式 → 三种处理路径。我个人推荐新手先从Y平面分析入手,等熟悉了再尝试更复杂的转换。

公众号:蓝海资料掘金营,微信deep3321