当前位置: 首页 > news >正文

【Harmony】鸿蒙相机拍照使用简单示例

完整代码

import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { image } from '@kit.ImageKit';
import { fileIo } from '@kit.CoreFileKit';
import { display, Prompt } from '@kit.ArkUI';@Component
export struct CameraView {private context: Context | undefined = this.getUIContext().getHostContext();cameraHelper: CameraHelper = new CameraHelper(this.context!);aboutToAppear() {this.cameraHelper.setTakePictureCallback((path) => {Prompt.showToast({ message: path })})}@BuilderuiBuilder() {XComponent({id: 'cameraPreview',type: XComponentType.SURFACE,controller: this.cameraHelper.getController()}).onLoad(() => {this.cameraHelper.requestPermissionsFn(() => {this.cameraHelper.initCamera().then();});}).width("100%").height("100%").backgroundColor(Color.Black)}build() {this.uiBuilder()}
}class CameraHelper {private context: Context | undefined;constructor(context: Context) {this.context = context;}getScreenWidth(): number {return display.getDefaultDisplaySync().width;}getScreenHeight(): number {return display.getDefaultDisplaySync().height;}private sWidth: number = this.getScreenWidth()private sHeight: number = this.getScreenHeight()private session: camera.PhotoSession | undefined = undefined;private cameras: Array<camera.CameraDevice> = [];private cameraInput: camera.CameraInput | undefined = undefined;private previewOutput: camera.PreviewOutput | undefined = undefined;private photoOutput: camera.PhotoOutput | undefined = undefined;private selectCameraPosition: number = camera.CameraPosition.CAMERA_POSITION_FRONT;private onTakePictureCallback: ((imagePath: string) => void) | undefined = undefined;private cameraManager: camera.CameraManager | undefined = undefined;private xComponentSurfaceId: string = '';private xComponentCtl: XComponentController = new XComponentController();// 获取控制器getController(): XComponentController {return this.xComponentCtl;}// 设置拍照回调setTakePictureCallback(callback: (imagePath: string) => void): void {this.onTakePictureCallback = callback;}// 请求相机权限async requestPermissionsFn(success?: (result: boolean) => void): Promise<void> {let atManager = abilityAccessCtrl.createAtManager();if (this.context) {let res = await atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA']);for (let i = 0; i < res.permissions.length; i++) {if (res.permissions[i] === 'ohos.permission.CAMERA' && res.authResults[i] === 0) {success?.(true);}}}}// 拍照方法async takePicture(): Promise<void> {if (!this.photoOutput) {Prompt.showToast({ message: 'PhotoOutput not initialized' })return;}try {let photoCaptureSetting: camera.PhotoCaptureSetting = {quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,rotation: camera.ImageRotation.ROTATION_0,mirror: this.isCameraFront()};console.info('开始拍照...');await this.photoOutput.capture(photoCaptureSetting);console.info('拍照成功完成');} catch (error) {let err = error as BusinessError;console.error(`拍照异常, error code: ${err.code}`);}}// 释放相机资源async releaseCamera(): Promise<void> {try {if (this.session) {await this.session.stop();this.session.release();this.session = undefined;}if (this.cameraInput) {await this.cameraInput.close();this.cameraInput = undefined;}this.photoOutput = undefined;this.previewOutput = undefined;} catch (error) {let err = error as BusinessError;console.error(`Release camera failed, error code: ${err.code}`);}}// 判断是否为前置摄像头(保持不变)isCameraFront(): boolean {return this.selectCameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT;}/*** 将ArrayBuffer保存为文件*/private async saveArrayBufferToFile(buffer: ArrayBuffer, fileName: string): Promise<string> {const filePath = `${this.context!.filesDir}/${fileName}`;let file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);// 写入数据fileIo.writeSync(file.fd, buffer);fileIo.closeSync(file.fd);return filePath;}// 检查是否有多个摄像头可用(保持不变)hasMultipleCameras(): boolean {return this.cameras && this.cameras.length > 1;}// 切换摄像头(保持不变)async switchCamera(): Promise<void> {if (!this.hasMultipleCameras()) {console.info('只有一个摄像头,无法切换');return;}try {if (this.isCameraFront()) {this.selectCameraPosition = camera.CameraPosition.CAMERA_POSITION_BACK;} else {this.selectCameraPosition = camera.CameraPosition.CAMERA_POSITION_FRONT;}await this.initCamera();} catch (error) {let err = error as BusinessError;console.error(`切换摄像头失败, error code: ${err.code}`);}}// 初始化相机async initCamera(): Promise<void> {await this.releaseCamera();this.xComponentSurfaceId = this.xComponentCtl.getXComponentSurfaceId();try {this.cameraManager = camera.getCameraManager(this.context);if (!this.cameraManager) {console.error('getCameraManager failed');return;}this.cameras = this.cameraManager.getSupportedCameras();if (!this.cameras || this.cameras.length === 0) {console.error('No cameras found');return;}const cameraDevice = this.cameras.find((v) => {return v.cameraPosition === this.selectCameraPosition;})this.cameraInput = this.cameraManager.createCameraInput(cameraDevice);if (!this.cameraInput) {console.error('createCameraInput failed');return;}await this.cameraInput.open();// 获取拍照模式的输出能力let capability: camera.CameraOutputCapability =this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_PHOTO);if (!capability) {// 如果拍照模式不支持,尝试使用视频模式获取预览配置capability = this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_VIDEO);}if (!capability || capability.previewProfiles.length === 0) {console.error('No preview profiles supported');this.releaseCamera();return;}// 选择预览配置let previewProfile = this.selectBestPreviewProfile(capability.previewProfiles, this.sHeight, this.sWidth);this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId);if (!this.previewOutput) {console.error('createPreviewOutput failed');this.releaseCamera();return;}// 创建拍照输出流if (capability.photoProfiles && capability.photoProfiles.length > 0) {let photoProfile = this.selectBestPreviewProfile(capability.photoProfiles, this.sHeight, this.sWidth);this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);}if (!this.photoOutput) {console.error('createPhotoOutput failed');this.releaseCamera();return;}// 创建拍照会话this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;if (!this.session) {console.error('createSession failed');this.releaseCamera();return;}// 配置会话this.session.beginConfig();this.session.addInput(this.cameraInput);this.session.addOutput(this.previewOutput);this.session.addOutput(this.photoOutput);await this.session.commitConfig();// 启动预览await this.session.start();console.info('摄像头预览启动成功');// 设置拍照监听器this.setupPhotoOutputListeners();} catch (error) {let err = error as BusinessError;console.error(`Camera initialization failed, error code: ${err.code}`);}}// 选择最佳预览配置的方法private selectBestPreviewProfile(previewProfiles: camera.Profile[],targetWidth: number,targetHeight: number): camera.Profile {if (previewProfiles.length === 0) {throw new Error('No preview profiles available');}// 计算目标宽高比const targetAspectRatio = targetWidth / targetHeight;let bestProfile = previewProfiles[0];let minAspectDiff = Number.MAX_VALUE;let minSizeDiff = Number.MAX_VALUE;for (const profile of previewProfiles) {const profileAspectRatio = profile.size.width / profile.size.height;const aspectDiff = Math.abs(profileAspectRatio - targetAspectRatio);// 优先选择宽高比最接近的配置if (aspectDiff < minAspectDiff) {minAspectDiff = aspectDiff;bestProfile = profile;minSizeDiff = Math.abs(profile.size.width - targetWidth) +Math.abs(profile.size.height - targetHeight);}// 如果宽高比相同,选择尺寸最接近的else if (Math.abs(aspectDiff - minAspectDiff) < 0.01) {const sizeDiff = Math.abs(profile.size.width - targetWidth) +Math.abs(profile.size.height - targetHeight);if (sizeDiff < minSizeDiff) {minSizeDiff = sizeDiff;bestProfile = profile;}}}console.info(`最佳匹配: ${bestProfile.size.width}x${bestProfile.size.height}, 宽高比: ${bestProfile.size.width /bestProfile.size.height}`);return bestProfile;}// 设置拍照输出流监听器private setupPhotoOutputListeners(): void {if (!this.photoOutput) {return;}this.photoOutput.on('photoAvailable', (err: BusinessError, photo: camera.Photo) => {if (err) {console.error(`获取照片失败: ${err.code}`);return;}// 处理照片数据this.processPhoto(photo);});}// 处理照片private async processPhoto(photo: camera.Photo): Promise<void> {photo.main.getComponent(image.ComponentType.JPEG, async (err: BusinessError, component: image.Component) => {if (err || !component) {Prompt.showToast({ message: `获取JPEG组件失败: ${err?.code}` })return;}try {const fileName = `checkSkin${Date.now()}.jpg`;const buffer = component.byteBuffer;const filePath = await this.saveArrayBufferToFile(buffer, fileName);this.onTakePictureCallback?.(filePath)// 释放资源photo.main.release();} catch (error) {photo.main.release();}});}
}
http://www.dtcms.com/a/545388.html

