HarmonyOS 鸿蒙Next 调用photoOutput的capture拍照得到的camera.Photo输出的图片宽高问题
HarmonyOS 鸿蒙Next 调用photoOutput的capture拍照得到的camera.Photo输出的图片宽高问题
通过 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
更多关于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 < 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) => {
console.error(`Camera input error code: ${error.code}`);
})
// 打开相机
await cameraInput.open();
// 获取支持的模式类型
let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 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<camera.Profile> = cameraOutputCap.previewProfiles;
if (!previewProfilesArray) {
console.error("createOutput previewProfilesArray == null || undefined");
}
let photoProfilesArray: Array<camera.Profile> = 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) => {
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) => {
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(() => {
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<number> = [];
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 <= 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) => {
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>) =></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系统中,调用photoOutput
的capture
方法进行拍照时,camera.Photo
输出的图片宽高问题通常与相机的预览配置、输出配置以及设备本身的摄像头特性有关。
-
预览配置:确保在调用
capture
之前,预览流的配置(如分辨率、方向等)已正确设置,因为预览配置会影响拍照输出的基础参数。 -
输出配置:在调用
capture
时,应明确指定输出图片的分辨率、格式等参数。如果未指定,系统可能会使用默认配置,这可能与预期不符。 -
设备摄像头特性:不同设备的摄像头硬件和驱动可能存在差异,这可能导致即使配置相同,输出图片的宽高也会有所不同。建议查阅设备文档或进行实际测试以了解设备特性。
-
图片处理:如果输出图片的宽高与预期不符,可以在获取图片后进行裁剪或缩放处理,以满足应用需求。
综上所述,要解决camera.Photo
输出图片的宽高问题,需确保预览配置、输出配置正确,并考虑设备摄像头特性。同时,可根据实际需求对图片进行后续处理。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html