

上图展示了MBLLEN算法的增强效果。第一张图表示的是MBLLEN算法在一些公有数据集上面和其它算法的比较结果,通过观察可以得知,该算法能够获得更加自然的增强效果,增强后的图像的整体效果看起来比较舒服,增强之后的细节更加清晰。第二张图表示的是该算法在几张真实的文档图像上面的增强效果,增强之后的图片中仿佛进入了阳光,整个图片都变得亮堂了很多,但是在有些情况下,该算法输出的结果有点过曝光的效果,幸运的是该算法还可以输出一个较低亮度的结果,具体的结果留给你自己去做测试。
论文链接-Github链接
在暗光条件下拍摄的图像经常(部分)能见度低。除了不满意的灯光,多种类型的退化,如由于相机质量的限制而产生的噪声和颜色失真,隐藏在黑暗中。换句话说,
仅仅提高暗区的亮度必然会放大隐藏的伪影。KinD算法是一个简单高效的网络,它是受到Retinex的启发,将原始的图像分解为两个部分。KinD将原始的图像空间分解为两个比较相似的子空间,该算法使用在不同曝光程度的图片块来进行训练,该算法对严重的视觉缺陷具有很强的鲁棒性,并且用户友好地任意调整光照水平。另外,我们的模型在2080ti GPU上处理一个VGA分辨率处理图像的时间不到50ms。

