曝光控制比你想象中复杂一点很多人在用 Camera Kit 开发相机应用时会卡在一个问题上自动曝光在某些场景下表现不佳。比如逆光拍人像脸部过暗夜景拍灯光高光溢出。自动曝光算法再聪明也没法懂你的构图意图。这时候就需要手动干预曝光参数。但 HarmonyOS 的曝光控制 API 有个特点看起来简单实际用起来限制不少。官方文档只写了“支持设置曝光模式和补偿值”但没说清楚这几个 API 在什么设备上生效、什么场景下会失效。这篇文章就把这些边界情况讲清楚。这篇文章主要解决三个场景手动控制曝光时间比如长曝光拍夜景调整曝光补偿适应不同光照环境锁定自动曝光对焦完成后不让画面变亮变暗环境说明DevEco Studio 版本DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本HarmonyOS 6.1.0(23) 及以上 目标设备手机建议真机测试模拟器在某些曝光 API 上行为不一致核心实现1. 获取并配置曝光模式相机服务提供三种曝光模式自动曝光、锁定曝光、手动曝光。import{camera}fromkit.CameraKit;// 在 session 创建完成后调用functionsetupExposure(session:camera.PhotoSession):void{// 检查设备是否支持手动曝光constexposureModeRange:camera.ExposureMode[]session.getSupportedExposureModes();if(exposureModeRange.includes(camera.ExposureMode.MANUAL)){// 切换到手动曝光模式session.setExposureMode(camera.ExposureMode.MANUAL);}else{// 不支持手动曝光回退到自动session.setExposureMode(camera.ExposureMode.AUTO);console.warn(当前设备不支持手动曝光模式);}}这段代码的核心逻辑很直接先查能力再设置模式。很多人直接调用setExposureMode(camera.ExposureMode.MANUAL)结果在某些机型上直接抛异常。实际项目里getSupportedExposureModes()返回的数组长度在不同设备上可能不同。低端机型可能只支持自动曝光和锁定曝光两种模式。2. 设置曝光时间手动模式下曝光时间的单位是纳秒。functionsetManualExposure(session:camera.PhotoSession,durationNs:number):void{// 获取支持的最小曝光时长和最大曝光时长const{min,max}session.getSupportedExposureDurationRange();// 限制范围防止设置超出硬件能力的值constclampedDurationMath.min(Math.max(durationNs,min),max);// 设置曝光时长session.setExposure(clampedDuration);// 验证是否生效constactualDurationsession.getExposure();console.info(已设置曝光时长${actualDuration}ns);}这里有个容易踩坑的地方setExposure只在手动曝光模式下生效。如果在自动模式下调用API 不会报错但不会产生任何效果。曝光时长范围一般是 1/10000 秒100,000 ns到 30 秒30,000,000,000 ns但这个范围因设备而异。建议在 UI 初始化时读取这个范围用来约束滑块的最大最小值。3. 曝光补偿 Exposure bias曝光补偿用于在自动曝光的基础上微调整体亮度。functionsetExposureCompensation(session:camera.PhotoSession,compensation:number):void{// 获取支持的最小补偿值和最大补偿值const{min,max,step}session.getSupportedExposureBiasRange();// 确保补偿值在有效范围内// 步进值一般为 0.1 或 0.3需要对齐constalignedCompensationMath.round(compensation/step)*step;constfinalCompensationMath.min(Math.max(alignedCompensation,min),max);// 单位是 EV曝光值session.setExposureBias(finalCompensation);// 建议配置完成后读取以验证constactualBiassession.getExposureBias();console.info(曝光补偿值${actualBias}EV);}补偿值的范围通常是 -4 EV 到 4 EV步进 0.1 EV 或 0.3 EV。但不同设备硬件差异较大不要硬编码补偿范围。自动曝光模式下曝光补偿才能生效。如果在手动模式下设置 compensationAPI 会直接返回错误码。4. 曝光锁定 Auto Exposure Lock曝光锁定的作用是让相机保持在当前亮度水平即使场景发生变化也不会重新调整。functiontoggleExposureLock(session:camera.PhotoSession,lock:boolean):void{if(lock){// 锁定自动曝光session.lockAutoExposure();console.info(自动曝光已锁定);}else{// 解锁自动曝光session.unlockAutoExposure();console.info(自动曝光已解锁);}}// 检查当前锁定状态functionisExposureLocked(session:camera.PhotoSession):boolean{returnsession.isAutoExposureLocked();}曝光锁定适合在用户完成对焦和构图后使用。例如短视频录制过程中场景内容在变化但用户不希望画面忽明忽暗。注意曝光锁定只在自动曝光模式下有意义。如果已经在手动曝光模式下锁定调用不会报错但不会改变行为。5. 曝光调节滑块控件把上面几个能力整合到一个 UI 组件中Componentstruct ExposureControlPanel{StateprivateexposureMode:camera.ExposureModecamera.ExposureMode.AUTO;StateprivateexposureValue:number0;// 曝光时间单位纳秒StateprivateexposureBias:number0;// 曝光补偿单位 EVStateprivateexposureLocked:booleanfalse;privatesession:camera.PhotoSession|nullnull;build(){Column({space:12}){// 曝光模式切换Row(){Text(曝光模式)Toggle({type:ToggleType.Switch,isOn:this.exposureModecamera.ExposureMode.MANUAL}).onChange((isOn:boolean){this.exposureModeisOn?camera.ExposureMode.MANUAL:camera.ExposureMode.AUTO;this.session?.setExposureMode(this.exposureMode);})}// 手动曝光时长滑块仅手动模式下可用if(this.exposureModecamera.ExposureMode.MANUAL){Slider({value:this.exposureValue,min:100000,// 最小 1/10000 秒max:30000000000,// 最大 30 秒step:100000}).onChange((value:number){this.exposureValuevalue;this.session?.setExposure(value);})}else{// 自动模式 曝光补偿Slider({value:this.exposureBias,min:-4,max:4,step:0.1}).onChange((value:number){this.exposureBiasvalue;this.session?.setExposureBias(value);})// 曝光锁定开关Toggle({type:ToggleType.Switch,isOn:this.exposureLocked}).onChange((isOn:boolean){this.exposureLockedisOn;if(isOn){this.session?.lockAutoExposure();}else{this.session?.unlockAutoExposure();}})}}.padding(16).backgroundColor(#1A000000).borderRadius(12)}}这段代码的逻辑核心是根据曝光模式动态切换 UI 控件。手动模式下只显示曝光时长滑块自动模式下显示曝光补偿滑块和锁定开关。这种设计避免了用户配置无效参数。常见问题问题 1setExposure 设置了值但画面没变化现象在手动曝光模式下调用session.setExposure(value)API 返回成功但预览画面亮度没有变化。原因这个 API 需要搭配正确的曝光模式才能生效。如果先调用了setExposureMode切换到手动模式但在后续操作中因为某些原因回调又切换回了自动模式比如restart()或重新配置 session那么设置的曝光时长会被忽略。解决方案每次配置曝光参数前先读取当前的曝光模式进行校验。functionsafeSetExposure(session:camera.PhotoSession,duration:number):void{constcurrentModesession.getExposureMode();if(currentMode!camera.ExposureMode.MANUAL){console.warn(当前不是手动曝光模式设置曝光时长无效);return;}session.setExposure(duration);}问题 2曝光锁定在页面返回后失效现象用户锁定曝光后按 Home 键返回桌面再回到应用或者页面发生路由跳转曝光锁定状态自动丢失。原因相机 session 在页面不可见时会被系统自动释放。页面重新可见后需要重新创建 session之前的状态全部丢失。解决方案在页面的生命周期回调中保存和恢复曝光锁定状态。StateprivatelockState:booleanfalse;privatesession:camera.PhotoSession|nullnull;onPageShow():void{// 页面显示时尝试恢复锁定状态if(this.lockStatethis.session){this.session.lockAutoExposure();}}onPageHide():void{// 页面隐藏时状态保存在 State 中}最佳实践先查能力再设置每次设置曝光参数前调用getSupportedExposureModes()、getSupportedExposureDurationRange()、getSupportedExposureBiasRange()获取设备能力。不同设备硬件差异大硬编码范围会导致 API 调用异常。曝光补偿值的步进对齐setExposureBias传参必须是对齐 step 的值。比如 step0.3 的设备传入 1.0 会取整为 0.9。建议在 UI 中直接限制用户滑动到对齐值。锁定状态与 UI 同步曝光锁定在 session 层面是一个一次性操作没有 state 概念。UI 的锁定开关状态必须手动维护不要在lockAutoExposure或unlockAutoExposure之后直接读取 session 状态来更新 UI。完整 Demo 入口// entry/src/main/ets/pages/ExposureDemo.etsimport{camera}fromkit.CameraKit;import{media}fromkit.MediaKit;EntryComponentstruct ExposureDemo{privatesession:camera.PhotoSession|nullnull;privatecameraManager:camera.CameraManager|nullnull;aboutToAppear():void{this.initCamera();}asyncinitCamera():Promisevoid{// 获取相机管理器实例this.cameraManagercamera.getCameraManager();// 获取相机设备constcameras:camera.CameraDevice[]this.cameraManager.getSupportedCameras();if(cameras.length0){console.error(没有找到摄像头);return;}// 创建会话this.sessionawaitthis.cameraManager.createCameraSession(camera.ControllerMode.PHOTO);awaitthis.session.beginConfig();awaitthis.session.addInput(cameras[0]);// 添加预览输出constpreviewOutputawaitthis.session.createPreviewOutput();awaitthis.session.addOutput(previewOutput);// 提交配置awaitthis.session.commitConfig();awaitthis.session.start();}build(){Stack(){// 相机预览流省略实际预览组件的实现XComponent({id:preview,type:XComponentType.SURFACE}).width(100%).height(100%)// 曝光控制面板ExposureControlPanel({session:this.session}).position({x:0,y:70%})}.width(100%).height(100%)}aboutToDisappear():void{this.session?.release();}}FAQQ为什么真机正常模拟器上曝光补偿值设置后画面不变化A模拟器对曝光硬件的模拟不完整特别是补偿值响应。建议真机调试曝光相关功能。Q为什么页面返回后曝光锁定失效了A相机 session 在页面不可见时被系统自动释放。重新创建 session 后之前锁定的状态不会自动恢复。需要在页面onPageShow回调中根据 UI 状态重新锁定。Q设置曝光补偿后预览画面亮度没有立即变化需要关闭重新打开 camera 吗A不需要。setExposureBias是实时生效的不需要重启 session。如果设置后 1-2 秒画面无变化检查传入的值是否在设备支持的范围内。另一个可能性是当前处于手动曝光模式补偿值不生效。
HarmonyOS技术精讲-Camera Kit(相机服务)第8篇:曝光控制深入解析
曝光控制比你想象中复杂一点很多人在用 Camera Kit 开发相机应用时会卡在一个问题上自动曝光在某些场景下表现不佳。比如逆光拍人像脸部过暗夜景拍灯光高光溢出。自动曝光算法再聪明也没法懂你的构图意图。这时候就需要手动干预曝光参数。但 HarmonyOS 的曝光控制 API 有个特点看起来简单实际用起来限制不少。官方文档只写了“支持设置曝光模式和补偿值”但没说清楚这几个 API 在什么设备上生效、什么场景下会失效。这篇文章就把这些边界情况讲清楚。这篇文章主要解决三个场景手动控制曝光时间比如长曝光拍夜景调整曝光补偿适应不同光照环境锁定自动曝光对焦完成后不让画面变亮变暗环境说明DevEco Studio 版本DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本HarmonyOS 6.1.0(23) 及以上 目标设备手机建议真机测试模拟器在某些曝光 API 上行为不一致核心实现1. 获取并配置曝光模式相机服务提供三种曝光模式自动曝光、锁定曝光、手动曝光。import{camera}fromkit.CameraKit;// 在 session 创建完成后调用functionsetupExposure(session:camera.PhotoSession):void{// 检查设备是否支持手动曝光constexposureModeRange:camera.ExposureMode[]session.getSupportedExposureModes();if(exposureModeRange.includes(camera.ExposureMode.MANUAL)){// 切换到手动曝光模式session.setExposureMode(camera.ExposureMode.MANUAL);}else{// 不支持手动曝光回退到自动session.setExposureMode(camera.ExposureMode.AUTO);console.warn(当前设备不支持手动曝光模式);}}这段代码的核心逻辑很直接先查能力再设置模式。很多人直接调用setExposureMode(camera.ExposureMode.MANUAL)结果在某些机型上直接抛异常。实际项目里getSupportedExposureModes()返回的数组长度在不同设备上可能不同。低端机型可能只支持自动曝光和锁定曝光两种模式。2. 设置曝光时间手动模式下曝光时间的单位是纳秒。functionsetManualExposure(session:camera.PhotoSession,durationNs:number):void{// 获取支持的最小曝光时长和最大曝光时长const{min,max}session.getSupportedExposureDurationRange();// 限制范围防止设置超出硬件能力的值constclampedDurationMath.min(Math.max(durationNs,min),max);// 设置曝光时长session.setExposure(clampedDuration);// 验证是否生效constactualDurationsession.getExposure();console.info(已设置曝光时长${actualDuration}ns);}这里有个容易踩坑的地方setExposure只在手动曝光模式下生效。如果在自动模式下调用API 不会报错但不会产生任何效果。曝光时长范围一般是 1/10000 秒100,000 ns到 30 秒30,000,000,000 ns但这个范围因设备而异。建议在 UI 初始化时读取这个范围用来约束滑块的最大最小值。3. 曝光补偿 Exposure bias曝光补偿用于在自动曝光的基础上微调整体亮度。functionsetExposureCompensation(session:camera.PhotoSession,compensation:number):void{// 获取支持的最小补偿值和最大补偿值const{min,max,step}session.getSupportedExposureBiasRange();// 确保补偿值在有效范围内// 步进值一般为 0.1 或 0.3需要对齐constalignedCompensationMath.round(compensation/step)*step;constfinalCompensationMath.min(Math.max(alignedCompensation,min),max);// 单位是 EV曝光值session.setExposureBias(finalCompensation);// 建议配置完成后读取以验证constactualBiassession.getExposureBias();console.info(曝光补偿值${actualBias}EV);}补偿值的范围通常是 -4 EV 到 4 EV步进 0.1 EV 或 0.3 EV。但不同设备硬件差异较大不要硬编码补偿范围。自动曝光模式下曝光补偿才能生效。如果在手动模式下设置 compensationAPI 会直接返回错误码。4. 曝光锁定 Auto Exposure Lock曝光锁定的作用是让相机保持在当前亮度水平即使场景发生变化也不会重新调整。functiontoggleExposureLock(session:camera.PhotoSession,lock:boolean):void{if(lock){// 锁定自动曝光session.lockAutoExposure();console.info(自动曝光已锁定);}else{// 解锁自动曝光session.unlockAutoExposure();console.info(自动曝光已解锁);}}// 检查当前锁定状态functionisExposureLocked(session:camera.PhotoSession):boolean{returnsession.isAutoExposureLocked();}曝光锁定适合在用户完成对焦和构图后使用。例如短视频录制过程中场景内容在变化但用户不希望画面忽明忽暗。注意曝光锁定只在自动曝光模式下有意义。如果已经在手动曝光模式下锁定调用不会报错但不会改变行为。5. 曝光调节滑块控件把上面几个能力整合到一个 UI 组件中Componentstruct ExposureControlPanel{StateprivateexposureMode:camera.ExposureModecamera.ExposureMode.AUTO;StateprivateexposureValue:number0;// 曝光时间单位纳秒StateprivateexposureBias:number0;// 曝光补偿单位 EVStateprivateexposureLocked:booleanfalse;privatesession:camera.PhotoSession|nullnull;build(){Column({space:12}){// 曝光模式切换Row(){Text(曝光模式)Toggle({type:ToggleType.Switch,isOn:this.exposureModecamera.ExposureMode.MANUAL}).onChange((isOn:boolean){this.exposureModeisOn?camera.ExposureMode.MANUAL:camera.ExposureMode.AUTO;this.session?.setExposureMode(this.exposureMode);})}// 手动曝光时长滑块仅手动模式下可用if(this.exposureModecamera.ExposureMode.MANUAL){Slider({value:this.exposureValue,min:100000,// 最小 1/10000 秒max:30000000000,// 最大 30 秒step:100000}).onChange((value:number){this.exposureValuevalue;this.session?.setExposure(value);})}else{// 自动模式 曝光补偿Slider({value:this.exposureBias,min:-4,max:4,step:0.1}).onChange((value:number){this.exposureBiasvalue;this.session?.setExposureBias(value);})// 曝光锁定开关Toggle({type:ToggleType.Switch,isOn:this.exposureLocked}).onChange((isOn:boolean){this.exposureLockedisOn;if(isOn){this.session?.lockAutoExposure();}else{this.session?.unlockAutoExposure();}})}}.padding(16).backgroundColor(#1A000000).borderRadius(12)}}这段代码的逻辑核心是根据曝光模式动态切换 UI 控件。手动模式下只显示曝光时长滑块自动模式下显示曝光补偿滑块和锁定开关。这种设计避免了用户配置无效参数。常见问题问题 1setExposure 设置了值但画面没变化现象在手动曝光模式下调用session.setExposure(value)API 返回成功但预览画面亮度没有变化。原因这个 API 需要搭配正确的曝光模式才能生效。如果先调用了setExposureMode切换到手动模式但在后续操作中因为某些原因回调又切换回了自动模式比如restart()或重新配置 session那么设置的曝光时长会被忽略。解决方案每次配置曝光参数前先读取当前的曝光模式进行校验。functionsafeSetExposure(session:camera.PhotoSession,duration:number):void{constcurrentModesession.getExposureMode();if(currentMode!camera.ExposureMode.MANUAL){console.warn(当前不是手动曝光模式设置曝光时长无效);return;}session.setExposure(duration);}问题 2曝光锁定在页面返回后失效现象用户锁定曝光后按 Home 键返回桌面再回到应用或者页面发生路由跳转曝光锁定状态自动丢失。原因相机 session 在页面不可见时会被系统自动释放。页面重新可见后需要重新创建 session之前的状态全部丢失。解决方案在页面的生命周期回调中保存和恢复曝光锁定状态。StateprivatelockState:booleanfalse;privatesession:camera.PhotoSession|nullnull;onPageShow():void{// 页面显示时尝试恢复锁定状态if(this.lockStatethis.session){this.session.lockAutoExposure();}}onPageHide():void{// 页面隐藏时状态保存在 State 中}最佳实践先查能力再设置每次设置曝光参数前调用getSupportedExposureModes()、getSupportedExposureDurationRange()、getSupportedExposureBiasRange()获取设备能力。不同设备硬件差异大硬编码范围会导致 API 调用异常。曝光补偿值的步进对齐setExposureBias传参必须是对齐 step 的值。比如 step0.3 的设备传入 1.0 会取整为 0.9。建议在 UI 中直接限制用户滑动到对齐值。锁定状态与 UI 同步曝光锁定在 session 层面是一个一次性操作没有 state 概念。UI 的锁定开关状态必须手动维护不要在lockAutoExposure或unlockAutoExposure之后直接读取 session 状态来更新 UI。完整 Demo 入口// entry/src/main/ets/pages/ExposureDemo.etsimport{camera}fromkit.CameraKit;import{media}fromkit.MediaKit;EntryComponentstruct ExposureDemo{privatesession:camera.PhotoSession|nullnull;privatecameraManager:camera.CameraManager|nullnull;aboutToAppear():void{this.initCamera();}asyncinitCamera():Promisevoid{// 获取相机管理器实例this.cameraManagercamera.getCameraManager();// 获取相机设备constcameras:camera.CameraDevice[]this.cameraManager.getSupportedCameras();if(cameras.length0){console.error(没有找到摄像头);return;}// 创建会话this.sessionawaitthis.cameraManager.createCameraSession(camera.ControllerMode.PHOTO);awaitthis.session.beginConfig();awaitthis.session.addInput(cameras[0]);// 添加预览输出constpreviewOutputawaitthis.session.createPreviewOutput();awaitthis.session.addOutput(previewOutput);// 提交配置awaitthis.session.commitConfig();awaitthis.session.start();}build(){Stack(){// 相机预览流省略实际预览组件的实现XComponent({id:preview,type:XComponentType.SURFACE}).width(100%).height(100%)// 曝光控制面板ExposureControlPanel({session:this.session}).position({x:0,y:70%})}.width(100%).height(100%)}aboutToDisappear():void{this.session?.release();}}FAQQ为什么真机正常模拟器上曝光补偿值设置后画面不变化A模拟器对曝光硬件的模拟不完整特别是补偿值响应。建议真机调试曝光相关功能。Q为什么页面返回后曝光锁定失效了A相机 session 在页面不可见时被系统自动释放。重新创建 session 后之前锁定的状态不会自动恢复。需要在页面onPageShow回调中根据 UI 状态重新锁定。Q设置曝光补偿后预览画面亮度没有立即变化需要关闭重新打开 camera 吗A不需要。setExposureBias是实时生效的不需要重启 session。如果设置后 1-2 秒画面无变化检查传入的值是否在设备支持的范围内。另一个可能性是当前处于手动曝光模式补偿值不生效。