多Camera架构:从单眼到多眼的进化
各位同学,今天我们来聊聊多Camera架构。说实话,这个主题在Android系统里算是比较复杂的部分。我当年刚接触时也花了不少时间才理清楚。
先问大家一个问题:为什么手机需要多个摄像头?
答案其实很简单——单个摄像头有物理极限。你想啊,广角和长焦不可能同时做到极致,大光圈和薄机身本身就是矛盾。所以厂商们开始堆摄像头,从双摄到三摄、四摄,甚至五摄。
但问题来了:这么多摄像头,系统怎么管理?应用怎么调用?这就是我们今天要讲的核心。
核心概念:多Camera架构的核心在于——对上层应用呈现统一的接口,对底层硬件进行灵活的管理。
多Camera拓扑:硬件怎么连的
先看硬件层面。多Camera拓扑,说白了就是摄像头和SoC之间的连接方式。
我见过几种常见的拓扑:
- 独立CSI通道:每个摄像头独占一条MIPI CSI总线。性能最好,但占用引脚多。
- 共享CSI通道:多个摄像头通过切换开关共用一条CSI。省引脚,但同一时间只能用一个。
- 混合拓扑:主摄独占,副摄共享。这是目前主流方案。
嗯,这里要注意一点:拓扑选择直接影响功耗和性能。我在项目中遇到过,某厂商为了省成本用了全共享方案,结果双摄虚化时延迟特别大,因为两个摄像头切换太频繁了。
下面这张图展示了典型的双摄拓扑结构:
逻辑Camera与物理Camera:抽象的力量
好,硬件拓扑讲完了。现在说说软件层面怎么管理这些摄像头。
Android引入了两个重要概念:逻辑Camera和物理Camera。
物理Camera很好理解——就是真实的摄像头硬件。每个物理Camera有独立的ID、传感器、镜头。
逻辑Camera呢?它是一个虚拟的概念。你可以把它理解成一个"摄像头组"。比如一个逻辑Camera可能包含广角+长焦两个物理Camera。
为什么要这么设计?
说白了,就是为了让上层应用不用关心底层有几个摄像头。应用只需要打开一个逻辑Camera,系统自动帮你调度物理Camera。
我的经验:在配置逻辑Camera时,一定要仔细定义好物理Camera的切换策略。我曾经见过一个案例,逻辑Camera在切换物理Camera时没有做平滑过渡,结果预览画面出现明显的跳变,用户体验很差。
逻辑Camera和物理Camera的关系,可以用下面这张图表示:
MultiCamera API:怎么用代码控制
Android从API Level 24开始支持多Camera API。核心接口是CameraManager和CameraDevice。
我给大家看一段实际代码,展示如何获取逻辑Camera的物理Camera列表:
// 获取CameraManager
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
// 获取逻辑Camera ID
String[] cameraIds = manager.getCameraIdList();
for (String id : cameraIds) {
CameraCharacteristics chars = manager.getCameraCharacteristics(id);
// 检查是否是逻辑Camera
Boolean isLogical = chars.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
if (isLogical != null && isLogical) {
// 获取物理Camera ID列表
Set<String> physicalIds = chars.get(
CameraCharacteristics.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
Log.d("MultiCamera", "逻辑Camera: " + id +
" 包含物理Camera: " + physicalIds);
}
}
这段代码我实际用过。当时调试一个三摄手机,发现逻辑Camera返回的物理ID列表顺序和预期不一样,后来查文档才知道——这个顺序是厂商自定义的,不能依赖。
注意:逻辑Camera的物理ID列表顺序在不同厂商设备上可能不同。不要硬编码索引,应该通过CameraCharacteristics的物理大小或焦距来判断。
Stereo/Depth实现:双摄怎么算深度
最后聊聊立体视觉和深度计算。这是多Camera最有意思的应用之一。
深度计算的基本原理是三角测量。两个摄像头从不同角度拍摄同一场景,通过计算对应点的视差,就能推算出深度信息。
具体流程是这样的:
- 同步采集:两个摄像头同时拍摄,保证时间同步
- 立体校正:将两幅图像对齐到同一平面
- 立体匹配:找到左右图像中的对应点
- 视差计算:计算对应点的像素偏移
- 深度映射:根据视差和相机参数计算实际深度
这里有个关键参数——基线距离。两个摄像头之间的距离。基线越长,深度精度越高,但盲区也越大。
| 基线距离 | 深度精度 | 适用场景 |
|---|---|---|
| 10-15mm | 中等 | 人像虚化 |
| 20-30mm | 较高 | AR/VR |
| >30mm | 高 | 专业3D扫描 |
在Android中,深度数据通过DepthCamera接口获取。不过说实话,这个接口在Android 10之后才比较成熟。早期版本厂商都是自己搞私有API。
避坑指南:我曾经在调试双摄深度时遇到一个问题——两个摄像头的自动白平衡不一致,导致立体匹配失败。后来我强制两个摄像头使用相同的白平衡参数,问题才解决。所以多Camera同步不仅仅是时间同步,参数同步也很重要。
深度计算的精度还受很多因素影响:纹理丰富度、光照条件、物体距离等。纯色墙面基本算不出深度,因为找不到匹配点。这也是为什么有些手机在暗光下虚化效果很差的原因。
好了,多Camera架构的核心内容就这些。从硬件拓扑到软件抽象,从API调用到深度实现,每个环节都有不少坑。希望今天的分享能帮大家少走弯路。
个人建议:如果你刚开始接触多Camera开发,建议先从逻辑Camera的配置入手,理解清楚物理Camera的映射关系。深度计算可以后面再深入,因为涉及算法层面的东西比较多。
公众号:蓝海资料掘金营,微信deep3321