头图

HarmonyOS 6.1.1 全栈实战录 - 96 探秘镜头下的微观物理世界:实战 Camera Kit 深度器件信息与逻辑相机解构

1、引言

对于专业的影像类 App(如测距仪、AR 引擎、专业相机等),仅仅知道前后置和分辨率是远远不够的。机器视觉与三维重建算法往往渴求极其精确的物理光学参数——镜头的实际焦距是多少?存在怎样的广角畸变?传感器的实际物理毫米尺寸多大?
image.png
在 HarmonyOS 6.1.1 (API 24) 之前,这些微观的硬件参数多隐藏在设备底层的硬件抽象层(HAL)中。而如今,Camera Kit 对 CameraDevice 接口进行了史诗级扩充,一口气开放了十余项极其硬核的光学参数与传感器物理信息。更关键的是,原生支持了逻辑摄像头(Logical Camera)的管理与解构,让你能清楚地知道,一个看起来是广角的镜头,底层其实是由几颗独立物理镜头“缝合”而成的!
image.png

2、效果展示与项目结构
2.1 运行效果展示

在本次 Demo 中,我们新增了“深度参数勘测舱”:

  • 微观参数扫描:一键拉取设备的相机阵列,展示其诸如等效焦距、镜头畸变矩阵、传感器像素阵列等硬核光学数据。
  • 逻辑相机解剖:当选中一个 isLogicalCamera === true 的摄像头时,UI 界面会自动展开树状结构,展示底层所有的 constituentCameraDevices(组成它的物理镜头簇)。
2.2 示例项目物理结构
ArkTSDemo
├── entry/src/main/ets/pages
│   ├── CameraKitIntro.ets     <-- 导航(新增器件参数入口)
│   ├── CameraDemo.ets         <-- 闪光灯监听演示
│   ├── CameraOISDemo.ets      <-- 光学防抖设置演示
│   └── CameraDeviceDemo.ets   <-- [新增] 深度器件与逻辑相机解构舱
└── entry/src/main/resources/base/profile
    └── main_pages.json        <-- 路由注册表
3、Kit 能力与核心 API 解析

所有的更新都集中在 camera.CameraDevice 数据模型上。不论是通过 cameraManager.getSupportedCameras() 还是从 Session 中提取,只要是 API 24 及以上的环境(包含元服务),都会挂载以下可选参数:

3.1 核心光学参数 (Optical Params)
  • lensEquivalentFocalLength (Array):等效焦距(对应 35mm 画幅),常用于摄影视角的粗略换算。
  • lensFocalLength (number):镜头实际物理焦距。
  • minimumFocusDistance (number):最小对焦距离,微距摄影或测距阈值的判断核心。
  • lensDistortion (Array):镜头畸变参数数组(通常为 k1, k2, p1, p2 等),用于 AR 视觉算法中的图像畸变校正。
  • lensIntrinsicCalibration (Array):镜头内参标定矩阵,计算相机空间到像素空间的投影关系。
3.2 传感器微观参数 (Sensor Params)
  • sensorPhysicalSize (Array):传感器物理尺寸 [宽, 高](毫米级别)。
  • sensorPixelArraySize (Array):传感器的真实像素阵列大小 [宽, 高](通常比实际出图分辨率略大,包含了边缘裁剪区域)。
  • sensorColorFilterArrangement (enum):拜耳阵列滤光片的排列方式(如 RGGB),决定了底层 RAW 数据的色彩解算方案。
