HarmonyOS 鸿蒙Next中在开发自定义相机时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?

HarmonyOS 鸿蒙Next中在开发自定义相机时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?

问题描述:在开发自定义相机功能过程中,拍照完成后需要全屏预览,发现拍照后图片显示全屏的时候会出现变形和拉伸,这里应该怎么处理,才不会显示变形。

3 回复

问题分析:

如果你在相机开发的时候,设置的预览画面是全屏的尺寸:meta60 2760/1260=2.19, 预览用的相机尺寸是1920/1080=1.777 那么这个预览画面1.77投在xcomponent2.19比例上,必然会拉伸变形;

所以要全屏预览还要不变形:需要先获取手机的宽高比,用手机的屏幕的height/width去和相机底层支持的预览尺寸的 width/height 去取最贴近的值

也就是cameraOutputCapability.previewProfiles的分辨率列表中选择2336/1080 = 2.16 这套参数,两个比值只相差 0.03 最合适

//查找【相机全屏预览宽高】最接近的手机默认分辨率
  findClosestNumber(cameraOutputCapability:camera.CameraOutputCapability,profileType:string):camera.Profile|undefined {
     let profileArr=cameraOutputCapability.previewProfiles;    //预览的分辨率
     if(profileType=='PhotoProfile'){
         profileArr = cameraOutputCapability.photoProfiles;   //相机支持的分辨率列表
      }
      let screenWidth=display.getDefaultDisplaySync().width
      let screenHeight=display.getDefaultDisplaySync().height
      let target:number=0;
      if(profileArr.length < 1) {
        return undefined;
      }
      if(screenWidth&&screenHeight){
          target=screenHeight/screenWidth       //全屏幕宽高比例
       }
      let closest = profileArr[0]; // 初始化最接近的数为数组的第一个元素
      let smallestDifference = Math.abs(profileArr[0].size.width/profileArr[0].size.height - target); // 初始化最小差值为第一个元素与目标值的差值
        for (let i = 1; i < profileArr.length; i++) {
        if(profileArr[i].size.width>=1080){  //避免小分辨率的比例算出来的数据也相临近,只计算分辨率大于1080的
          let currentDifference = Math.abs(profileArr[i].size.width/profileArr[i].size.height - target);
          if (currentDifference <= smallestDifference) {
            smallestDifference = currentDifference;
            closest = profileArr[i];
          }
        }
      }
      return closest;  //返回全屏时候最佳的分辨率

  }

cke_1119.png

更多关于HarmonyOS 鸿蒙Next中在开发自定义相机时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,解决相机全屏预览画面变形和拉伸问题,需正确配置预览尺寸与屏幕宽高比。使用CameraKit API,通过getSupportedPreviewSizes()获取支持的预览尺寸列表,选择与屏幕宽高比最匹配的尺寸。然后,在PreviewOutput中设置该尺寸,确保预览视图的布局参数与所选尺寸比例一致。若需适配不同屏幕,可动态计算并应用缩放与裁剪策略。

在HarmonyOS Next开发自定义相机时,解决全屏预览画面变形和拉伸的核心在于正确处理预览流(PreviewOutput)的宽高比预览显示窗口(XComponent)的宽高比之间的匹配关系。

这是一个典型的宽高比不匹配问题。相机传感器输出的预览图像有固定的宽高比(如4:3、16:9),而你的全屏预览窗口的宽高比通常由设备屏幕决定(如常见的19.5:9)。直接将一种宽高比的图像填充到另一种宽高比的视图中,必然导致拉伸或变形。

解决方案:使用“适配填充”模式