上图展示了KinD算法的整体网络架构。整个网络的架构和RetinexNet很相似,整个网络包括Decomposition-Net、Adjustment-Net和Restoration-Net,Decomposition-Net子网络的作用是对输入的图像进行分解,网络结构方面和RetinexNet稍微有一些不同,但是主要的作用是相同的;Adjustment-Net用来调节光照,该子网络由几个卷积层组成,该网络不执行去噪操作;Restoration-Net子网络的作用是通过组合反射率图像和光照图像形成最终的增强图像,该子网络是一个带有残差连接的编解码网络,具体的细节如上图所示。
import tensorflow as tf
import tensorflow.contrib.slim as slim
from tensorflow.contrib.layers.python.layers import initializers
def lrelu(x, trainbable=None):
return tf.maximum(x*0.2,x)
def upsample_and_concat(x1, x2, output_channels, in_channels, scope_name, trainable=True):
with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
pool_size = 2
deconv_filter = tf.get_variable('weights', [pool_size, pool_size, output_channels, in_channels], trainable= True)
deconv = tf.nn.conv2d_transpose(x1, deconv_filter, tf.shape(x2) , strides=[1, pool_size, pool_size, 1], name=scope_name)
deconv_output = tf.concat([deconv, x2],3)
deconv_output.set_shape([None, None, None, output_channels*2])
return deconv_output
def DecomNet_simple(input):
'''分解网络实现函数'''
with tf.variable_scope('DecomNet', reuse=tf.AUTO_REUSE):
conv1=slim.conv2d(input,32,[3,3], rate=1, activation_fn=lrelu,scope='g_conv1_1')
pool1=slim.max_pool2d(conv1, [2, 2], stride = 2, padding='SAME' )
conv2=slim.conv2d(pool1,64,[3,3], rate=1, activation_fn=lrelu,scope='g_conv2_1')
pool2=slim.max_pool2d(conv2, [2, 2], stride = 2, padding='SAME' )
conv3=slim.conv2d(pool2,128,[3,3], rate=1, activation_fn=lrelu,scope='g_conv3_1')
up8 = upsample_and_concat( conv3, conv2, 64, 128 , 'g_up_1')
conv8=slim.conv2d(up8, 64,[3,3], rate=1, activation_fn=lrelu,scope='g_conv8_1')
up9 = upsample_and_concat( conv8, conv1, 32, 64 , 'g_up_2')
conv9=slim.conv2d(up9, 32,[3,3], rate=1, activation_fn=lrelu,scope='g_conv9_1')
conv10=slim.conv2d(conv9,3,[1,1], rate=1, activation_fn=None, scope=
'g_conv10')
R_out = tf.sigmoid(conv10)
l_conv2=slim.conv2d(conv1,32,[3,3], rate=1, activation_fn=lrelu,scope='l_conv1_2')
l_conv3=tf.concat([l_conv2, conv9],3)
l_conv4=slim.conv2d(l_conv3,1,[1,1], rate=1, activation_fn=None,scope='l_conv1_4')
L_out = tf.sigmoid(l_conv4)
return R_out, L_out
def Restoration_net(input_r, input_i):
'''重建网络实现函数'''
with tf.variable_scope('Restoration_net', reuse=tf.AUTO_REUSE):
input_all = tf.concat([input_r,input_i], 3)
conv1=slim.conv2d(input_all,32,[3,3], rate=1, activation_fn=lrelu,scope='de_conv1_1')
conv1=slim.conv2d(conv1,32,[3,3], rate=1, activation_fn=lrelu,scope='de_conv1_2')
pool1=slim.max_pool2d(conv1, [2, 2], padding='SAME' )
conv2=slim.conv2d(pool1,64,[3,3], rate=1, activation_fn=lrelu,scope='de_conv2_1')
conv2=slim.conv2d(conv2,64,[3,3], rate=1, activation_fn=lrelu,scope='de_conv2_2')
pool2=slim.max_pool2d(conv2, [2, 2], padding='SAME' )
conv3=slim.conv2d(pool2,128,[3,3], rate=1, activation_fn=lrelu,scope='de_conv3_1')
conv3=slim.conv2d(conv3,128,[3,3], rate=1, activation_fn=lrelu,scope='de_conv3_2')
pool3=slim.max_pool2d(conv3, [2, 2], padding='SAME' )
conv4=slim.conv2d(pool3,256,[3,3], rate=1, activation_fn=lrelu,scope='de_conv4_1')
conv4=slim.conv2d(conv4,256,[3,3], rate=1, activation_fn=lrelu,scope='de_conv4_2')
pool4=slim.max_pool2d(conv4, [2, 2], padding='SAME' )
conv5=slim.conv2d(pool4,512,[3,3], rate=1, activation_fn=lrelu,scope='de_conv5_1')
conv5=slim.conv2d(conv5,512,[3,3], rate=1, activation_fn=lrelu,scope='de_conv5_2')
up6 = upsample_and_concat( conv5, conv4, 256, 512, 'up_6')
conv6=slim.conv2d(up6, 256,[3,3], rate=1, activation_fn=lrelu,scope='de_conv6_1')
conv6=slim.conv2d(conv6,256,[3,3], rate=1, activation_fn=lrelu,scope='de_conv6_2')
up7 = upsample_and_concat( conv6, conv3, 128, 256, 'up_7' )
conv7=slim.conv2d(up7, 128,[3,3], rate=1, activation_fn=lrelu,scope='de_conv7_1')
conv7=slim.conv2d(conv7,128,[3,3], rate=1, activation_fn=lrelu,scope='de_conv7_2')
up8 = upsample_and_concat( conv7, conv2,
64, 128, 'up_8' )
conv8=slim.conv2d(up8, 64,[3,3], rate=1, activation_fn=lrelu,scope='de_conv8_1')
conv8=slim.conv2d(conv8,64,[3,3], rate=1, activation_fn=lrelu,scope='de_conv8_2')
up9 = upsample_and_concat( conv8, conv1, 32, 64, 'up_9' )
conv9=slim.conv2d(up9, 32,[3,3], rate=1, activation_fn=lrelu,scope='de_conv9_1')
conv9=slim.conv2d(conv9,32,[3,3], rate=1, activation_fn=lrelu,scope='de_conv9_2')
conv10=slim.conv2d(conv9,3,[3,3], rate=1, activation_fn=None, scope='de_conv10')
out = tf.sigmoid(conv10)
return out
def Illumination_adjust_net(input_i, input_ratio):
'''光照调节网络实现函数'''
with tf.variable_scope('Illumination_adjust_net', reuse=tf.AUTO_REUSE):
input_all = tf.concat([input_i, input_ratio], 3)
conv1=slim.conv2d(input_all,32,[3,3], rate=1, activation_fn=lrelu,scope='en_conv_1')
conv2=slim.conv2d(conv1,32,[3,3], rate=1, activation_fn=lrelu,scope='en_conv_2')
conv3=slim.conv2d(conv2,32,[3,3], rate=1, activation_fn=lrelu,scope='en_conv_3')
conv4=slim.conv2d(conv3,1,[3,3], rate=1, activation_fn=lrelu,scope='en_conv_4')
L_enhance = tf.sigmoid(conv4)
return L_enhance