3.3 逻辑摄像头解构 (Logical Camera)
  • isLogicalCamera (boolean):标志当前相机是否是由多颗物理镜头融合而来的“逻辑相机”(例如无缝变焦时后台协调的超广角+主摄)。
  • constituentCameraDevices(Array<CameraDevice>):只有当 isLogicalCamera` 为 true 时存在。返回构成此逻辑相机的物理摄像头列表及其各自的物理参数。
4、业务逻辑与数据流图

image.png

5、6.1新增特性实战

CameraDeviceDemo.ets 中,为了演示数据的呈现方式,我们在 ArkTS 侧模拟拉取了一组包含“逻辑相机”以及深度内参信息的复杂相机组合:

import { router } from '@kit.ArkUI';

interface MockConstituentCamera {
  cameraId: string;
  lensFocalLength: number;
  minimumFocusDistance: number;
  lensDistortion?: number[];
  sensorPhysicalSize: number[];
  sensorPixelArraySize: number[];
}

interface MockCameraDevice {
  cameraId: string;
  cameraType: string;
  isLogicalCamera: boolean;
  lensEquivalentFocalLength: number[];
  constituentCameraDevices: MockConstituentCamera[];
}

@Entry
@Component
struct CameraDeviceDemo {
  @State logs: string[] = [];

  private appendLog(msg: string): void {
    let now = new Date();
    let timeStr = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}.${now.getMilliseconds()}`;
    this.logs.unshift(`[${timeStr}] ${msg}`);
  }

  scanCameraDevices(): void {
    this.appendLog('🔍 正在向 CameraManager 请求 Supported Cameras...');
    
    // 真实业务中: let cameras = cameraManager.getSupportedCameras();
    // 这里我们构建一个强仿真的数据结构用于演示 UI 渲染逻辑
    let mockCameras: MockCameraDevice[] = [
      {
        cameraId: 'LOGICAL_BACK_01',
        cameraType: 'CAMERA_TYPE_WIDE_ANGLE',
        isLogicalCamera: true, // 标记为逻辑相机
        lensEquivalentFocalLength: [24.0, 70.0], // 变焦覆盖
        constituentCameraDevices: [
          {
            cameraId: 'PHYSICAL_BACK_MAIN',
            lensFocalLength: 5.4,
            minimumFocusDistance: 0.1,
            lensDistortion: [0.12, -0.05, 0.001, 0.002, 0.0], // 畸变参数
            sensorPhysicalSize: [8.0, 6.0], // 传感器物理 mm
            sensorPixelArraySize: [4000, 3000] // 像素阵列
          },
          {
            cameraId: 'PHYSICAL_BACK_TELE',
            lensFocalLength: 14.2,
            minimumFocusDistance: 0.5,
            sensorPhysicalSize: [4.0, 3.0]
          }
        ]
      }
    ];

    this.appendLog(`✅ 成功扫描到 ${mockCameras.length} 个根相机设备`);
    
    mockCameras.forEach((cam: MockCameraDevice) => {
      this.appendLog(`========== ${cam.cameraId} ==========`);
      if (cam.isLogicalCamera) {
        this.appendLog(`⚡ 发现逻辑摄像头!等效焦距段: ${cam.lensEquivalentFocalLength.join(' - ')}mm`);
        this.appendLog(`📂 开始解构底层物理构成: [共 ${cam.constituentCameraDevices.length} 颗物理镜头]`);
        
        cam.constituentCameraDevices.forEach((subCam: MockConstituentCamera, index: number) => {
          this.appendLog(`  ├─ 物理镜头 ${index + 1}: ${subCam.cameraId}`);
          this.appendLog(`  │  ├─ 实际焦距: ${subCam.lensFocalLength}mm`);
          this.appendLog(`  │  ├─ 最近对焦: ${subCam.minimumFocusDistance}m`);
          this.appendLog(`  │  ├─ 畸变参数: [${subCam.lensDistortion?.join(', ')}]`);
          this.appendLog(`  │  └─ Sensor尺寸: ${subCam.sensorPhysicalSize[0]} x ${subCam.sensorPhysicalSize[1]} mm`);
        });
      }
    });
  }

  build() {
    Column() {
      // 头部导航及操作按钮区域...
      // (详见工程源码)
    }
    .width('100%').height('100%').backgroundColor(Color.White)
  }
}

运行效果如下:
image.png

6、避坑指南
  1. 可选字段(Optional)防空指针:新增加的这些参数如 lensFocalLengthlensDistortion 等全部标记为可选字段(可选)。因为老旧机型的底层驱动并没有上传这些参数矩阵,如果未判断 undefined 直接调用 .length 或作为算法输入,将导致白屏崩溃。
  2. 畸变与内参的数组长度:虽然文档说明这是一个 Array<number>,但不同的 OEM 厂商提供的畸变模型(如 Brown-Conrady 或鱼眼)不同,返回的参数个数(比如 5个 或 8个)存在差异,务必在 AR 标定算法中先读取数组长度决定校准模型。
  3. 逻辑相机的操控权限:你只能对外部的逻辑相机进行配置流(如流分辨率、开启流),不能直接对 constituentCameraDevices 中解构出来的某个单纯的物理子镜头单独开启预览流,它们只供你查询物理参数使用!
7、总结

HarmonyOS 6.1.1 带来的 CameraDevice 物理参数透明化,彻底打破了应用层与传感器硬件之间的“信息黑盒”。借助这些珍贵的畸变、焦距及像素阵列参数,开发者可以毫不费力地介入高级计算机视觉(CV)、实时 AR 跟踪渲染,以及科研级的数据收集,为鸿蒙生态下的硬核影像应用铺平了道路。


轻口味
39.5k 声望5.9k 粉丝

移动端十年老人,主要做IM、音视频、AI方向,目前在做鸿蒙化适配,欢迎这些方向的同学交流:wodekouwei