在专业影像类应用的开发中,测距仪、AR引擎、专业相机等场景往往需要的不只是前后置摄像头和分辨率信息。机器视觉与三维重建算法依赖高精度的物理光学参数:镜头实际焦距是多少、广角畸变如何校正、传感器物理尺寸是多大。HarmonyOS 6.1.1(API 24)之前的版本中,这些微观硬件参数通常隐藏在设备底层的硬件抽象层(HAL),应用层难以触及。Camera Kit 在本次更新中对 CameraDevice 接口做了大幅扩充,开放了十余项光学参数与传感器物理信息,并原生支持逻辑摄像头(Logical Camera)的管理与解构。开发者现在可以清楚地知道,一个看起来是广角的镜头,底层实际上是由几颗独立物理镜头协同工作组成的。
一、核心 API 变化
这次更新集中在 camera.CameraDevice 数据模型上。无论通过 cameraManager.getSupportedCameras() 获取设备列表,还是从 Session 中提取设备信息,只要运行环境在 API 24 及以上,这些新增字段都会挂载到 CameraDevice 上。需要特别注意的是,所有新增字段均为可选字段,老机型或底层驱动未上报时可能返回 undefined。
二、光学参数与传感器参数
光学参数方面,CameraDevice 新增了以下字段。
lensEquivalentFocalLength(Array):等效焦距,对应 35mm 画幅的换算值,用于摄影视角的粗略估算。
lensFocalLength(number):镜头实际物理焦距。
minimumFocusDistance(number):最小对焦距离,是微距摄影或测距阈值判断的核心依据。
lensDistortion(Array):镜头畸变参数数组,通常包含 k1、k2、p1、p2 等系数,供 AR 视觉算法做图像畸变校正。
lensIntrinsicCalibration(Array):镜头内参标定矩阵,用于计算相机空间到像素空间的投影关系。
传感器微观参数方面,新增了三个字段。
sensorPhysicalSize(Array):传感器物理尺寸,单位为毫米,包含宽和高。
sensorPixelArraySize(Array):传感器真实像素阵列大小,通常比实际出图分辨率略大,因为其中包含边缘裁剪区域。
sensorColorFilterArrangement(enum):拜耳阵列滤光片排列方式,如 RGGB,决定了底层 RAW 数据的色彩解算方案。
三、逻辑摄像头解构
逻辑摄像头是 HarmonyOS 相机框架中一个重要概念。当手机进行无缝变焦时,系统会在超广角与主摄之间协同调度,对外呈现为一个统一的摄像头,这个摄像头就是逻辑相机。CameraDevice 通过两个字段来支持逻辑相机的解构。
isLogicalCamera(boolean):标记当前相机是否由多颗物理镜头融合而来。
constituentCameraDevices(Array<CameraDevice>):仅在 isLogicalCamera 为 true 时存在,返回组成该逻辑相机的物理摄像头列表及各自的物理参数。
四、实战演示
为了验证这些新增能力,可以创建一个深度器件参数勘测页面,通过列表形式展示相机阵列及光学数据。示例工程在原有 Camera Kit 系列 Demo 基础上,新增了 CameraDeviceDemo.ets 页面作为深度器件与逻辑相机解构的演示入口,并在 main_pages.json 中完成路由注册。
下面的示例代码展示了如何在 ArkTS 中模拟拉取一组包含逻辑相机与深度内参信息的复杂相机组合,重点演示 UI 渲染逻辑。
- 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],
- 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)
- }
- }
复制代码
这段代码的要点是:先请求 CameraManager 获取支持的相机列表,然后遍历每个相机设备,判断 isLogicalCamera 标记;如果为 true,再递归遍历 constituentCameraDevices,展示每颗物理镜头的焦距、最近对焦距离、畸变参数和传感器物理尺寸。在实际业务中,将 mock 数据替换为 cameraManager.getSupportedCameras() 的返回值即可。
五、避坑指南
在实际开发中,有三个问题需要特别注意。
第一,新增字段都是可选字段。lensFocalLength、lensDistortion 等参数在老机型上可能因为底层驱动未上报而返回 undefined。如果不对 undefined 做判断就直接调用 .length 或作为算法输入,会导致白屏崩溃。建议先判空再使用。
第二,畸变参数与内参数组的长度并不统一。虽然文档标注为 Array<number>,但不同 OEM 厂商实现的畸变模型不同,Brown-Conrady 模型和鱼眼模型返回的参数个数可能有差异,例如 5 个或 8 个。在 AR 标定算法中,务必先读取数组长度再决定使用哪种校准模型。
第三,逻辑相机的操控权限有限。开发者只能对逻辑相机整体进行流配置(如设置流分辨率、开启流),不能对 constituentCameraDevices 中解构出来的某个物理子镜头单独开启预览流。这些解构出的物理设备仅用于查询物理参数,不能作为独立的流操作对象。
六、总结
HarmonyOS 6.1.1 的 CameraDevice 物理参数透明化,打破了应用层与传感器硬件之间的信息黑盒。开发者可以借助这些畸变系数、焦距数据和像素阵列参数,更便捷地实现高级计算机视觉算法、实时 AR 跟踪渲染以及科研级的数据采集。对于鸿蒙生态下的专业影像应用而言,这是一项基础设施级别的能力升级。 |