HarmonyOS 鸿蒙Next 调用photoOutput的capture拍照得到的camera.Photo输出的图片宽高问题

发布于 1周前 作者 wuwangju 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 调用photoOutput的capture拍照得到的camera.Photo输出的图片宽高问题

cke_147.png

通过 photo.main 获取到size的高度为1;component的rowStride 为 0

日志输入为 width:25165824 height:1 stride:0


更多关于HarmonyOS 鸿蒙Next 调用photoOutput的capture拍照得到的camera.Photo输出的图片宽高问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复
具体描述下问题是什么,只看截图看不懂

更多关于HarmonyOS 鸿蒙Next 调用photoOutput的capture拍照得到的camera.Photo输出的图片宽高问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


setPhotoOutputCb(photoOutput: camera.PhotoOutput): void { //设置回调之后,调用photoOutput的capture方法,就会将拍照的buffer回传到回调中 photoOutput.on(‘photoAvailable’, (errCode: BusinessError, photo: camera.Photo): void => { console.info(‘getPhoto start’); console.info(err: ${JSON.stringify(errCode)}); if (errCode || photo === undefined) { console.error(‘getPhoto failed’); return; } let imageObj = photo.main; imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => { console.info(‘getComponent start’); if (errCode || component === undefined) { console.error(‘getComponent failed’); return; } if (component.byteBuffer) { // 请参考步骤7解析buffer数据,本示例以方式一为例 let width = imageObj.size.width; // 获取图片的宽 let height = imageObj.size.height; // 获取图片的高 let stride = component.rowStride; // 获取图片的stride 像素的宽度 // console.debug(getComponent with width:${width} height:${height} stride:${stride}); // stride与width一致 if (stride == width) { this.pixelMap = await image.createPixelMap(component.byteBuffer, { size: { height: height, width: width }, srcPixelFormat: 8, })

      } else {
        // stride与width不一致
        const dstBufferSize = width * height * 1.5 // 以NV21为例(YUV_420_SP格式的图片)YUV_420_SP内存计算公式:长x宽+(长x宽)/2
        const dstArr = new Uint8Array(dstBufferSize)
        for (let j = 0; j < height * 1.5; j++) {
          const srcBuf = new Uint8Array(component.byteBuffer, j * stride, width)
          dstArr.set(srcBuf, j * width)
        }
        this.pixelMap = await image.createPixelMap(dstArr.buffer, {
          size: { height: height, width: width },
          srcPixelFormat: 8,
        })
      }
    } else {
      console.error('byteBuffer is null');
      return;
    }
    // 如需要在图库中看到所保存的图片、视频资源,请使用用户无感的安全控件创建媒体资源。
    // buffer处理结束后需要释放该资源,如果未正确释放资源会导致后续拍照获取不到buffer
    imageObj.release();
  });
});

} async cameraShootingCase(surfaceId: string): Promise<void> { // 创建CameraManager对象 let cameraManager: camera.CameraManager = camera.getCameraManager(getContext()); if (!cameraManager) { console.error(“camera.getCameraManager error”); return; } // 监听相机状态变化 cameraManager.on(‘cameraStatus’, (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => { if (err !== undefined && err.code !== 0) { console.error('cameraStatus with errorCode = ’ + err.code); return; } console.info(camera : ${cameraStatusInfo.camera.cameraId}); console.info(status: ${cameraStatusInfo.status}); }); // 获取当前设备支持的相机device列表 let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras(); if (cameraArray.length <= 0) { console.error(“cameraManager.getSupportedCameras error”); return; }

for (let index = 0; index &lt; cameraArray.length; index++) {
  console.info('cameraId : ' + cameraArray[index].cameraId); // 获取相机ID
  console.info('cameraPosition : ' + cameraArray[index].cameraPosition); // 获取相机位置
  console.info('cameraType : ' + cameraArray[index].cameraType); // 获取相机类型
  console.info('connectionType : ' + cameraArray[index].connectionType); // 获取相机连接类型
}

// 创建相机输入流
let cameraInput: camera.CameraInput | undefined = undefined;
try {
  cameraInput = cameraManager.createCameraInput(camera.CameraPosition.CAMERA_POSITION_FRONT,
    camera.CameraType.CAMERA_TYPE_DEFAULT);
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to createCameraInput errorCode = ' + err.code);
}
if (cameraInput === undefined) {
  return;
}

// 监听cameraInput错误信息
let cameraDevice: camera.CameraDevice = cameraArray[0];
cameraInput.on('error', cameraDevice, (error: BusinessError) =&gt; {
  console.error(`Camera input error code: ${error.code}`);
})

// 打开相机
await cameraInput.open();

// 获取支持的模式类型
let sceneModes: Array&lt;camera.SceneMode&gt; = cameraManager.getSupportedSceneModes(cameraArray[0]);
let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) &gt;= 0;
if (!isSupportPhotoMode) {
  console.error('photo mode not support');
  return;
}
// 获取相机设备支持的输出流能力
let cameraOutputCap: camera.CameraOutputCapability =
  cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_PHOTO);
