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

完整代码

typescript 复制代码
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 })
    })
  }

  @Builder
  uiBuilder() {
    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();
      }
    });
  }
}
相关推荐
爱笑的眼睛119 小时前
HarmonyOS RemoteWindow远程窗口组件的分布式能力深度解析
华为·harmonyos
鲜枣课堂11 小时前
华为最新光通信架构AI-OTN,如何应对AI浪潮?
人工智能·华为·架构
格林威12 小时前
AOI在新能源电池制造领域的应用
人工智能·数码相机·计算机视觉·视觉检测·制造·工业相机
爱笑的眼睛1114 小时前
HarmonyOS Badge徽标组件:深入消息提示的实现与优化
华为·harmonyos
格林威15 小时前
AOI在传统汽车制造领域中的应用
大数据·人工智能·数码相机·计算机视觉·ai·制造·aoi
爱笑的眼睛1116 小时前
HarmonyOS List组件性能优化:从基础到高级实践
华为·harmonyos
ifeng091817 小时前
HarmonyOS实战项目:打造智能家居控制中心(设备发现与控制)
wpf·智能家居·harmonyos
ifeng091820 小时前
HarmonyOS分布式数据管理——跨设备数据同步实战
harmonyos
ifeng091821 小时前
HarmonyOS实战项目:开发一个分布式新闻阅读客户端
分布式·wpf·harmonyos