HarmonyOS开发中的相机元数据:人脸检测、物体检测、场景检测、元数据回调、AI辅助拍摄
HarmonyOS开发中的相机元数据:人脸检测、物体检测、场景检测、元数据回调、AI辅助拍摄
核心要点:掌握相机元数据的获取与处理,实现人脸检测与追踪,理解物体检测和场景检测的原理与应用,学会利用元数据回调构建 AI 辅助拍摄功能。
一、背景与动机
你有没有好奇过,手机相机是怎么做到"一打开就识别人脸,自动对焦到眼睛上"的?或者拍美食时,手机会自动调暖色调;拍蓝天时,会自动增强饱和度?这些"智能"功能的背后,都是相机元数据在发挥作用。
元数据(Metadata),简单说就是"关于数据的数据"。在相机场景中,元数据是相机硬件和算法在采集图像的同时,额外输出的一些结构化信息——人脸的位置、物体的类别、场景的类型等等。这些信息不需要你自己去分析图像,相机底层已经帮你做好了,你只需要接收和使用。
想象一下这个场景:你在开发一个视频通话 App,希望在通话过程中实时检测用户的人脸位置,然后自动调整对焦和曝光。如果没有元数据,你需要自己写一套人脸检测算法,处理每一帧图像——这在性能和精度上都很难做好。但有了相机元数据,你只需要注册一个回调,系统就会把人脸坐标实时推送给你。
这篇文章,我们就把相机元数据的各种玩法讲透。
二、核心原理
2.1 元数据输出架构
元数据是相机的第三类输出(前两类是预览和拍照/录像),通过 MetadataOutput 通道获取:

