一、Camera与AI:从“拍照”到“看懂世界”
各位同学,今天我们来聊聊Camera和AI的结合。说实话,这个话题我期待很久了。
你想想看,传统的相机系统,说白了就是个“光学+传感器+ISP”的流水线。它能把光信号变成数字图像,但图像里有什么?它不知道。一个人站在镜头前,它只知道像素值,不知道那是个人。
但AI加入后,情况完全变了。相机开始“看懂”画面了。它能认出人脸、检测物体、识别场景,甚至能帮你把照片修得更好看。这不是魔法,这是Camera2 + MLKit的功劳。
我在项目中遇到过不少团队,把AI和相机当成两个独立模块来开发。结果呢?延迟高、功耗大、体验差。其实,AI和相机应该是一体的——从Pipeline层面就融合在一起。
1.1 为什么是Camera2 + MLKit?
Android上做相机AI,有两个主流选择:CameraX和Camera2。我个人习惯用Camera2,为什么?
- 更底层的控制:Camera2让你直接操作CaptureRequest、CaptureSession,能精确控制每一帧的流向
- 更低的延迟:跳过CameraX的抽象层,直接把ImageReader的YUV_420_888数据喂给MLKit
- 更灵活的Pipeline:你可以决定“先做检测再做美颜”,还是“先美颜再检测”
MLKit这边,Google已经封装好了各种模型:物体检测、人脸识别、文字识别、场景分类……你不需要自己训练模型,直接调用API就行。但要注意——MLKit的模型是跑在设备端的,不是云端。这意味着你的App可以离线工作,而且数据不会上传,隐私安全有保障。
核心思路:Camera2负责“取帧”,MLKit负责“理解帧”,两者通过ImageReader的OnImageAvailableListener桥接。
1.2 实时物体检测:让相机“看见”物体
实时物体检测,是AI相机最基础也最常用的功能。它的流程其实不复杂:
- Camera2每帧回调,拿到YUV_420_888格式的Image
- 把Image转换成MLKit的InputImage
- 调用MLKit的ObjectDetection API,得到检测结果
- 把结果(BoundingBox、标签、置信度)绘制到Preview上
嗯,这里要注意一个坑。我曾经在项目里直接在主线程调用MLKit的detect方法,结果卡得不行。后来才发现,MLKit的detect是同步的,必须在后台线程执行。正确的做法是:
// 错误示范:主线程调用
val result = detector.process(image).await() // 卡死!
// 正确做法:在后台线程处理
imageReader.setOnImageAvailableListener({ reader ->
val image = reader.acquireLatestImage() ?: return
executor.execute {
val inputImage = InputImage.fromMediaImage(image, rotation)
detector.process(inputImage)
.addOnSuccessListener { results ->
// 在主线程更新UI
runOnUiThread { drawResults(results) }
}
.addOnFailureListener { e -> Log.e(TAG, "检测失败", e) }
.addOnCompleteListener { image.close() }
}
}, backgroundHandler)
你看,关键就两点:异步处理和及时释放Image。Image不释放,Camera2的Buffer池会耗尽,画面就会卡住。我见过不少新手踩这个坑。
1.3 人脸识别:不只是“框出人脸”
人脸识别比物体检测复杂一些。MLKit的人脸检测API不仅能检测人脸位置,还能提取人脸轮廓、眼睛位置、嘴巴状态,甚至能判断是否微笑、是否闭眼。
我在做一个考勤App时,需要检测用户是否“正视摄像头”。一开始我直接用FaceDetector的getHeadEulerAngleZ()来判断头部偏转角度,但发现角度阈值不好设——有的人天生歪头。后来我改用“双眼位置+鼻尖位置”的相对关系来判断,准确率提升了不少。
这里给个参考配置:
val options = FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST) // 实时场景用FAST
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL) // 提取所有特征点
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL) // 开启表情分类
.setMinFaceSize(0.15f) // 最小人脸占比,太小了检测不准
.build()
val detector = FaceDetection.getClient(options)
小技巧:实时人脸识别场景,建议把minFaceSize设到0.15~0.2之间。太小了容易误检,太大了远处的人脸检测不到。这个值我是在真机上反复调出来的。
1.4 场景识别:相机自动“看懂”你在拍什么
场景识别,说白了就是让相机知道你现在在拍什么——是风景、人像、食物,还是夜景?知道了场景,相机就能自动调整参数:拍风景时提高锐度,拍食物时增强饱和度,拍夜景时拉高ISO。
MLKit没有直接提供场景识别API,但我们可以用Image Labeling来实现。它会给图像打标签,比如“山”、“海滩”、“食物”、“室内”等。根据标签的置信度,我们可以推断场景类型。
举个例子:
val labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS)
labeler.process(image)
.addOnSuccessListener { labels ->
for (label in labels) {
when (label.text) {
"Food" -> applyFoodMode() // 食物模式:暖色调、高饱和度
"Landscape" -> applyLandscapeMode() // 风景模式:高锐度、广角校正
"Night" -> applyNightMode() // 夜景模式:多帧合成、降噪
"Portrait" -> applyPortraitMode() // 人像模式:背景虚化、肤色优化
}
}
}
不过说实话,场景识别在工程上有个难点:场景切换的平滑性。你想想看,用户从拍风景切换到拍食物,如果相机参数突然大变,画面会闪一下,体验很差。我当时的做法是:用滑动窗口做场景判定,连续3帧都是“食物”才切换模式,切换时参数渐变,而不是突变。
1.5 AI美颜:从“磨皮”到“微整形”
AI美颜是用户感知最强的功能。传统的磨皮算法,就是高斯模糊+边缘保留,效果生硬。AI美颜不一样,它能识别出人脸的关键区域,然后针对性地处理:
- 皮肤区域:只磨皮,不模糊眼睛、眉毛、嘴唇
- 眼睛区域:增强瞳孔对比度,让眼睛更有神
- 嘴唇区域:提升饱和度,让唇色更自然
- 脸型轮廓:通过人脸关键点做液化,瘦脸、大眼
MLKit本身不提供美颜功能,但我们可以结合OpenCV或自研的Shader来实现。我常用的方案是:
- 用MLKit的人脸检测拿到106个关键点
- 根据关键点生成皮肤Mask(排除眼睛、眉毛、嘴唇)
- 在GPU上做双边滤波或引导滤波,只处理Mask区域
- 用关键点做三角剖分,实现瘦脸/大眼等液化效果
注意:美颜效果一定要可调节!我见过有些App把美颜开到最大,结果人脸像塑料一样。用户需要的是“自然美”,不是“假面”。建议提供0-100的滑条,让用户自己控制强度。
1.6 整体架构:一张图看懂
说了这么多,我们来画一张架构图,把Camera2 + MLKit的完整Pipeline展示出来:
这张图把整个流程串起来了。你看,从Camera2取帧,到ImageReader拿到YUV数据,再到MLKit做各种检测,最后输出结果并更新UI——每一步都有明确的职责。
1.7 避坑指南:我踩过的那些坑
最后,分享几个我在实际项目中踩过的坑,希望能帮你少走弯路:
- 坑一:Image不释放导致Camera卡死。我曾经调试了一整天,发现Camera预览突然停了,最后定位到是ImageReader的Image没有close()。记住:
acquireLatestImage()拿到的Image,用完必须close(),否则Camera2的Buffer池会耗尽。 - 坑二:MLKit检测结果和预览画面方向不一致。这是因为Camera2的Sensor方向和屏幕方向可能不同。解决办法:在创建InputImage时传入正确的rotation值,用
InputImage.fromMediaImage(image, rotation)。 - 坑三:美颜效果在低端机上卡顿。AI美颜涉及人脸关键点检测+图像处理,计算量不小。我的建议是:在低端机上降低检测帧率(比如每3帧检测一次),或者降低美颜算法的精度(用快速双边滤波代替引导滤波)。
我的习惯:在开发阶段,我会在界面上显示当前帧率和MLKit处理耗时。这样能直观地看到性能瓶颈在哪里。推荐用Choreographer来统计帧率,用System.nanoTime()来统计MLKit耗时。
好了,这一章的内容就到这里。Camera2 + MLKit的组合,说白了就是让相机从“记录画面”升级到“理解画面”。下一章我们会深入Camera2的底层原理,看看它到底是怎么工作的。
公众号:蓝海资料掘金营,微信deep3321