多Camera架构:从单眼到多眼的进化

各位同学,今天我们来聊聊多Camera架构。说实话,这个主题在Android系统里算是比较复杂的部分。我当年刚接触时也花了不少时间才理清楚。

先问大家一个问题:为什么手机需要多个摄像头?

答案其实很简单——单个摄像头有物理极限。你想啊,广角和长焦不可能同时做到极致,大光圈和薄机身本身就是矛盾。所以厂商们开始堆摄像头,从双摄到三摄、四摄,甚至五摄。

但问题来了:这么多摄像头,系统怎么管理?应用怎么调用?这就是我们今天要讲的核心。

核心概念:多Camera架构的核心在于——对上层应用呈现统一的接口,对底层硬件进行灵活的管理。

多Camera拓扑:硬件怎么连的

先看硬件层面。多Camera拓扑,说白了就是摄像头和SoC之间的连接方式。

我见过几种常见的拓扑:

  • 独立CSI通道:每个摄像头独占一条MIPI CSI总线。性能最好,但占用引脚多。
  • 共享CSI通道:多个摄像头通过切换开关共用一条CSI。省引脚,但同一时间只能用一个。
  • 混合拓扑:主摄独占,副摄共享。这是目前主流方案。

嗯,这里要注意一点:拓扑选择直接影响功耗和性能。我在项目中遇到过,某厂商为了省成本用了全共享方案,结果双摄虚化时延迟特别大,因为两个摄像头切换太频繁了。

下面这张图展示了典型的双摄拓扑结构:

典型双Camera拓扑结构 主摄像头 IMX586 48MP 副摄像头 IMX350 20MP CSI-0 通道 4-lane MIPI CSI-1 通道 2-lane MIPI ISP 图像信号处理器 双通道输入 主摄占用独立CSI-0通道(4-lane),副摄使用CSI-1通道(2-lane) ISP同时接收两路数据,支持同步处理

逻辑Camera与物理Camera:抽象的力量

好,硬件拓扑讲完了。现在说说软件层面怎么管理这些摄像头。

Android引入了两个重要概念:逻辑Camera物理Camera

物理Camera很好理解——就是真实的摄像头硬件。每个物理Camera有独立的ID、传感器、镜头。

逻辑Camera呢?它是一个虚拟的概念。你可以把它理解成一个"摄像头组"。比如一个逻辑Camera可能包含广角+长焦两个物理Camera。

为什么要这么设计?

说白了,就是为了让上层应用不用关心底层有几个摄像头。应用只需要打开一个逻辑Camera,系统自动帮你调度物理Camera。

我的经验:在配置逻辑Camera时,一定要仔细定义好物理Camera的切换策略。我曾经见过一个案例,逻辑Camera在切换物理Camera时没有做平滑过渡,结果预览画面出现明显的跳变,用户体验很差。

逻辑Camera和物理Camera的关系,可以用下面这张图表示:

逻辑Camera与物理Camera映射关系 Camera App 逻辑Camera (ID: 0) 包含: 主摄 + 广角 + 长焦 物理Camera 0 主摄 48MP 物理Camera 1 广角 12MP 物理Camera 2 长焦 8MP

MultiCamera API:怎么用代码控制

Android从API Level 24开始支持多Camera API。核心接口是CameraManagerCameraDevice

我给大家看一段实际代码,展示如何获取逻辑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最有意思的应用之一。

深度计算的基本原理是三角测量。两个摄像头从不同角度拍摄同一场景,通过计算对应点的视差,就能推算出深度信息。

具体流程是这样的:

  1. 同步采集:两个摄像头同时拍摄,保证时间同步
  2. 立体校正:将两幅图像对齐到同一平面
  3. 立体匹配:找到左右图像中的对应点
  4. 视差计算:计算对应点的像素偏移
  5. 深度映射:根据视差和相机参数计算实际深度

这里有个关键参数——基线距离。两个摄像头之间的距离。基线越长,深度精度越高,但盲区也越大。

基线距离 深度精度 适用场景
10-15mm 中等 人像虚化
20-30mm 较高 AR/VR
>30mm 专业3D扫描

在Android中,深度数据通过DepthCamera接口获取。不过说实话,这个接口在Android 10之后才比较成熟。早期版本厂商都是自己搞私有API。

避坑指南:我曾经在调试双摄深度时遇到一个问题——两个摄像头的自动白平衡不一致,导致立体匹配失败。后来我强制两个摄像头使用相同的白平衡参数,问题才解决。所以多Camera同步不仅仅是时间同步,参数同步也很重要。

深度计算的精度还受很多因素影响:纹理丰富度、光照条件、物体距离等。纯色墙面基本算不出深度,因为找不到匹配点。这也是为什么有些手机在暗光下虚化效果很差的原因。

好了,多Camera架构的核心内容就这些。从硬件拓扑到软件抽象,从API调用到深度实现,每个环节都有不少坑。希望今天的分享能帮大家少走弯路。

个人建议:如果你刚开始接触多Camera开发,建议先从逻辑Camera的配置入手,理解清楚物理Camera的映射关系。深度计算可以后面再深入,因为涉及算法层面的东西比较多。


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