相关文章:

  • 论文笔记:“Mind the Gap Preserving and Compensating for the Modality Gap in“
  • 国产光学软件突破 | 3D可视化衍射光波导仿真
  • 仓颉语言中的Option类型与空安全处理:深度解析与实践
  • 无穷级数概念
  • mysql的事务、锁以及MVCC
  • [Dify 实战] 使用插件实现内容抓取与格式转换流程:从网页到结构化输出的自动化方案
  • 李宏毅机器学习笔记35
  • 类和对象深层回顾:(内含面试题)拷贝构造函数,传值返回和传引用返回区别
  • Rust环境搭建
  • 潍坊做网站价格个人网页设计软件
  • LeetCode 刷题【138. 随机链表的复制】
  • 做可转债好的网站wordpress不用邮件确认
  • Rust 中的减少内存分配策略:从分配器视角到架构设计 [特殊字符]
  • MySQL8.0.30 版本中redo log的变化
  • 0430. 扁平化多级双向链表
  • 网站关键词多少合适icp备案服务码
  • TypeScript声明合并详解二
  • 做网站组织架构my77728域名查询
  • 深度学习------图像分割项目
  • 【深度学习2】线性回归的从零开始实现
  • LeetCode第2题:两数相加及其变种(某大厂面试原题)
  • Java 字符编码全解析:从乱码根源到 Unicode 实战指南
  • SpringBoot 高效工具类大全
  • 自己做网站用软件wordpress电商优秀
  • 百度网站建设中的自由容器网站用哪个数据库
  • 入侵检测系统——HIDS和NIDS的区别
  • C语言多进程创建和回收
  • 仓颉编程语言:控制流语句详解(if/else)
  • 专利撰写与申请核心要点简报
  • AI搜索引擎num=100参数移除影响深度分析:内容标识与准确性变化