/** * 获取元数据输出流 * @param cameraManager 相机管理器实例 * @param cameraOutputCapability 相机支持的输出能力 * @return 元数据输出流 */ getMetadataOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability) { let metadataObjectTypes: Array<camera.MetadataObjectType> = cameraOutputCapability.supportedMetadataObjectTypes; try { this.metadataOutput = cameraManager.createMetadataOutput(metadataObjectTypes); } catch (error) { let err = error as BusinessError; console.error(`Failed to createMetadataOutput, error code: ${err.code}`); } }
/** * 元数据流可用监听,获取人脸位置信息 * @param metadataOutput 元数据输出流 */ onMetadataObjectsAvailable(metadataOutput: camera.MetadataOutput): void { metadataOutput.on('metadataObjectsAvailable', (err: BusinessError, metadataObjectArr: Array<camera.MetadataObject>) => { if (err !== undefined && err.code !== 0) { return; } if (metadataObjectArr.length === 1) { let faceRect = metadataObjectArr[0]; if (faceRect.boundingBox.topLeftX && faceRect.boundingBox.topLeftY && faceRect.boundingBox.width && faceRect.boundingBox.height) { this.faceTopLeftX = this.getUIContext().px2vp(faceRect.boundingBox.topLeftX) * this.actualWidth; this.faceTopLeftY = this.getUIContext().px2vp(faceRect.boundingBox.topLeftY) * this.actualHeight; this.faceWidth = this.getUIContext().px2vp(faceRect.boundingBox.width) * this.actualWidth; this.faceHeight = this.getUIContext().px2vp(faceRect.boundingBox.height) * this.actualHeight; // 打印人脸归一化坐标 console.info(`人脸归一化坐标:boundingBox = ${JSON.stringify(faceRect.boundingBox)}`); } } }); }
/** * 元数据流错误信息监听 * @param metadataOutput 元数据输出流 */ onMetadataError(metadataOutput: camera.MetadataOutput): void { metadataOutput.on('error', (metadataOutputError: BusinessError) => { console.error(`Metadata output error code: ${metadataOutputError.code}`); }); }
build() { Column({ space: 20 }) { Stack({ alignContent: Alignment.TopStart }) { XComponent({ type: XComponentType.SURFACE, controller: this.mXComponentController, imageAIOptions: this.options }) .backgroundColor(Color.Gray) .onLoad(async () => { // 动态计算XComponent的宽高,确保与相机预览比例一致 const displayWidth = display.getDefaultDisplaySync().width; // 更新XComponent的实际显示高度 this.mXComponentController.setXComponentSurfaceRect({ surfaceWidth: displayWidth, surfaceHeight: this.surfaceHeight }); this.actualWidth = displayWidth; this.actualHeight = this.surfaceHeight; // 同步更新状态变量,确保UI使用正确高度 this.xComponentSurfaceId = this.mXComponentController.getXComponentSurfaceId(); }) .height(this.getUIContext().px2vp(this.surfaceHeight)) // 绘制人脸区域,宽高和位置需动态变化 Text() .border({ width: 1, color: Color.Orange }) .position({ y: this.faceTopLeftY, x: this.faceTopLeftX }) .visibility(this.showRect ? Visibility.Visible : Visibility.Hidden) .width(this.faceWidth) .height(this.faceHeight) } Button('点击开始检测人脸') .onClick(() => { // 显示人像区域 this.showRect = true; }) Button('点击取消检测人脸') .onClick(() => { // 不显示人像区域 this.showRect = false; }) } }
完整示例参考如下:
import { camera } from '@kit.CameraKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { image } from '@kit.ImageKit'; import { bundleManager, abilityAccessCtrl, PermissionRequestResult, Permissions } from '@kit.AbilityKit'; import { display } from '@kit.ArkUI'; @Entry @Component struct Index { @State faceTopLeftX: number = 0; @State faceTopLeftY: number = 0; @State faceWidth: number = 0; @State faceHeight: number = 0; @State actualWidth: number = 0; @State actualHeight: number = 0; @State showRect: boolean = false; private imageSize: image.Size = { width: 480, height: 640 }; private context: Context = this.getUIContext().getHostContext() as Context; private mXComponentController: XComponentController = new XComponentController; private cameraManager: camera.CameraManager | undefined = undefined; private cameraSession: camera.PhotoSession | undefined = undefined; private photoOutput: camera.PhotoOutput | undefined = undefined; private cameraInput: camera.CameraInput | undefined = undefined; private previewOutput: camera.PreviewOutput | undefined = undefined; private metadataOutput: camera.MetadataOutput | undefined = undefined; private aiController: ImageAnalyzerController = new ImageAnalyzerController(); private xComponentSurfaceId: string = ''; private notHasPermission: boolean = true; private displayWidth = display.getDefaultDisplaySync().width; private surfaceHeight = this.displayWidth * (this.imageSize.height / this.imageSize.width); private options: ImageAIOptions = { types: [ImageAnalyzerType.SUBJECT, ImageAnalyzerType.TEXT], aiController: this.aiController }; checkPermissions(permission: Permissions) { let atManager = abilityAccessCtrl.createAtManager(); let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); let tokenID = bundleInfo.appInfo.accessTokenId; let authResults = atManager.checkAccessTokenSync(tokenID, permission); return authResults === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; } aboutToAppear(): void { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA']) .then((data: PermissionRequestResult) => { if (data.authResults[0] === 0) { this.notHasPermission = false; this.prepareCamera(); } else if (data.authResults[0] === -1) { this.notHasPermission = true; } }) .catch((err: BusinessError) => { console.error(`data: ${JSON.stringify(err)}`); }); } onPageShow(): void { if (!this.notHasPermission) { let permissions: Permissions = 'ohos.permission.CAMERA'; if (this.checkPermissions(permissions)) { this.prepareCamera(); this.notHasPermission = false; } else { this.notHasPermission = true; } } } onPageHide(): void { if (!this.notHasPermission) { this.releaseCamera(); } } aboutToDisappear(): void { if (!this.notHasPermission) { this.releaseCamera(); } } build() { Column({ space: 20 }) { Stack({ alignContent: Alignment.TopStart }) { XComponent({ type: XComponentType.SURFACE, controller: this.mXComponentController, imageAIOptions: this.options }) .backgroundColor(Color.Gray) .onLoad(async () => { // 动态计算XComponent的宽高,确保与相机预览比例一致 const displayWidth = display.getDefaultDisplaySync().width; // 更新XComponent的实际显示高度 this.mXComponentController.setXComponentSurfaceRect({ surfaceWidth: displayWidth, surfaceHeight: this.surfaceHeight }); this.actualWidth = displayWidth; this.actualHeight = this.surfaceHeight; // 同步更新状态变量,确保UI使用正确高度 this.xComponentSurfaceId = this.mXComponentController.getXComponentSurfaceId(); }) .height(this.getUIContext().px2vp(this.surfaceHeight)) // 绘制人脸区域,宽高和位置需动态变化 Text() .border({ width: 1, color: Color.Orange }) .position({ y: this.faceTopLeftY, x: this.faceTopLeftX }) .visibility(this.showRect ? Visibility.Visible : Visibility.Hidden) .width(this.faceWidth) .height(this.faceHeight) } Button('点击开始检测人脸') .onClick(() => { // 显示人像区域 this.showRect = true; }) Button('点击取消检测人脸') .onClick(() => { // 不显示人像区域 this.showRect = false; }) } } createCameraManager() { // 创建CameraManager对象 let cameraManager: camera.CameraManager = camera.getCameraManager(this.context); if (!cameraManager) { console.error('CameraDemo camera.getCameraManager error'); return; } this.cameraManager = cameraManager; // 监听相机状态变化 this.cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => { if (err) { console.error(`Camera status listener failed: ${JSON.stringify(err)}}`); return; } console.info(`CameraDemo camera: ${cameraStatusInfo.camera.cameraId}, status: ${cameraStatusInfo.status}`); }); } async prepareCamera() { this.createCameraManager(); if (!this.cameraManager) { console.error('CameraDemo cameraManager is undefined.'); return; } // 获取支持指定的相机设备对象 let cameraDevices: Array<camera.CameraDevice> = []; let cameraDevice: camera.CameraDevice; try { cameraDevices = this.cameraManager.getSupportedCameras(); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo The getSupportedCameras call failed. error: ${JSON.stringify(err)}`); } cameraDevices.forEach((cameraDevice: camera.CameraDevice) => { console.info(`CameraDemo cameraId: ${cameraDevice.cameraId}, cameraPosition: ${cameraDevice.cameraPosition.toString()}, cameraType: ${cameraDevice.cameraType.toString()}, connectionType: ${cameraDevice.connectionType.toString()}`); }); // 创建相机输入流 try { cameraDevice = cameraDevices[1]; this.cameraInput = this.cameraManager.createCameraInput(cameraDevice); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo createCaptureSession error. error: ${JSON.stringify(err)}`); return; } // 监听cameraInput错误信息 this.cameraInput.on('error', cameraDevice, (error: BusinessError) => { console.error(`CameraDemo Camera input error: ${JSON.stringify(error)}`); }); // 打开相机 try { await this.cameraInput.open(); } catch (error) { let err = error as BusinessError; // 相机不可用错误码 if (error.code === 1000500001) { this.releaseCamera(); await this.prepareCamera(); return; } console.error(`CameraDemo cameraInput open error. error: ${JSON.stringify(err)}`); } // 获取相机设备支持的输出流能力 let cameraOutputCapability: camera.CameraOutputCapability = this.cameraManager.getSupportedOutputCapability(cameraDevices[0], camera.SceneMode.NORMAL_PHOTO); if (!cameraOutputCapability) { console.error('CameraDemo cameraManager.getSupportedOutputCapability error'); return; } let targetProfile: camera.Profile | undefined; for (let profile of cameraOutputCapability.previewProfiles) { if (profile.size.width === this.imageSize.height && profile.size.height === this.imageSize.width) { targetProfile = profile; break; } } if (targetProfile) { const previewProfile = targetProfile; this.imageSize.height = targetProfile?.size?.height; this.imageSize.width = targetProfile?.size?.width; console.info(`选择的最佳预览尺寸:宽 = ${this.imageSize.width}, 高 = ${this.imageSize.height}`); this.getMetadataOutput(this.cameraManager, cameraOutputCapability); if (!this.metadataOutput) { console.error('initCamera metadataOutput error'); return; } this.metadataOutput.start().then(() => { console.info('metadataOutput start'); }).catch((err: BusinessError) => { console.error(`metadataOutput start err: ${JSON.stringify(err)}`); }); this.onMetadataObjectsAvailable(this.metadataOutput); this.onMetadataError(this.metadataOutput); // 创建预览输出流 try { this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId); if (!this.previewOutput) { console.error('previewOutput undefined!'); } } catch (error) { let err = error as BusinessError; console.error(`CameraDemo createCaptureSession error. error: ${JSON.stringify(err)}`); return; } // 监听预览输出流的错误信息 this.previewOutput.on('error', (error: BusinessError) => { console.error(`CameraDemo previewOutput error: ${JSON.stringify(error)}`); }); // 选取合适的分辨率 try { let photoProfile: camera.Profile | undefined = undefined; cameraOutputCapability.photoProfiles.forEach((profile) => { if (profile.size.width === this.imageSize.width && profile.size.height === this.imageSize.height) { photoProfile = profile; return; } }); if (photoProfile) { this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile); } } catch (error) { let err = error as BusinessError; console.error(`CameraDemo createPhotoOutput error ${JSON.stringify(err)}`); } if (this.photoOutput === undefined) { console.error('CameraDemo photoOutput is undefined.'); return; } // 创建相机会话 try { this.cameraSession = this.cameraManager.createSession<camera.PhotoSession>(camera.SceneMode.NORMAL_PHOTO); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo createCaptureSession error. error: ${JSON.stringify(err)}`); return; } // 监听session错误信息 this.cameraSession.on('error', (error: BusinessError) => { console.error(`CameraDemo Capture session error: ${JSON.stringify(error)}`); }); // 开始会话配置 try { this.cameraSession.beginConfig(); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo beginConfig error. error: ${JSON.stringify(err)}`); } // 向会话中添加相机输入流 try { this.cameraSession.addInput(this.cameraInput); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo addInput error. error: ${JSON.stringify(err)}`); } // 向会话中添加预览输出流和元数据输出流 try { this.cameraSession.addOutput(this.previewOutput); this.cameraSession.addOutput(this.metadataOutput); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo add previewOutput error. error: ${JSON.stringify(err)}`); } // 向会话中添加拍照输出流 try { this.cameraSession.addOutput(this.photoOutput); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo add photoOutput error. error: ${JSON.stringify(err)}`); } // 提交会话配置 try { await this.cameraSession.commitConfig(); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo captureSession commitConfig error: ${JSON.stringify(err)}`); } // 启动会话 try { await this.cameraSession.start(); } catch (error) { let err = error as BusinessError; console.error(`CameraDemo captureSession start error: ${JSON.stringify(err)}`); } } } // 释放相机流 async releaseCamera() { if (this.cameraInput) { this.cameraInput.close(); } if (this.previewOutput) { this.previewOutput.release(); } if (this.photoOutput) { this.photoOutput.release(); } if (this.cameraSession) { this.cameraSession.stop(); } if (this.cameraSession) { this.cameraSession.release(); } if (this.metadataOutput) { this.metadataOutput.release(); } this.cameraSession = undefined; } /** * 获取元数据输出流 * @param cameraManager 相机管理器实例 * @param cameraOutputCapability 相机支持的输出能力 * @return 元数据输出流 */ getMetadataOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability) { let metadataObjectTypes: Array<camera.MetadataObjectType> = cameraOutputCapability.supportedMetadataObjectTypes; try { this.metadataOutput = cameraManager.createMetadataOutput(metadataObjectTypes); } catch (error) { let err = error as BusinessError; console.error(`Failed to createMetadataOutput, error code: ${err.code}`); } } /** * 元数据流可用监听,获取人脸位置信息 * @param metadataOutput 元数据输出流 */ onMetadataObjectsAvailable(metadataOutput: camera.MetadataOutput): void { metadataOutput.on('metadataObjectsAvailable', (err: BusinessError, metadataObjectArr: Array<camera.MetadataObject>) => { if (err !== undefined && err.code !== 0) { return; } if (metadataObjectArr.length === 1) { let faceRect = metadataObjectArr[0]; if (faceRect.boundingBox.topLeftX && faceRect.boundingBox.topLeftY && faceRect.boundingBox.width && faceRect.boundingBox.height) { this.faceTopLeftX = this.getUIContext().px2vp(faceRect.boundingBox.topLeftX) * this.actualWidth; this.faceTopLeftY = this.getUIContext().px2vp(faceRect.boundingBox.topLeftY) * this.actualHeight; this.faceWidth = this.getUIContext().px2vp(faceRect.boundingBox.width) * this.actualWidth; this.faceHeight = this.getUIContext().px2vp(faceRect.boundingBox.height) * this.actualHeight; // 打印人脸归一化坐标 console.info(`人脸归一化坐标:boundingBox = ${JSON.stringify(faceRect.boundingBox)}`); } } }); } /** * 元数据流错误信息监听 * @param metadataOutput 元数据输出流 */ onMetadataError(metadataOutput: camera.MetadataOutput): void { metadataOutput.on('error', (metadataOutputError: BusinessError) => { console.error(`Metadata output error code: ${metadataOutputError.code}`); }); } }
在使用相机功能前需要在module.json5文件中配置相机权限:
{
"name": "ohos.permission.CAMERA",
"reason": "$string:reason_camera",
"usedScene": {
"abilities": [
"EntryAbility"
]
}
}, 元数据每秒钟可以获取大约30次回调,优于使用ImageReceiver获取预览帧图片后再使用Core Vision Kit中的人脸检测能力(此方法每秒钟获取到的回调远少于元数据,会造成相机预览界面的严重卡顿),所以在对流数据进行检测时应优先选择Camera Kit中提供的元数据能力。