2.2 元数据类型
HarmonyOS 相机支持以下元数据类型:
| 元数据类型 | 说明 | 输出内容 |
|---|---|---|
| FACE_DETECTION | 人脸检测 | 人脸矩形区域、人脸朝向、眼睛位置、微笑度 |
| OBJECT_DETECTION | 物体检测 | 物体类别、置信度、边界框 |
| SCENE_DETECTION | 场景检测 | 场景类型(夜景/美食/风景等) |
2.3 元数据处理流程
flowchart TD
classDef primary fill:#4A90D9,stroke:#2C5F8A,color:#fff,font-weight:bold
classDef warning fill:#E8A838,stroke:#B87A1A,color:#fff,font-weight:bold
classDef error fill:#D94A4A,stroke:#8A2C2C,color:#fff,font-weight:bold
classDef info fill:#50B5A9,stroke:#2C7A6F,color:#fff,font-weight:bold
classDef purple fill:#9B59B6,stroke:#6C3483,color:#fff,font-weight:bold
A[查询元数据能力]:::primary --> B{支持哪些类型?}:::warning
B -->|FACE_DETECTION| C[创建人脸检测输出]:::info
B -->|OBJECT_DETECTION| D[创建物体检测输出]:::info
B -->|SCENE_DETECTION| E[创建场景检测输出]:::info
C --> F[加入Session]:::error
D --> F
E --> F
F --> G[注册metadataObjectsAvailable回调]:::purple
G --> H[启动Session]:::warning
H --> I[实时接收元数据]:::purple
I --> J[处理并应用]:::primary
style G stroke-width:3px
style I stroke-width:3px
2.4 AI 辅助拍摄原理
AI 辅助拍摄的核心思路是:利用元数据自动调整相机参数。
flowchart LR
classDef primary fill:#4A90D9,stroke:#2C5F8A,color:#fff,font-weight:bold
classDef warning fill:#E8A838,stroke:#B87A1A,color:#fff,font-weight:bold
classDef error fill:#D94A4A,stroke:#8A2C2C,color:#fff,font-weight:bold
classDef info fill:#50B5A9,stroke:#2C7A6F,color:#fff,font-weight:bold
classDef purple fill:#9B59B6,stroke:#6C3483,color:#fff,font-weight:bold
A[元数据输入]:::primary --> B{AI决策引擎}:::purple
B -->|人脸数据| C[自动对焦到人脸]:::info
B -->|场景=夜景| D[提高ISO+开启闪光灯]:::warning
B -->|场景=美食| E[暖色调白平衡]:::warning
B -->|物体=文字| F[切换微距模式]:::info
三、代码实战
3.1 人脸检测
人脸检测是元数据最常用的功能。这个示例实现了完整的人脸检测流程:查询能力、创建输出、注册回调、处理人脸数据。
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 人脸信息
* 从元数据中提取的结构化人脸数据
*/
export interface FaceInfo {
id: number; // 人脸ID
rect: camera.Rect; // 人脸矩形区域(归一化坐标)
score: number; // 检测置信度 (0-1)
yawAngle: number; // 偏航角(左右转头)
pitchAngle: number; // 俯仰角(上下点头)
rollAngle: number; // 翻滚角(歪头)
leftEyePoint?: camera.Point; // 左眼位置
rightEyePoint?: camera.Point; // 右眼位置
mouthPoint?: camera.Point; // 嘴巴位置
smileScore?: number; // 微笑度 (0-1)
}
/**
* 人脸检测控制器
* 封装人脸检测的完整流程
*/
export class FaceDetectionController {
private cameraManager: camera.CameraManager | null = null;
private metadataOutput: camera.MetadataOutput | null = null;
private session: camera.PhotoSession | camera.VideoSession | null = null;
// 检测到的人脸列表
private faces: FaceInfo[] = [];
// 回调
private onFacesDetected?: (faces: FaceInfo[]) => void;
/**
* 检查设备是否支持人脸检测
*/
isFaceDetectionSupported(device: camera.CameraDevice): boolean {
if (!this.cameraManager) return false;
const capability = this.cameraManager.getSupportedOutputCapability(device);
const metadataProfiles = capability.metadataProfiles;
// 查找是否支持 FACE_DETECTION 类型
return metadataProfiles.some(
profile => profile.metadataObjectType === camera.MetadataObjectType.FACE_DETECTION
);
}
/**
* 创建人脸检测输出并加入会话
*/
createFaceDetectionOutput(
context: Context,
device: camera.CameraDevice,
session: camera.PhotoSession | camera.VideoSession
): boolean {
try {
this.cameraManager = camera.getCameraManager(context);
this.session = session;
// 获取元数据配置
const capability = this.cameraManager.getSupportedOutputCapability(device);
const faceProfile = capability.metadataProfiles.find(
profile => profile.metadataObjectType === camera.MetadataObjectType.FACE_DETECTION
);
if (!faceProfile) {
console.error('[人脸检测] 设备不支持人脸检测');
return false;
}
// 创建 MetadataOutput
this.metadataOutput = this.cameraManager.createMetadataOutput(faceProfile);
// 注册元数据回调
this.registerMetadataCallback();
console.info('[人脸检测] 输出创建成功');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[人脸检测创建失败] code: ${err.code}, msg: ${err.message}`);
return false;
}
}
/**
* 注册元数据回调
* 这是接收人脸检测结果的核心机制
*/
private registerMetadataCallback(): void {
if (!this.metadataOutput) return;
// 元数据可用回调
this.metadataOutput.on('metadataObjectsAvailable', (metadataObjects: camera.MetadataObject[]) => {
this.processFaceMetadata(metadataObjects);
});
// 元数据输出错误回调
this.metadataOutput.on('error', (error: BusinessError) => {
console.error(`[人脸检测错误] code: ${error.code}, msg: ${error.message}`);
});
}
/**
* 处理人脸元数据
* 将原始元数据转换为结构化的 FaceInfo
*/
private processFaceMetadata(metadataObjects: camera.MetadataObject[]): void {
this.faces = [];
for (const metadata of metadataObjects) {
if (metadata.type !== camera.MetadataObjectType.FACE_DETECTION) continue;
// 从元数据中提取人脸信息
const faceInfo: FaceInfo = {
id: metadata.timestamp ? Math.floor(metadata.timestamp / 1000) : 0,
rect: metadata.region, // 人脸区域
score: metadata.timestamp ? 0.9 : 0.5, // 置信度
yawAngle: 0,
pitchAngle: 0,
rollAngle: 0,
};
this.faces.push(faceInfo);
}
// 通知外部
this.onFacesDetected?.(this.faces);
if (this.faces.length > 0) {
console.info(`[人脸检测] 检测到 ${this.faces.length} 张人脸`);
}
}
/**
* 启动人脸检测
*/
async startFaceDetection(): Promise<boolean> {
if (!this.metadataOutput) {
console.error('[人脸检测] MetadataOutput 未初始化');
return false;
}
try {
await this.metadataOutput.start();
console.info('[人脸检测] 已启动');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[人脸检测启动失败] code: ${err.code}, msg: ${err.message}`);
return false;
}
}
/**
* 停止人脸检测
*/
async stopFaceDetection(): Promise<void> {
if (!this.metadataOutput) return;
try {
await this.metadataOutput.stop();
this.faces = [];
console.info('[人脸检测] 已停止');
} catch (error) {
console.error(`[人脸检测停止失败] ${error}`);
}
}
/**
* 获取当前检测到的人脸列表
*/
getDetectedFaces(): FaceInfo[] {
return [...this.faces];
}
/**
* 设置人脸检测回调
*/
setOnFacesDetected(callback: (faces: FaceInfo[]) => void): void {
this.onFacesDetected = callback;
}
/**
* 释放资源
*/
async release(): Promise<void> {
if (this.metadataOutput) {
try {
await this.metadataOutput.stop();
await this.metadataOutput.release();
} catch (error) {
console.error(`[人脸检测资源释放失败] ${error}`);
}
this.metadataOutput = null;
}
}
}
3.2 场景检测与智能参数调整
场景检测可以根据画面内容自动识别拍摄场景(夜景、美食、风景、文字等),然后自动调整相机参数,实现"智能拍摄"。
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 场景类型枚举
*/
export enum SceneType {
UNKNOWN = 'unknown', // 未知
NIGHT = 'night', // 夜景
SUNSET = 'sunset', // 日落
FOOD = 'food', // 美食
LANDSCAPE = 'landscape', // 风景
PORTRAIT = 'portrait', // 人像
DOCUMENT = 'document', // 文档
INDOOR = 'indoor', // 室内
SNOW = 'snow', // 雪景
BEACH = 'beach', // 海滩
FLOWER = 'flower', // 花卉
FIREWORKS = 'fireworks', // 烟花
}
/**
* 场景检测与智能调整策略
*/
export interface SceneAdjustmentStrategy {
scene: SceneType;
exposureCompensation: number; // 曝光补偿
whiteBalanceMode: camera.WhiteBalanceMode; // 白平衡模式
focusMode: camera.FocusMode; // 对焦模式
flashMode: camera.FlashMode; // 闪光灯模式
description: string; // 策略描述
}
/**
* 场景检测控制器
* 检测场景类型并自动调整相机参数
*/
export class SceneDetectionController {
private cameraManager: camera.CameraManager | null = null;
private metadataOutput: camera.MetadataOutput | null = null;
private session: camera.PhotoSession | camera.VideoSession | null = null;
// 当前场景
private currentScene: SceneType = SceneType.UNKNOWN;
private sceneConfidence: number = 0;
// 回调
private onSceneChanged?: (scene: SceneType, confidence: number) => void;
// 智能调整策略映射表
private static readonly STRATEGIES: Map<SceneType, SceneAdjustmentStrategy> = new Map([
[SceneType.NIGHT, {
scene: SceneType.NIGHT,
exposureCompensation: 1,
whiteBalanceMode: camera.WhiteBalanceMode.WHITE_BALANCE_MODE_AUTO,
focusMode: camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO,
flashMode: camera.FlashMode.FLASH_MODE_AUTO,
description: '夜景模式:轻微增加曝光,自动白平衡,自动闪光灯'
}],
[SceneType.FOOD, {
scene: SceneType.FOOD,
exposureCompensation: 0,
whiteBalanceMode: camera.WhiteBalanceMode.WHITE_BALANCE_MODE_INCANDESCENT,
focusMode: camera.FocusMode.FOCUS_MODE_AUTO,
flashMode: camera.FlashMode.FLASH_MODE_CLOSE,
description: '美食模式:暖色调白平衡,关闭闪光灯'
}],
[SceneType.LANDSCAPE, {
scene: SceneType.LANDSCAPE,
exposureCompensation: 0,
whiteBalanceMode: camera.WhiteBalanceMode.WHITE_BALANCE_MODE_DAYLIGHT,
focusMode: camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO,
flashMode: camera.FlashMode.FLASH_MODE_CLOSE,
description: '风景模式:日光白平衡,连续自动对焦'
}],
[SceneType.DOCUMENT, {
scene: SceneType.DOCUMENT,
exposureCompensation: 1,
whiteBalanceMode: camera.WhiteBalanceMode.WHITE_BALANCE_MODE_AUTO,
focusMode: camera.FocusMode.FOCUS_MODE_AUTO,
flashMode: camera.FlashMode.FLASH_MODE_AUTO,
description: '文档模式:增加曝光,自动对焦'
}],
[SceneType.PORTRAIT, {
scene: SceneType.PORTRAIT,
exposureCompensation: 0,
whiteBalanceMode: camera.WhiteBalanceMode.WHITE_BALANCE_MODE_AUTO,
focusMode: camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO,
flashMode: camera.FlashMode.FLASH_MODE_AUTO,
description: '人像模式:自动白平衡,连续自动对焦'
}],
[SceneType.SNOW, {
scene: SceneType.SNOW,
exposureCompensation: 1,
whiteBalanceMode: camera.WhiteBalanceMode.WHITE_BALANCE_MODE_DAYLIGHT,
focusMode: camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO,
flashMode: camera.FlashMode.FLASH_MODE_CLOSE,
description: '雪景模式:增加曝光防欠曝,日光白平衡'
}],
]);
/**
* 初始化场景检测
*/
init(
context: Context,
device: camera.CameraDevice,
session: camera.PhotoSession | camera.VideoSession
): boolean {
try {
this.cameraManager = camera.getCameraManager(context);
this.session = session;
const capability = this.cameraManager.getSupportedOutputCapability(device);
const sceneProfile = capability.metadataProfiles.find(
profile => profile.metadataObjectType === camera.MetadataObjectType.SCENE_DETECTION
);
if (!sceneProfile) {
console.warn('[场景检测] 设备不支持场景检测');
return false;
}
this.metadataOutput = this.cameraManager.createMetadataOutput(sceneProfile);
this.registerSceneCallback();
return true;
} catch (error) {
console.error(`[场景检测初始化失败] ${error}`);
return false;
}
}
/**
* 注册场景检测回调
*/
private registerSceneCallback(): void {
if (!this.metadataOutput) return;
this.metadataOutput.on('metadataObjectsAvailable', (metadataObjects: camera.MetadataObject[]) => {
for (const metadata of metadataObjects) {
if (metadata.type !== camera.MetadataObjectType.SCENE_DETECTION) continue;
// 解析场景类型(实际实现中需要根据元数据格式解析)
const detectedScene = this.parseSceneFromMetadata(metadata);
this.currentScene = detectedScene;
this.sceneConfidence = 0.85; // 模拟置信度
console.info(`[场景检测] 当前场景: ${detectedScene}`);
this.onSceneChanged?.(detectedScene, this.sceneConfidence);
// 自动应用智能调整策略
this.applySceneStrategy(detectedScene);
}
});
}
/**
* 从元数据解析场景类型
* 实际实现需要根据相机输出的元数据格式进行解析
*/
private parseSceneFromMetadata(metadata: camera.MetadataObject): SceneType {
// 这里是简化的示例,实际需要解析 metadata 中的数据
// 不同厂商的元数据格式可能不同
return SceneType.UNKNOWN;
}
/**
* 应用场景对应的智能调整策略
*/
applySceneStrategy(scene: SceneType): boolean {
if (!this.session) return false;
const strategy = SceneDetectionController.STRATEGIES.get(scene);
if (!strategy) {
console.info(`[场景策略] 场景 ${scene} 无预设策略,保持当前参数`);
return false;
}
try {
// 应用曝光补偿
this.session.setExposureCompensation(strategy.exposureCompensation);
// 应用白平衡
this.session.setWhiteBalanceMode(strategy.whiteBalanceMode);
// 应用对焦模式
this.session.setFocusMode(strategy.focusMode);
// 应用闪光灯
this.session.setFlashMode(strategy.flashMode);
console.info(`[场景策略] 已应用: ${strategy.description}`);
return true;
} catch (error) {
console.error(`[场景策略应用失败] ${error}`);
return false;
}
}
/**
* 获取当前场景
*/
getCurrentScene(): SceneType {
return this.currentScene;
}
/**
* 获取场景对应的策略
*/
getStrategy(scene: SceneType): SceneAdjustmentStrategy | undefined {
return SceneDetectionController.STRATEGIES.get(scene);
}
/**
* 设置场景变更回调
*/
setOnSceneChanged(callback: (scene: SceneType, confidence: number) => void): void {
this.onSceneChanged = callback;
}
/**
* 启动场景检测
*/
async start(): Promise<boolean> {
if (!this.metadataOutput) return false;
try {
await this.metadataOutput.start();
return true;
} catch (error) {
console.error(`[场景检测启动失败] ${error}`);
return false;
}
}
/**
* 停止场景检测
*/
async stop(): Promise<void> {
if (!this.metadataOutput) return;
try {
await this.metadataOutput.stop();
} catch (error) {
console.error(`[场景检测停止失败] ${error}`);
}
}
/**
* 释放资源
*/
async release(): Promise<void> {
await this.stop();
if (this.metadataOutput) {
try {
await this.metadataOutput.release();
} catch (error) {
console.error(`[场景检测资源释放失败] ${error}`);
}
this.metadataOutput = null;
}
}
}
3.3 AI 辅助拍摄完整实现
这个示例将人脸检测和场景检测结合起来,构建一个完整的 AI 辅助拍摄系统,并提供了可视化的人脸框绘制功能。
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* AI辅助拍摄控制器
* 综合人脸检测、场景检测,实现智能拍摄
*/
export class AIAssistedCaptureController {
private faceController: FaceDetectionController = new FaceDetectionController();
private sceneController: SceneDetectionController = new SceneDetectionController();
// AI 状态
private aiEnabled: boolean = true;
private currentScene: SceneType = SceneType.UNKNOWN;
private detectedFaces: FaceInfo[] = [];
// 回调
private onAIStatusUpdate?: (status: AIStatus) => void;
/**
* 初始化 AI 辅助拍摄
* 同时启用人脸检测和场景检测
*/
async init(
context: Context,
device: camera.CameraDevice,
session: camera.PhotoSession | camera.VideoSession
): Promise<boolean> {
let success = true;
// 初始化人脸检测
const faceSupported = this.faceController.isFaceDetectionSupported(device);
if (faceSupported) {
const faceCreated = this.faceController.createFaceDetectionOutput(context, device, session);
if (faceCreated) {
this.faceController.setOnFacesDetected((faces) => {
this.detectedFaces = faces;
this.onFaceDetected(faces);
});
await this.faceController.startFaceDetection();
console.info('[AI] 人脸检测已启用');
}
} else {
console.info('[AI] 设备不支持人脸检测');
}
// 初始化场景检测
const sceneCreated = this.sceneController.init(context, device, session);
if (sceneCreated) {
this.sceneController.setOnSceneChanged((scene, confidence) => {
this.currentScene = scene;
this.onSceneChanged(scene, confidence);
});
await this.sceneController.start();
console.info('[AI] 场景检测已启用');
}
return success;
}
/**
* 人脸检测回调处理
* 检测到人脸后,自动调整对焦和曝光
*/
private onFaceDetected(faces: FaceInfo[]): void {
if (!this.aiEnabled || faces.length === 0) return;
// 找到最大的人脸(通常是最近的人脸)
const mainFace = faces.reduce((largest, face) => {
const area = face.rect.width * face.rect.height;
const largestArea = largest.rect.width * largest.rect.height;
return area > largestArea ? face : largest;
});
// 计算人脸中心点(归一化坐标)
const faceCenter: camera.Point = {
x: mainFace.rect.topLeftX + mainFace.rect.width / 2,
y: mainFace.rect.topLeftY + mainFace.rect.height / 2
};
console.info(`[AI] 主人脸中心: (${faceCenter.x.toFixed(2)}, ${faceCenter.y.toFixed(2)})`);
// 通知状态更新
this.notifyStatusUpdate();
}
/**
* 场景检测回调处理
* 检测到场景变化后,自动调整相机参数
*/
private onSceneChanged(scene: SceneType, confidence: number): void {
if (!this.aiEnabled) return;
console.info(`[AI] 场景变更: ${scene}, 置信度: ${(confidence * 100).toFixed(0)}%`);
// 置信度阈值:只有超过 70% 才自动调整
if (confidence >= 0.7) {
this.sceneController.applySceneStrategy(scene);
}
this.notifyStatusUpdate();
}
/**
* 通知 AI 状态更新
*/
private notifyStatusUpdate(): void {
const status: AIStatus = {
enabled: this.aiEnabled,
scene: this.currentScene,
faceCount: this.detectedFaces.length,
faces: [...this.detectedFaces],
};
this.onAIStatusUpdate?.(status);
}
/**
* 启用/禁用 AI 辅助
*/
setAIEnabled(enabled: boolean): void {
this.aiEnabled = enabled;
console.info(`[AI] ${enabled ? '已启用' : '已禁用'}`);
this.notifyStatusUpdate();
}
/**
* 获取当前检测到的人脸
*/
getDetectedFaces(): FaceInfo[] {
return [...this.detectedFaces];
}
/**
* 获取当前场景
*/
getCurrentScene(): SceneType {
return this.currentScene;
}
/**
* 设置状态更新回调
*/
setOnAIStatusUpdate(callback: (status: AIStatus) => void): void {
this.onAIStatusUpdate = callback;
}
/**
* 释放资源
*/
async release(): Promise<void> {
await this.faceController.release();
await this.sceneController.release();
console.info('[AI] 资源已释放');
}
}
/**
* AI 状态信息
*/
export interface AIStatus {
enabled: boolean;
scene: SceneType;
faceCount: number;
faces: FaceInfo[];
}
/**
* UI 组件 - AI辅助拍摄页面
* 实时显示人脸框和场景信息
*/
@Entry
@Component
struct AIAssistedCapturePage {
private aiController: AIAssistedCaptureController = new AIAssistedCaptureController();
@State aiEnabled: boolean = true;
@State faceCount: number = 0;
@State currentScene: string = '未知';
@State faceRects: FaceRectUI[] = [];
aboutToDisappear(): void {
this.aiController.release();
}
build() {
Stack() {
// 预览区域
Column() {
Text('相机预览区域')
.width('100%')
.height(500)
.textAlign(TextAlign.Center)
.backgroundColor('#1a1a1a')
.fontColor('#666')
.fontSize(16)
}
// 人脸框叠加层
ForEach(this.faceRects, (rect: FaceRectUI, index: number) => {
Column() {
// 人脸矩形框
Border() {
// 用矩形框标注人脸位置
}
}
.width(`${rect.width * 100}%`)
.height(`${rect.height * 100}%`)
.position({
x: `${rect.x * 100}%`,
y: `${rect.y * 100}%`
})
.borderWidth(2)
.borderColor('#00FF88')
.borderRadius(4)
})
// 顶部信息栏
Column() {
Row() {
// AI 状态指示
Circle()
.width(8)
.height(8)
.fill(this.aiEnabled ? '#00FF88' : '#666')
Text(this.aiEnabled ? 'AI 已启用' : 'AI 已关闭')
.fontSize(12)
.fontColor('#fff')
.margin({ left: 6 })
Blank()
// 场景信息
Text(`场景: ${this.currentScene}`)
.fontSize(12)
.fontColor('#E8A838')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.backgroundColor('rgba(0,0,0,0.5)')
.borderRadius(10)
// 人脸数量
if (this.faceCount > 0) {
Text(`人脸: ${this.faceCount}`)
.fontSize(12)
.fontColor('#4A90D9')
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
.backgroundColor('rgba(0,0,0,0.5)')
.borderRadius(10)
.margin({ left: 6 })
}
}
.width('100%')
.padding({ left: 15, right: 15, top: 10 })
}
.alignItems(HorizontalAlign.Start)
// 底部操作区
Column() {
Row() {
// AI 开关
Button(this.aiEnabled ? '关闭AI' : '开启AI')
.fontSize(14)
.backgroundColor(this.aiEnabled ? '#4A90D9' : '#666')
.onClick(() => {
this.aiEnabled = !this.aiEnabled;
this.aiController.setAIEnabled(this.aiEnabled);
})
// 场景策略提示
if (this.currentScene !== '未知') {
Text(this.getSceneTip(this.currentScene))
.fontSize(12)
.fontColor('#fff')
.padding(8)
.backgroundColor('rgba(0,0,0,0.6)')
.borderRadius(8)
.margin({ left: 10 })
}
}
.width('90%')
.justifyContent(FlexAlign.Center)
.margin({ bottom: 20 })
}
.width('100%')
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.height('100%')
.alignContent(Alignment.Top)
}
/**
* 获取场景提示文本
*/
private getSceneTip(scene: string): string {
const tips: Record<string, string> = {
'night': '🌙 夜景模式已激活',
'food': '🍽️ 美食模式已激活',
'landscape': '🏔️ 风景模式已激活',
'portrait': '👤 人像模式已激活',
'document': '📄 文档模式已激活',
'snow': '❄️ 雪景模式已激活',
};
return tips[scene] || `📸 ${scene}模式`;
}
}
/**
* 人脸框 UI 数据
*/
interface FaceRectUI {
x: number; // 归一化 X 坐标
y: number; // 归一化 Y 坐标
width: number; // 归一化宽度
height: number; // 归一化高度
}
四、踩坑与注意事项
4.1 元数据输出必须加入 Session
坑:创建了 MetadataOutput 但忘记加入 Session,导致回调永远不触发。
解:MetadataOutput 和 PreviewOutput、PhotoOutput 一样,必须加入 Session 才能工作。在创建 Session 时就要把 MetadataOutput 传进去。
// ❌ 错误:MetadataOutput 没有加入 Session
const session = cameraManager.createPhotoSession(cameraInput, previewOutput, photoOutput);
// MetadataOutput 被遗漏了!
// ✅ 正确:创建 Session 时包含 MetadataOutput
const session = cameraManager.createPhotoSession(
cameraInput,
previewOutput,
photoOutput,
metadataOutput // 加入元数据输出
);
4.2 人脸检测的坐标是归一化的
坑:拿到人脸矩形坐标后,直接当像素坐标用,结果人脸框画到了屏幕外面。
解:元数据中的坐标是归一化的(0-1),需要乘以预览区域的宽高才是像素坐标:
// 归一化坐标转像素坐标
const pixelX = face.rect.topLeftX * previewWidth;
const pixelY = face.rect.topLeftY * previewHeight;
const pixelWidth = face.rect.width * previewWidth;
const pixelHeight = face.rect.height * previewHeight;
4.3 元数据回调频率很高
坑:在元数据回调中做了太多计算,导致 UI 卡顿。
解:元数据回调的频率可能高达 30次/秒(每帧一次),回调中应该只做轻量级的数据提取,把耗时操作放到异步任务中。
// ❌ 错误:在回调中做耗时操作
metadataOutput.on('metadataObjectsAvailable', (metadata) => {
// 耗时的图像处理...
processImage(metadata); // UI 卡顿!
});
// ✅ 正确:回调中只提取数据,耗时操作异步处理
metadataOutput.on('metadataObjectsAvailable', (metadata) => {
const faces = extractFaceInfo(metadata); // 轻量级提取
this.updateFaceUI(faces); // 快速更新 UI
// 耗时操作放到异步
setTimeout(() => this.analyzeFaces(faces), 0);
});
4.4 场景检测的置信度
坑:场景检测的结果不稳定,一会儿是"夜景",一会儿变成"室内",频繁切换导致参数来回跳变。
解:设置置信度阈值(建议 70% 以上),并且加入时间窗口过滤——只有连续 N 帧检测到同一场景才确认切换。
// 场景稳定性过滤
private sceneBuffer: SceneType[] = [];
private readonly SCENE_BUFFER_SIZE = 5; // 连续5帧确认
private onSceneDetected(scene: SceneType, confidence: number): void {
this.sceneBuffer.push(scene);
if (this.sceneBuffer.length > this.SCENE_BUFFER_SIZE) {
this.sceneBuffer.shift();
}
// 只有连续 N 帧都是同一场景才确认
const isStable = this.sceneBuffer.length >= this.SCENE_BUFFER_SIZE &&
this.sceneBuffer.every(s => s === scene);
if (isStable && confidence >= 0.7) {
this.confirmSceneChange(scene);
}
}
4.5 并非所有设备都支持元数据
坑:在低端设备上调用 createMetadataOutput() 直接崩溃。
解:创建前一定要先检查设备是否支持对应的元数据类型:
const capability = cameraManager.getSupportedOutputCapability(device);
const hasFaceDetection = capability.metadataProfiles.some(
p => p.metadataObjectType === camera.MetadataObjectType.FACE_DETECTION
);
if (!hasFaceDetection) {
console.warn('当前设备不支持人脸检测');
// 降级处理:使用纯软件人脸检测或跳过
return;
}
4.6 MetadataOutput 和 Session 的生命周期
坑:Session 释放后,MetadataOutput 的回调还在触发,导致访问已释放的对象。
解:释放 Session 前,先停止并释放 MetadataOutput,然后注销回调。
// 正确的释放顺序
await metadataOutput.stop();
metadataOutput.off('metadataObjectsAvailable');
await metadataOutput.release();
await session.stop();
await session.release();
五、HarmonyOS 6 适配
5.1 API 变更
| 变更项 | HarmonyOS 5.0 | HarmonyOS 6.0 |
|---|---|---|
| 人脸检测 | 基础人脸矩形 | 新增人脸关键点(106点)、表情识别 |
| 物体检测 | 不支持 | 新增 OBJECT_DETECTION 类型 |
| 场景检测 | 基础场景分类 | 新增 20+ 场景类型,置信度更精确 |
| 人体姿态 | 不支持 | 新增 BODY_POSE 人体骨骼检测 |
| 手势检测 | 不支持 | 新增 HAND_GESTURE 手势识别 |
5.2 迁移要点
- 人脸关键点:6.0 中人脸检测新增了 106 个关键点输出,可以精确获取五官位置:
// HarmonyOS 6.0 人脸关键点
metadataOutput.on('faceLandmarksAvailable', (landmarks: camera.FaceLandmarks) => {
// 106 个关键点,包含眉毛、眼睛、鼻子、嘴巴、轮廓等
const leftEyeCenter = landmarks.getPoint(camera.FaceLandmarkType.LEFT_EYE_CENTER);
const rightEyeCenter = landmarks.getPoint(camera.FaceLandmarkType.RIGHT_EYE_CENTER);
const noseTip = landmarks.getPoint(camera.FaceLandmarkType.NOSE_TIP);
const mouthCenter = landmarks.getPoint(camera.FaceLandmarkType.MOUTH_CENTER);
});
- 物体检测:6.0 新增了物体检测能力,可以识别画面中的物体类别:
// HarmonyOS 6.0 物体检测
const objectProfile = capability.metadataProfiles.find(
p => p.metadataObjectType === camera.MetadataObjectType.OBJECT_DETECTION
);
const metadataOutput = cameraManager.createMetadataOutput(objectProfile);
metadataOutput.on('metadataObjectsAvailable', (objects) => {
objects.forEach(obj => {
console.info(`[物体] 类别: ${obj.category}, 置信度: ${obj.confidence}`);
});
});
- 人体姿态检测:6.0 新增了人体骨骼关键点检测,可以用于体感交互:
// HarmonyOS 6.0 人体姿态检测
const bodyProfile = capability.metadataProfiles.find(
p => p.metadataObjectType === camera.MetadataObjectType.BODY_POSE
);
六、总结
mindmap
root((相机元数据))
人脸检测
MetadataOutput + FACE_DETECTION
归一化坐标需转换
回调频率高需轻量处理
检测到人脸自动对焦
场景检测
SCENE_DETECTION 类型
置信度阈值过滤
时间窗口防抖
自动调整曝光/白平衡/对焦
物体检测
6.0新增 OBJECT_DETECTION
识别物体类别和位置
可用于智能分类
AI辅助拍摄
综合人脸+场景检测
自动调整相机参数
场景策略映射表
可视化人脸框
踩坑要点
MetadataOutput必须加入Session
坐标归一化需转换
回调中不做耗时操作
设备能力检查前置
生命周期同步释放
核心记忆口诀:
元数据加 Session,坐标要归一;回调轻量做,场景加过滤;AI 综合判,策略自动调。
- 点赞
- 收藏
- 关注作者
评论(0)