目录

拍照(ArkTS)

概述

拍照是相机的重要功能之一,拍照模块基于相机复杂的逻辑,为了保证用户拍出的照片质量,在中间步骤可以设置分辨率、闪光灯、焦距、照片质量及旋转角度等信息。

目前相机开发有两种相机拍照方案,分别是相机分段式拍照和相机单段式拍照(本文将以单段式拍照为基础进行说明)。

开发步骤

详细的API说明请参考@ohos.multimedia.camera (相机管理)。

  1. 导入Image接口。获取拍照输出的数据需要用到系统提供的Image接口能力,导入Image接口的方法如下。

    import { image } from '@kit.ImageKit';
    import { camera } from '@kit.CameraKit';
    import { fileIo } from '@kit.CoreFileKit';
    import { BusinessError } from '@kit.BasicServicesKit';
  2. 创建拍照输出流。

    通过CameraOutputCapability中的photoProfiles属性,可获取当前设备支持的拍照输出流。通过createPhotoOutput方法传入支持的某一个输出流Profile创建拍照输出流。

    getPhotoOutput(cameraManager: camera.CameraManager,
      cameraOutputCapability: camera.CameraOutputCapability): camera.PhotoOutput | undefined {
      let photoProfilesArray: camera.Profile[] = cameraOutputCapability.photoProfiles;
      if (!photoProfilesArray || photoProfilesArray.length === 0) {
        console.error('photoProfilesArray is null or []');
      }
      let photoOutput: camera.PhotoOutput | undefined = undefined;
      try {
        this.photoProfileObj = photoProfilesArray[0]
        photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0]);
      } catch (error) {
        let err = error as BusinessError;
        console.error(`Failed to createPhotoOutput. error: ${err}`);
      }
      return photoOutput;
    }
  3. 设置拍照on('photoAvailable')的回调,并将拍照的buffer保存为图片。

    Context获取方式请参考:获取UIAbility的上下文信息。

    如需要在图库中看到所保存的图片、视频资源,需要将其保存到媒体库,保存方式请参考:保存媒体库资源。

    需要在photoOutput.on('photoAvailable')接口获取到buffer时,将buffer在安全控件中保存到媒体库。

    setPhotoOutputCb(photoOutput: camera.PhotoOutput, context: Context) {
      // 设置回调之后,调用photoOutput的capture方法,就会将拍照的buffer回传到回调中。
      photoOutput.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => {
        console.info('getPhoto start');
        if (errCode || photo === undefined) {
          console.error(`getPhoto failed, err: ${errCode}`);
          return;
        }
          // 如需要在图库中看到所保存的图片、视频资源,请使用用户无感的安全控件创建媒体资源。
          this.mediaLibSavePhotoSingle(context, photo.main)
      });
    }
    
    mediaLibSavePhotoSingle(context: Context, imageObj: image.Image) {
      imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => {
        if (errCode || component === undefined) {
          Logger.error('getComponent failed');
          return;
        }
        const buffer: ArrayBuffer = component.byteBuffer;
        if (!buffer) {
          Logger.error('byteBuffer is null');
          return;
        }
        let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE;
        let extension: string = 'jpg';
        let options: photoAccessHelper.CreateOptions = {
          title: 'testPhoto'
        }
        let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
          photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(context, photoType, extension, options);
        assetChangeRequest.addResource(photoAccessHelper.ResourceType.IMAGE_RESOURCE, buffer)
        assetChangeRequest.saveCameraPhoto();
        let accessHelper: photoAccessHelper.PhotoAccessHelper =
          photoAccessHelper.getPhotoAccessHelper(context);
        await accessHelper.applyChanges(assetChangeRequest);
        let imageSource = image.createImageSource(buffer);
        let pixelmap = imageSource.createPixelMapSync();
        this.callback(pixelmap, assetChangeRequest.getAsset().uri);
        accessHelper.release();
        // buffer处理结束后需要释放该资源,如果未正确释放资源会导致后续拍照获取不到buffer。
        imageObj.release();
      });
    }
  4. 参数配置。

    配置相机的参数可以调整拍照的一些功能,包括闪光灯、变焦、焦距等。

    configuringSession(photoSession: camera.PhotoSession | camera.VideoSession): void {
      // 判断设备是否支持闪光灯。
      let flashStatus: boolean = false;
      try {
        flashStatus = photoSession.hasFlash();
      } catch (error) {
        let err = error as BusinessError;
        console.error(`Failed to hasFlash. error: ${err}`);
      }
      console.info(`Returned with the flash light support status: ${flashStatus}`);
      if (flashStatus) {
        // 判断是否支持关闭闪光灯模式。
        let flashModeStatus: boolean = false;
        try {
          flashModeStatus = photoSession?.isFlashModeSupported(camera.FlashMode.FLASH_MODE_CLOSE);
        } catch (error) {
          let err = error as BusinessError;
          console.error(`Failed to check whether the flash mode is supported. error: ${err}`);
        }
        if (flashModeStatus) {
          // 设置闪光灯模式关闭。
          try {
            photoSession?.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE);
          } catch (error) {
            let err = error as BusinessError;
            console.error(`Failed to set the flash mode. error: ${err}`);
          }
        }
      }
      // 判断是否支持连续自动变焦模式。
      let focusModeStatus: boolean = false;
      try {
        focusModeStatus = photoSession?.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
      } catch (error) {
        let err = error as BusinessError;
        console.error(`Failed to check whether the focus mode is supported. error: ${err}`);
      }
      if (focusModeStatus) {
        // 设置连续自动变焦模式。
        try {
          photoSession?.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
        } catch (error) {
          let err = error as BusinessError;
          console.error(`Failed to set the focus mode. error: ${err}`);
        }
      }
      // 获取相机支持的可变焦距比范围。
      let zoomRatioRange: number[] = [];
      try {
        zoomRatioRange = photoSession?.getZoomRatioRange();
      } catch (error) {
        let err = error as BusinessError;
        console.error(`Failed to get the zoom ratio range. error: ${err}`);
      }
      if (zoomRatioRange.length <= 0 ) {
        return;
      }
      // 设置可变焦距比。
      try {
        photoSession?.setZoomRatio(zoomRatioRange[0]);
      } catch (error) {
        let err = error as BusinessError;
        console.error(`Failed to set the zoom ratio value. error: ${err}`);
      }
    }
  5. 触发拍照。

    通过photoOutput的capture方法,执行拍照任务。该方法有两个参数,第一个参数为拍照设置参数的setting,setting中可以设置照片的质量和旋转角度,第二参数为回调函数。

    获取拍照旋转角度的方法为,通过PhotoOutput中的getPhotoRotation方法获取rotation实际的值。

    图片地理位置信息Location,使用方法可参考capture示例。

    capture(captureLocation?: camera.Location): void {
      let captureLocationDefault: camera.Location = {
        latitude: 0,
        longitude: 0,
        altitude: 0
      };
      if (captureLocation != undefined) {
        captureLocationDefault = captureLocation;
      }
      let settings: camera.PhotoCaptureSetting = {
        quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,  // 设置图片质量高。
        // 设置图片旋转角度的camera.ImageRotation.ROTATION_0是通过说明中获取拍照角度的getPhotoRotation方法获取的值进行设置。
        rotation: camera.ImageRotation.ROTATION_0,
        location: captureLocation,  // 设置图片地理位置。
        mirror: false  // 设置镜像使能开关(默认关)。
      };
      try {
        if (this.photoOutput == undefined) {
          console.info(`photoOutput is undefined.`);
          return;
        }
        this.photoOutput.capture(settings, (err: BusinessError) => {
          if (err) {
            console.error(`Failed to capture the photo. error: ${err}`);
            return;
          }
          console.info('Callback invoked to indicate the photo capture request success.');
        });
      } catch (error) {
        console.error(`capture call failed. error: ${error}`);
      }
    }

高性能拍照

从API version 21开始支持高性能拍照功能,即在进行单段式拍照时设置明确的画质优先策略。

单段式拍照的体验主要由出图速度和最终图片质量衡量。因此,为满足开发者在不同场景下的差异化需求,对这两项指标的侧重也不同。例如,街头抓拍要求快速捕捉瞬间,而风景或人像拍摄则更追求极致的画质。

仅单段式拍照支持设置画质优先策略。若在分段式拍照中设置画质优先策略,该设置将无效。

[h2]画质优先策略

在使用单段式拍照时,支持设置速度优先和画质优先两种画质优先策略类型,并且分别对应着不同的PhotoQualityPrioritization枚举类型。

[h2]如何正确设置画质优先策略

为了正确地在单段式拍照中设置画质优先策略,高性能拍照功能提供了如下两个接口:

[h2]开发步骤

高性能拍照相关接口需要在会话管理(ArkTS)流程的使能步骤中进行调用。

具体调用时机如下:

状态监听

在相机应用开发过程中,可以随时监听拍照输出流状态,包括拍照流开始、拍照帧的开始与结束、拍照输出流的错误。