专题介绍
在低光照环境下,传统成像设备往往因画面昏暗、细节丢失而受限。LLIE(低照度暗光增强)技术应运而生,它通过提升图像亮度、对比度,减少噪点并恢复色彩细节,让暗夜变得清晰可见。
LLIE技术从传统方法如直方图均衡化、Retinex模型等起步,近年来借助深度学习,尤其是卷积神经网络(CNN),GAN模型,扩散模型实现了质的飞跃。这些算法能自动学习图像特征,精准处理低光照图像,效果显著优于传统技术。
本专题将聚焦LLIE技术的核心原理、应用案例及最新进展,让我们一起见证LLIE如何点亮暗夜,开启视觉新视界!欢迎一起探讨交流!
无监督轻量型暗光增强算法-ZeroDCE(2020,CVPR)
本文将对 ZeroDCE: Zero-Reference Deep Curve Estimation for Low-Light Image Enhancement,这篇暗光增强算法进行讲解。参考资料如下:
[1].[ZeroDCE论文地址]
[2].[ZeroDCE代码地址]
一、研究背景
低光照条件下的图像增强对于提高图像的视觉质量以及计算机视觉系统在极端光照条件下的性能至关重要。然而,传统的低光照图像增强方法通常需要大量的配对或未配对数据进行训练,这在实际应用中往往难以获取。此外,现有的一些基于深度学习的方法在处理复杂光照条件时存在过拟合和计算效率低下的问题。因此,研究一种无需参考图像的低光照图像增强方法具有重要的理论和实际意义。
二、ZeroDCE方法
ZeroDCE的框架图如下:

Zero-DCE是一种新型的低光照图像增强方法,其核心思想是将光照增强任务转化为图像特定曲线(LE-curve)的估计问题。该方法通过训练一个轻量级的深度网络(DCE-Net)来估计像素级的高阶曲线(网络为每个像素点估计α值,通过α值来构造高阶曲线),从而实现对输入图像动态范围的调整。该曲线具有以下特点:
1.像素值范围限制:确保增强后的图像像素值在归一化的 [0,1] 范围内,避免信息丢失。
2.单调性:保持相邻像素的差异(对比度)。
3.可微性:便于通过深度卷积神经网络进行参数调整。
为了达到以上三个目标,文中设计了一个二次方曲线:
L E ( I ( x ) ; α ) = I ( x ) + α I ( x ) ( 1 − I ( x ) ) LE(I(x);α)=I(x)+αI(x)(1-I(x)) LE(I(x);α)=I(x)+αI(x)(1−I(x))
其中,x为像素坐标; LE(I(x);α)是输入I(x)的增强版本; α∈[−1,1]是曲线的可训练参数,用于调整LE-curve的级数以及曝光度。每个像素值都在[0,1]之间,每个运算都是像素层面上的。使用时,在输入的RGB通道分别应用LE-Curve,这可以更好地保持固有颜色。下图是不同α值的LE-curve示意图:

可以看到设计的曲线可以很好地满足上述的三个要求。此外,该曲线还可以叠加使用,实现更灵活的调整,以应对具有挑战性的低光条件。
L E n ( x ) = L E n − 1 ( x ) + α n L E n − 1 ( x ) ( 1 − L E n + + 1 ( x ) ) L E_{n}(\mathbf{x})=L E_{n-1}(\mathbf{x})+\alpha_{n} L E_{n-1}(\mathbf{x})\left(1-L E_{n++1}(\mathbf{x})\right) LEn(x)=LEn−1(x)+αnLEn−1(x)(1−LEn++1(x))
其中,n是迭代的次数,文中将迭代次数设置为8,可以满足大多数情况。当n为1时,上式就退化为了基本形式。下图是n为4时候曲线形态,可以看出,n增大后曲线调节能力更强。

DCE-Net 网络结构:DCE-Net 是一个轻量级的深度网络,用于估计最佳拟合的 LE 曲线。输入为low-light图像,输出为一组用于高阶曲线的pixel-wise curve parameter maps(上文提到的α值)。
DCE-Net由7个具有对称结构的卷积层组成(类似于U-Net)
- 前6层的卷积核为(3x3x32,stride=1)然后接一个ReLU激活,抛弃了down-sampling和bn层(作者认为这会破坏领域像素间的关系);
- 最后一层卷积通道为24(用于8个迭代轮次的parameter maps,也就是为每个像素点估计不同的α值),接一个Tanh激活函数。
其网络结构代码如下所示:
class enhance_net_nopool(nn.Module):
def __init__(self):
super(enhance_net_nopool, self).__init__()
self.relu = nn.ReLU(inplace=True)
number_f = 32
self.e_conv1 = nn.Conv2d(3,number_f,3,1,1,bias=True)
self.e_conv2 = nn.Conv2d(number_f,number_f,3,1,1,bias=True)
self.e_conv3 = nn.Conv2d(number_f,number_f,3,1,1,bias=True)
self.e_conv4 = nn.Conv2d(number_f,number_f,3,1,1,bias=True)
self.e_conv5 = nn.Conv2d(number_f*2,number_f,3,1,1,bias=True)
self.e_conv6 = nn.Conv2d(number_f*2,number_f,3,1,1,bias=True)
self.e_conv7 = nn.Conv2d(number_f*2,24,3,1,1,bias=True)
self.maxpool = nn.MaxPool2d(2, stride=2, return_indices=False, ceil_mode=False)
self.upsample = nn.UpsamplingBilinear2d(scale_factor=2)
def forward(self, x):
x1 = self.relu(self.e_conv1(x))
x2 = self.relu(self.e_conv2(x1))
x3 = self.relu(self.e_conv3(x2))
x4 = self.relu(self.e_conv4(x3))
x5 = self.relu(self.e_conv5(torch.cat([x3,x4],1)))
x6 = self.relu(self.e_conv6(torch.cat([x2,x5],1)))
x_r = F.tanh(self.e_conv7(torch.cat([x1,x6],1)))
r1,r2,r3,r4,r5,r6,r7,r8 = torch.split(x_r, 3, dim=1)


2596

被折叠的 条评论
为什么被折叠?