if (!cameraOutputCap) {
  console.error("cameraManager.getSupportedOutputCapability error");
  return;
}
console.info("outputCapability: " + JSON.stringify(cameraOutputCap));

let previewProfilesArray: Array&lt;camera.Profile&gt; = cameraOutputCap.previewProfiles;
if (!previewProfilesArray) {
  console.error("createOutput previewProfilesArray == null || undefined");
}

let photoProfilesArray: Array&lt;camera.Profile&gt; = cameraOutputCap.photoProfiles;
if (!photoProfilesArray) {
  console.error("createOutput photoProfilesArray == null || undefined");
}

// 创建预览输出流,其中参数 surfaceId 参考上文 XComponent 组件,预览流为XComponent组件提供的surface
let previewOutput: camera.PreviewOutput | undefined = undefined;
let previewProfile: camera.Profile = previewProfilesArray[0];
//计算输出流的宽高比
const width = previewProfile.size.width
const height = previewProfile.size.height
const radio = width / height
this.imageWidth = this.imageHeight * radio; // 更新xComponent组件的高
try {
  previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId);
} catch (error) {
  let err = error as BusinessError;
  console.error(`Failed to create the PreviewOutput instance. error code: ${err.code}`);
}
if (previewOutput === undefined) {
  return;
}
// 监听预览输出错误信息
previewOutput.on('error', (error: BusinessError) =&gt; {
  console.error(`Preview output error code: ${error.code}`);
});

// 创建拍照输出流
// this.photoOutput: camera.PhotoOutput | undefined = undefined;
try {
  this.photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0]);
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to createPhotoOutput errorCode = ' + err.code);
}
if (this.photoOutput === undefined) {
  return;
}

//调用上面的回调函数来保存图片
this.setPhotoOutputCb(this.photoOutput);

//创建会话
let photoSession: camera.PhotoSession | undefined = undefined;
try {
  photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to create the session instance. errorCode = ' + err.code);
}
if (photoSession === undefined) {
  return;
}
// 监听session错误信息
photoSession.on('error', (error: BusinessError) =&gt; {
  console.error(`Capture session error code: ${error.code}`);
});

// 开始配置会话
try {
  photoSession.beginConfig();
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to beginConfig. errorCode = ' + err.code);
}

// 向会话中添加相机输入流
try {
  photoSession.addInput(cameraInput);
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to addInput. errorCode = ' + err.code);
}

// 向会话中添加预览输出流
try {
  photoSession.addOutput(previewOutput);
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to addOutput(previewOutput). errorCode = ' + err.code);
}
let canAdd: boolean = photoSession.canAddOutput(this.photoOutput);
if (canAdd) {
  // 向会话中添加拍照输出流
  try {
    photoSession.addOutput(this.photoOutput);
  } catch (error) {
    let err = error as BusinessError;
    console.error('Failed to addOutput(photoOutput). errorCode = ' + err.code);
  }
}

// 提交会话配置
await photoSession.commitConfig();

// 启动会话
await photoSession.start().then(() =&gt; {
  console.info('Promise returned to indicate the session start success.');
});
// 判断设备是否支持闪光灯
let flashStatus: boolean = false;
try {
  flashStatus = photoSession.hasFlash();
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to hasFlash. errorCode = ' + err.code);
}
console.info('Returned with the flash light support status:' + flashStatus);

if (flashStatus) {
  // 判断是否支持自动闪光灯模式
  let flashModeStatus: boolean = false;
  try {
    let status: boolean = photoSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO);
    flashModeStatus = status;
  } catch (error) {
    let err = error as BusinessError;
    console.error('Failed to check whether the flash mode is supported. errorCode = ' + err.code);
  }
  if (flashModeStatus) {
    // 设置自动闪光灯模式
    try {
      photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO);
    } catch (error) {
      let err = error as BusinessError;
      console.error('Failed to set the flash mode. errorCode = ' + err.code);
    }
  }
}

// 判断是否支持连续自动变焦模式
let focusModeStatus: boolean = false;
try {
  let status: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
  focusModeStatus = status;
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to check whether the focus mode is supported. errorCode = ' + err.code);
}

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. errorCode = ' + err.code);
  }
}