正确的做法不是拉伸图像,而是采用 Image.ClipType 或通过计算进行裁剪适配,确保图像内容区域不变形,同时填充整个屏幕。在HarmonyOS Next中,这通常通过以下步骤实现:

  1. 获取关键参数

    • 预览流尺寸:从CameraOutputCapability中获取PreviewOutput支持的预览分辨率(宽高),并选择一个(通常选择与屏幕比例最接近或分辨率最高的)。
    • 屏幕/窗口尺寸:获取用于显示预览的XComponentSurface的实际宽高。
  2. 计算适配比例与偏移

    • 比较预览流宽高比与屏幕宽高比。
    • 核心逻辑
      • 如果预览流更“胖”(宽高比更大),则预览流的高度需要适配屏幕高度,宽度会超出屏幕。此时需要按高度比例缩放预览流,并计算水平方向的裁剪起始点(cropStartX)。
      • 如果屏幕更“胖”(宽高比更大),则预览流的宽度需要适配屏幕宽度,高度会超出屏幕。此时需要按宽度比例缩放预览流,并计算垂直方向的裁剪起始点(cropStartY)。
    • 计算公式本质上是求两个比例之间的最大公倍数填充,确保一个方向完全填充,另一个方向居中裁剪。
  3. 应用至预览输出

    • 在配置PreviewOutput时,关键一步是设置其Surface(通常来自XComponent)的裁剪区域和变换矩阵
    • 在HarmonyOS Next的相机框架中,你需要通过PreviewOutputsetSurfaceSize()setSurfaceOffset()等方法,或通过CameraSessionsetDisplayRotation()setDisplayCrop()等机制(具体API名称可能随版本调整),将计算出的缩放比例、裁剪区域应用到预览画布上。这相当于告诉系统:“请将相机预览流按照这个规则映射到我的显示Surface上”。
    • 更常见的做法是,将用于预览的XComponent的布局和显示模式设置为能够处理这种比例差异。虽然XComponent本身可能不直接提供复杂的缩放模式,但你可以将其嵌入到一个StackFlex布局中,并配合Image组件的objectFit属性(如设置为Cover)的类似思想,通过上层容器控制其显示。但更底层的控制仍需通过相机API设置Surface的映射关系。

简要代码思路示意(概念层面):

// 1. 获取预览能力并选择预览分辨率
let previewProfile: camera.PreviewProfile = ... // 从capability中选择一个
let previewRatio = previewProfile.size.width / previewProfile.size.height;

// 2. 获取显示窗口尺寸
let displayWidth = ... // XComponent的宽度
let displayHeight = ... // XComponent的高度
let displayRatio = displayWidth / displayHeight;

// 3. 计算缩放与裁剪
let scale: number;
let cropX: number = 0;
let cropY: number = 0;

if (previewRatio > displayRatio) {
    // 预览流更宽,按高度适配
    scale = displayHeight / previewProfile.size.height;
    let scaledWidth = previewProfile.size.width * scale;
    cropX = (scaledWidth - displayWidth) / 2.0 / scale; // 换算回原始预览流坐标
} else {
    // 屏幕更宽(或同等),按宽度适配
    scale = displayWidth / previewProfile.size.width;
    let scaledHeight = previewProfile.size.height * scale;
    cropY = (scaledHeight - displayHeight) / 2.0 / scale; // 换算回原始预览流坐标
}

// 4. 将计算出的cropX, cropY, scale等参数应用于PreviewOutput的Surface配置
// 具体API调用需参考最新的HarmonyOS Next Camera Kit文档
previewOutput.setSurfaceCropRegion({ /* 根据计算结果设置矩形区域 */ });
// 或通过设置变换矩阵等方式

总结要点:

  • 根源:预览流与显示窗口宽高比不一致。
  • 原则:以裁剪代替拉伸,保持图像内容比例正确。
  • 关键:计算正确的缩放比例和裁剪起始点,并通过相机API(PreviewOutputCameraSession 的相关设置)将其应用于预览Surface。
  • 后续处理:拍照得到的图片(PhotoOutput)是完整的传感器输出,其处理方式与预览分离。全屏查看照片时应使用图片查看组件,并同样采用objectFit: Cover或类似模式,避免二次拉伸。

请根据HarmonyOS Next最新的相机API文档,查找与设置预览Surface裁剪、缩放相关的具体方法并实施。

回到顶部