上图展示了KinD算法的增强效果。第一张图像表示的是该算法和其它经典的增强算法之间的比较结果,通过观察我们可以发现,通过该算法增强后的图像的更加明亮,看起来更加逼真,色彩基本上都正常的复原了。第二张图像表示的是该算法在几张真实的文档图片上面的增强效果,除了在第四列图像上面有一点问题,在其它图像上面都展现出来较好的增强效果。除此之外,需要提到的是该算法的运行速度并不快,2080Ti上面都需要50ms,都达不到实时,速度还待改进,作者出了一个改进版的KinD++,具体的细节的效果请看该链接。
论文链接-Github链接
EnlightenGAN算法是一个高效的无监督生成对抗网络,它不需要使用低光照/正常图像块训练,大量实验结果表明该算法可以在很多测试图片中取得较好的泛化效果。该算法使用从输入本身提取的信息来正则化非配对训练,论文中提出了一个全局-局部鉴别器结构、一个自正则感知损失融合与注意力机制。总而言之,这个算法开启了无监督图像增强的先河,在训练使用非配对的训练块,而且具有很好的泛化效果,它推动了GAN在图像增强问题上面的应用。

上图展示了EnlightenGAN网络的整体架构。整个网络包括一个生成器和一个判别器,该生成器是一个带有残差连接的编解码网络,每一个卷积块包含两个3x3的卷积、一个BN和一个LeakyRelu,每一个注意力模块是有特征映射和注意力映射相乘获得的;该网络包含一个全局判别器和一个局部判别器,全局判别器的输入是整张图片,它用来判别输入的图片是没有增强的还是增强过的,局部判别器的输入是一些patch块,用来判断这些patch是增强之后还是没有增强的。
def define_G(input_nc, output_nc, ngf, which_model_netG, norm='batch', use_dropout=False, gpu_ids=[], skip=False, opt=None):
'''生成器网络代码实现'''
netG = None
use_gpu = len(gpu_ids) > 0
norm_layer = get_norm_layer(norm_type=norm)
if use_gpu:
assert(torch.cuda.is_available())
if which_model_netG == 'resnet_9blocks':
netG = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=9, gpu_ids=gpu_ids)
elif which_model_netG == 'resnet_6blocks':
netG = ResnetGenerator(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=6, gpu_ids=gpu_ids)
elif which_model_netG == 'unet_128':
netG = UnetGenerator(input_nc, output_nc, 7, ngf, norm_layer=norm_layer, use_dropout=use_dropout, gpu_ids=gpu_ids)
elif which_model_netG == 'unet_256':
netG = UnetGenerator(input_nc, output_nc, 8, ngf, norm_layer=norm_layer, use_dropout=use_dropout, gpu_ids=gpu_ids, skip=skip, opt=opt)
elif which_model_netG == 'unet_512':
netG = UnetGenerator(input_nc, output_nc, 9, ngf, norm_layer=norm_layer, use_dropout=use_dropout, gpu_ids=gpu_ids, skip=skip, opt=opt)
elif which_model_netG == 'sid_unet':
netG = Unet(opt, skip)
elif which_model_netG == 'sid_unet_shuffle':
netG = Unet_pixelshuffle(opt, skip)
elif which_model_netG == 'sid_unet_resize':
netG = Unet_resize_conv(opt, skip)
elif which_model_netG == 'DnCNN':
netG = DnCNN(opt, depth=17, n_channels=64, image_channels=1, use_bnorm=True, kernel_size=3)
else:
raise NotImplementedError('Generator model name [%s] is not recognized' % which_model_netG)
if len(gpu_ids) >= 0:
netG.cuda(device=gpu_ids[0])
netG = torch.nn.DataParallel(netG, gpu_ids)
netG.apply(weights_init)
return netG
def define_D(input_nc, ndf, which_model_netD,
n_layers_D=3, norm='batch', use_sigmoid=False, gpu_ids=[], patch=False):
'''判别器网络代码实现'''
netD = None
use_gpu = len(gpu_ids) > 0
norm_layer = get_norm_layer(norm_type=norm)
if use_gpu:
assert(torch.cuda.is_available())
if which_model_netD == 'basic':
netD = NLayerDiscriminator(input_nc, ndf, n_layers=3, norm_layer=norm_layer, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids)
elif which_model_netD == 'n_layers':
netD = NLayerDiscriminator(input_nc, ndf, n_layers_D, norm_layer=norm_layer, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids)
elif which_model_netD == 'no_norm':
netD = NoNormDiscriminator(input_nc, ndf, n_layers_D, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids)
elif which_model_netD == 'no_norm_4':
netD = NoNormDiscriminator(input_nc, ndf, n_layers_D, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids)
elif which_model_netD == 'no_patchgan':
netD = FCDiscriminator(input_nc, ndf, n_layers_D, use_sigmoid=use_sigmoid, gpu_ids=gpu_ids, patch=patch)
else:
raise NotImplementedError('Discriminator model name [%s] is not recognized' %
which_model_netD)
if use_gpu:
netD.cuda(device=gpu_ids[0])
netD = torch.nn.DataParallel(netD, gpu_ids)
netD.apply(weights_init)
return netD


