第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 = 高效图像处理。掌握好帧率控制和内存管理,你就能写出流畅的实时分析应用。
这张图展示了整个流程:摄像头采集 → ImageAnalysis分发 → YUV_420_888格式 → 三种处理路径。我个人推荐新手先从Y平面分析入手,等熟悉了再尝试更复杂的转换。