// 获取相机支持的可变焦距比范围
let zoomRatioRange: Array&lt;number&gt; = [];
try {
  zoomRatioRange = photoSession.getZoomRatioRange();
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to get the zoom ratio range. errorCode = ' + err.code);
}
if (zoomRatioRange.length &lt;= 0) {
  return;
}
// 设置可变焦距比
try {
  photoSession.setZoomRatio(zoomRatioRange[0]);
} catch (error) {
  let err = error as BusinessError;
  console.error('Failed to set the zoom ratio value. errorCode = ' + err.code);
}
let photoCaptureSetting: camera.PhotoCaptureSetting = {
  quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // 设置图片质量高
  rotation: camera.ImageRotation.ROTATION_0 // 设置图片旋转角度0
}
// 使用当前拍照设置进行拍照
this.photoOutput.capture(photoCaptureSetting, (err: BusinessError) =&gt; {
  if (err) {
    console.error(`Failed to capture the photo ${err.message}`);
    return;
  }
  console.info('Callback invoked to indicate the photo capture request success.');
});

// // 需要在拍照结束之后调用以下关闭摄像头和释放会话流程,避免拍照未结束就将会话释放。
// // 停止当前会话
// await photoSession.stop();
//
// // 释放相机输入流
// await cameraInput.close();
//
// // 释放预览输出流
// await previewOutput.release();
//
// // 释放拍照输出流
// await photoOutput.release();
//
// // 释放会话
// await photoSession.release();
//
// // 会话置空
// photoSession = undefined;

}

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

就是文档上的整个案例呢 拍照的时候 通过’photoAvailable’回调 拿到的image.Image 打印出来的高度是1

然后 在调用 image.createPixelMap(component.byteBuffer, { size: { height: height, width: width }, srcPixelFormat: 8, })转成pixelMap时 报错 62980096 操作失败

getComponent方法传入的类型是.ComponentType.JPEG时,返回值的是NV21数据;

NV21格式的图片当前创建pixelmap需要通过createimageSource创建ImageSource;然后沟通通过ImageSource的createPixelMap接口创建pixelmap。关键代码如下:

深色代码主题
复制
          let imageObj: image.Image = photo.main;
          imageObj.getComponent(image.ComponentType.JPEG, async (err: BusinessError, component: image.Component) => {
            Logger.info(TAG, `getComponent start`);
            if (err) {
              Logger.error(TAG, `getComponent err:${err.code}`);
              return;
            }
            let buffer: ArrayBuffer = component.byteBuffer;
            let sourceOptions: image.SourceOptions = {
              sourceSize: { width: imageObj.size.width, height: imageObj.size.height },
              sourceDensity: 0
            };
        <span class="hljs-keyword">let</span> <span class="hljs-attr">imageSource</span>: image.<span class="hljs-property">ImageSource</span> = image.<span class="hljs-title function_">createImageSource</span>(buffer, sourceOptions);

        imageSource.<span class="hljs-title function_">createPixelMap</span>(<span class="hljs-function">(<span class="hljs-params">err: BusinessError, pixelMap: image.PixelMap</span>) =&gt;</span> {
          <span class="hljs-keyword">if</span> (err) {
            <span class="hljs-title class_">Logger</span>.<span class="hljs-title function_">error</span>(<span class="hljs-variable constant_">TAG</span>, <span class="hljs-string">`createPixelMap err:<span class="hljs-subst">${err.code}</span>`</span>);
            <span class="hljs-keyword">return</span>;
          }
          <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">handlePhotoAssetCb</span>(pixelMap);
        });</code></pre></div></div></div>

在HarmonyOS鸿蒙Next系统中,调用photoOutputcapture方法进行拍照时,camera.Photo输出的图片宽高问题通常与相机的预览配置、输出配置以及设备本身的摄像头特性有关。

  1. 预览配置:确保在调用capture之前,预览流的配置(如分辨率、方向等)已正确设置,因为预览配置会影响拍照输出的基础参数。

  2. 输出配置:在调用capture时,应明确指定输出图片的分辨率、格式等参数。如果未指定,系统可能会使用默认配置,这可能与预期不符。

  3. 设备摄像头特性:不同设备的摄像头硬件和驱动可能存在差异,这可能导致即使配置相同,输出图片的宽高也会有所不同。建议查阅设备文档或进行实际测试以了解设备特性。

  4. 图片处理:如果输出图片的宽高与预期不符,可以在获取图片后进行裁剪或缩放处理,以满足应用需求。

综上所述,要解决camera.Photo输出图片的宽高问题,需确保预览配置、输出配置正确,并考虑设备摄像头特性。同时,可根据实际需求对图片进行后续处理。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部