3、亮度调节核心算法:基于Lux的线性映射、对数映射与指数映射、Gamma校正与视觉感知
好,咱们进入正题。
上一章我们聊了环境光传感器怎么采集数据,怎么滤波。但拿到一个干净的Lux值之后,怎么把它变成屏幕亮度?
这就是算法要做的事。
说白了,就是把一个物理量(环境光)映射到另一个物理量(屏幕亮度)。这个映射关系,决定了用户的眼睛舒不舒服。
3.1 线性映射:最直接,但最不好用
线性映射,公式很简单:
ScreenLux = k * AmbientLux + b
k是增益,b是偏移量。
举个例子:环境光从0变到10000 Lux,屏幕亮度从10 nit线性增加到500 nit。听起来很合理对吧?
但我在项目中踩过这个坑。
有一次,客户反馈说:“车子从地库开出来,屏幕突然亮得刺眼。” 我一看日志,环境光从50 Lux瞬间跳到8000 Lux,屏幕亮度直接从20 nit跳到400 nit。线性映射嘛,一步到位。
用户的眼睛根本来不及适应。
线性映射适合做基准测试,但不适合直接用在产品里。除非你的屏幕亮度范围特别窄,比如只有0-100 nit,那还能凑合用。
3.2 对数映射与指数映射:更接近人眼
人眼对亮度的响应,其实是对数关系。你想想看,在暗处,一点点光变化就很明显;在亮处,几百Lux的变化你可能都感觉不到。
对数映射的公式:
ScreenLux = a * log(AmbientLux + 1) + b
加1是为了防止AmbientLux为0时取对数出错。
这个映射的好处是:低光区变化平缓,高光区变化也平缓。整体曲线像是一个“S”形的前半段。
我个人的习惯是,在车载场景下,优先用对数映射。为什么呢?
- 地库到地面:亮度变化不会太突兀
- 隧道出口:屏幕不会突然亮瞎眼
- 夜间行车:屏幕亮度足够低,不干扰驾驶
指数映射则相反:
ScreenLux = a * exp(AmbientLux / c) + b
这个映射会让亮度变化在低光区很慢,高光区很快。说实话,在车载场景下用得不多。除非你想做“强光下屏幕特别亮”的效果,但那样功耗也上去了。
3.3 Gamma校正:让屏幕“说人话”
这里有个容易混淆的点。
屏幕本身的亮度响应也是非线性的。你给屏幕一个50%的电压,它不会输出50%的亮度,而是大概22%左右。这就是Gamma特性。
Gamma校正,就是提前把亮度值“扭曲”一下,让最终输出符合预期。
公式:
Output = Input ^ (1 / Gamma)
标准Gamma值通常是2.2。所以校正公式就是:
Output = Input ^ (1 / 2.2)
举个例子:
| 输入亮度(归一化) | 未校正输出 | Gamma校正后输出 |
|---|---|---|
| 0.5 | 0.22 | 0.73 |
| 0.8 | 0.55 | 0.90 |
你看,校正后输出值变大了。这样屏幕才能显示出正确的亮度。
我曾经遇到过一个Bug:算法算出来目标亮度是200 nit,但屏幕显示出来只有150 nit。查了半天,发现是Gamma校正没做。嗯,从那以后,我把Gamma校正写进了算法流程的固定步骤里。
3.4 视觉感知:算法最终要服务眼睛
算法再复杂,最终是给人看的。所以必须考虑人眼的视觉特性。
几个关键点:
- 亮度适应时间: 人眼从暗到亮需要几秒,从亮到暗需要几十秒。算法不能瞬间跳变,要加平滑过渡。我一般用一阶低通滤波,时间常数设1-3秒。
- 对比度敏感度: 人眼对中频对比度最敏感。所以屏幕上的文字、图标,在中等亮度下最清晰。太暗或太亮都会降低可读性。
- 色温影响: 亮度变化时,色温也要跟着变。这个我们下一章细讲。
我个人习惯在算法里加一个“视觉舒适度”指标。比如:
舒适度 = 1 - |目标亮度 - 当前亮度| / 最大亮度范围
这个值越接近1,说明亮度变化越平缓,用户越舒服。
3.5 算法流程总结
好,我们把整个流程串一下:
- 获取滤波后的环境光Lux值
- 用对数映射(或混合映射)计算目标亮度
- 对目标亮度做平滑滤波(一阶低通)
- 对平滑后的亮度做Gamma校正
- 输出到屏幕驱动
代码示例(伪代码):
float ambientLux = getFilteredLux();
float targetBrightness = logMapping(ambientLux);
float smoothedBrightness = lowPassFilter(targetBrightness, 0.3);
float gammaCorrected = pow(smoothedBrightness, 1.0 / 2.2);
setScreenBrightness(gammaCorrected);
嗯,就这么简单。但每一步都有坑,每一步都需要调参。
下一章,我们聊聊色温调节。那个更麻烦,因为涉及到白平衡和人的主观感受。