Zero-DCE是低光图像增强领域应用广泛的零参考深度曲线估计方法,它通过输入图像自身估计一组非线性曲线,不再依赖成对训练样本。实际部署中,增强结果常出现偏色,尤其是夜景、室内暖光或混合光源下,画面整体偏黄、偏绿,肤色不自然。表面上看是曲线强度过高,本质上可归结为两点:曲线对RGB三通道独立映射,缺少统一的色调约束;训练与推理阶段缺少显式白平衡校正。下面从色调映射和白平衡两个方向讨论解决办法。

要解决偏色,不能只靠降低增强幅度。Zero-DCE的深度曲线本身就是一种逐通道色调映射,它改变了每个像素的强度分布。如果不理解这种映射为什么会让色彩偏移,就很难针对性地设计修正策略。
一、Zero-DCE色彩偏差从哪里来
Zero-DCE将低光增强问题转化为像素级曲线估计。网络DCE-Net输入一张低光图像,输出24张参数图,对应8次曲线迭代和3个颜色通道。对于第n次迭代,曲线更新公式可以表示为LE_n(x) = LE_{n-1}(x) + α_n(x) LE_{n-1}(x)(1 - LE_{n-1}(x)),其中α_n(x)是与输入相关的曲线参数。初始值LE_0(x)就是输入图像本身。这个公式理论上可以单调映射像素值,并在训练中自约束增强强度。
色彩偏差的关键在于α_n(x)是RGB三通道各自独立的。同一个像素位置,红色通道和蓝色通道可能学到完全不同的拉伸量。对于原本接近白色的光源区域,如果红色曲线比蓝色曲线提升更多,结果就会偏红;反之则偏青或偏绿。在室内暖光场景中,蓝色通道原本就较弱,为了达到曝光目标,蓝色曲线可能被过度拉伸,暗部噪声和偏蓝现象会同时加重。
原Zero-DCE虽然设计了颜色恒常性损失,但它通常只约束增强结果R、G、B三个通道的全局均值尽量接近,形式类似于计算两两通道均值差的平方。这种约束偏弱,因为它不关心局部空间上的色相变化。一个图像可以整体均值接近灰色,但局部高光和阴影区域仍然出现明显偏色。而且灰度世界假设并不是对所有场景都成立,例如大面积草地或晚霞场景,强制把RGB均值拉平反而会带来新的色彩失真。
二、基于亮度通道的色调映射改进
减少偏色最直接的方法之一,是把Zero-DCE中逐通道的曲线映射改为对亮度通道单独映射。图像可以先从BGR或RGB转换到HSV、YUV、YCbCr等颜色空间,只对亮度分量V或Y应用曲线增强,色度信息保持不变。这样曲线只提升明暗,不直接改变色相和饱和度,偏色会明显减少。
下面的示例使用OpenCV将BGR图像转换到HSV空间,对V通道应用Zero-DCE风格曲线调整,再转回BGR。这样处理后的图像亮度提升,但H和S通道未参与曲线拉伸,色彩偏移受到抑制。
import cv2
import numpy as np
def enhance_luma(img_bgr, alpha_map):
# img_bgr 为 0-1 浮点图像,alpha_map 形状与亮度通道相同
img_bgr = img_bgr.astype(np.float32)
hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
v = v + alpha_map * v * (1.0 - v)
v = np.clip(v, 0.0, 1.0)
hsv = cv2.merge([h, s, v])
out_bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
return np.clip(out_bgr, 0.0, 1.0)
这种方法也意味着网络结构可以简化。原Zero-DCE每个迭代需要输出3个通道的曲线参数图,如果只增强亮度,则每个迭代只需输出1个参数图,8次迭代从24张参数图降为8张。计算量下降的同时,偏色风险减少。不过亮度映射不能解决原始图像已经存在的严重色偏,例如白炽灯下拍摄的图像本身整体偏黄。此时还需要配合白平衡校正。
亮度映射的另一个细节是色域裁剪。HSV空间中只提升V分量后,再转回BGR时,某些高饱和像素可能超出色域范围,导致颜色被裁剪。实际实现时可以在提升亮度后,对饱和度做轻微衰减,例如当V提升较多时,让S乘以一个略小于1的系数,减少颜色溢出风险。这样可以在亮度和自然色彩之间取得更好的平衡。
三、白平衡校正:训练约束与后处理
白平衡校正解决的是场景光源本身带来的颜色偏差。Zero-DCE训练时颜色恒常性损失可以看作一种隐式白平衡先验,但由于约束过于全局,效果有限。更实用的做法是在数据预处理或后处理中加入显式白平衡算法。灰度世界假设是最常用的方法,它假定自然场景的平均反射接近灰色,当图像RGB三通道均值明显偏离时,用增益系数把各通道拉回同一水平。
下面给出一个基于OpenCV的灰度世界白平衡实现。该示例以0到255的8位图像为输入,先计算B、G、R三个通道均值,再按灰度均值缩放每个通道。
import cv2
import numpy as np
def gray_world(img_bgr):
# 针对 0-255 的 BGR 图像进行灰度世界白平衡
img = img_bgr.astype(np.float32)
b, g, r = cv2.split(img)
b_mean = b.mean()
g_mean = g.mean()
r_mean = r.mean()
gray_mean = (b_mean + g_mean + r_mean) / 3.0
b = np.clip(b * (gray_mean / b_mean), 0.0, 255.0)
g = np.clip(g * (gray_mean / g_mean), 0.0, 255.0)
r = np.clip(r * (gray_mean / r_mean), 0.0, 255.0)
return cv2.merge([b, g, r])
白平衡和增强的顺序会影响最终效果。先白平衡再增强,可以让Zero-DCE曲线在颜色较中性的输入上工作,一般能减少增强后的偏色。先增强再白平衡,可以纠正增强过程中新引入的颜色偏移,但可能因为增强已经放大了某些通道噪声,白平衡容易把这些噪声进一步放大。实践上更推荐先做轻量白平衡,再执行亮度曲线增强。
如果场景不满足灰度世界假设,例如夜间霓虹灯场景,画面本身大面积偏某种颜色,灰度世界可能错误地中和掉环境氛围。此时可以引入完美反射算法,假设图像中最亮点应是白色,用它来估计光源色偏。还可以结合色温估计,把有明显色彩倾向的场景排除在白平衡校正之外,只对接近中性色的区域计算增益系数,从而保留必要的光源氛围。
四、联合优化策略与调试建议
在实际改进Zero-DCE时,建议同时从训练约束和推理后处理两个层面入手。训练阶段可以保留颜色恒常性损失,但将其权重适度降低,同时增加一个基于亮度通道的曲线输出。推理阶段如果仍发现轻微偏色,再用灰度世界做快速校正。这样的组合比单纯调高颜色恒常性损失更稳定,因为过强的颜色约束会破坏场景原有的色彩关系。
对于有条件的工程,可以设计一个轻量白平衡模块,用少量卷积层估计R、G、B增益系数,并与Zero-DCE网络联合训练。这种可学习白平衡比固定算法更灵活,能根据场景自动判断是否执行强校正。联合训练时,可以先冻结Zero-DCE部分,单独训练白平衡模块,再以较小学习率整体微调,避免曲线估计被白平衡模块干扰。
调试过程中需要关注几个关键信号。一是增强后高光区域是否出现颜色溢出现象,如果有,优先降低亮度曲线的上界或对饱和度做衰减。二是肤色是否自然,肤色对偏色非常敏感,可以选取典型人脸样本做主观评估。三是暗部颜色噪声是否加重,这说明某个通道在暗区被过度拉伸,可以增加光照平滑损失或限制曲线参数范围。通过迭代调整曲线映射方式和白平衡顺序,Zero-DCE也能在保持低光细节提升能力的同时,输出更干净的色彩表现。