上图展示了EnlightenGAN算法的增强效果。第一张证明了EnlightenGAN论文中提出的局部判别器和注意力模块的有效性,同时也体现出了EnlightenGAN算法的增强效果。第二张展示了该算法在真实场景中的一些图片上面的展示效果,整体看来该算法取得了较好的增强效果,但是在有些图像上面增强的结果不是很均匀,仍然存在着一些阴影区域,不过越来越多的基于GAN的图像增强算法会慢慢的解决这些问题,敬请期待。
本文主要对几种经典的低光照图像算法进行了简单的介绍,但是并不代表当前只有这么多算法,我介绍的可能只是冰山一角,如果你对低光照图像增强感兴趣,请参考参考文献中的一些论文和资料。总而言之,低光照图像增强算法在现实场景中会有很多的应用,比如低光照条件下面的检测、跟踪、行人重识别;通过图像增强来提升文本检测和文本识别的精度,更多的场景还需要你自己去挖掘和应用。
1、Learning-to-See-in-the-Dark-论文链接-Github链接-项目主页
2、DPED-论文链接-Github链接-项目主页
3、noteshrink-Github链接
4、SICE-论文链接Github链接
5、Attention-guided Low-light Image Enhancement-论文链接
6、部分低光照资料汇总-Github链接
[1] 如果您对AI、自动驾驶、AR、ChatGPT等技术感兴趣,欢迎关注我的微信公众号“AI产品汇”,有问题可以在公众号中私聊我!
[2] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:1575262785@qq.com),我会在第一时间回复大家,谢谢大家的关注.
[3] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[4] 如果您在阅读本博客时遇到不理解的地方,希望您可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[5] 本文测试的图片可以通过该链接进行下载。网盘链接- 提取码:x7ta。
[5] 本人业余时间承接各种本科毕设设计和各种小项目,包括图像处理(数据挖掘、机器学习、深度学习等)、matlab仿真、python算法及仿真等,有需要的请加QQ:1575262785详聊,备注“项目”!!!
在
低照度场景下进行目标检测任务,常存在图像RGB特征信息少、提取特征困难、目标识别和定位精度
低等问题,给检测带来一定的难度。
使用
图像增强模块对原始图像进行画质提升,恢复各类图像信息,再使用目标检测网络对增强图像进行特定目标检测,有效提高检测的精确度。
本资源包含传统方法、Retinex、EnlightenGAN、SCI、Zero-DCE、IceNet、RRDNet、URetinex-Net等
低照度
图像增强代码,均已经过测试,可直接运行。
之前在做光照对于高层视觉任务的影响的相关工作,看了不少基于深度学习的低光照增强(low-light enhancement)的文章[3,4,5,7,8,9,10],于是决定简单梳理一下。
光照估计(illumination estimation)和低光照增强(low-light enhancement)的区别:光照估计是一个专门的底层视觉任务(例如[1,2,6]),它的输出结果可以被用到其它任务...