• 基于AI的3DLUT系列论文分享


    目录

    简介

    AI3DLUT

    AdaInt

    SeqLUT

     4D LUT

    CLUT-Net

    总结


    简介

             在图像处理中,经常会用到3DLUT来做颜色增强或者色域映射,3DLUT插值部分基本都是硬件处理,但难点在于如何生成对应的3DLUT表。目前,工程上基本上都是静态表,就是固定几张3DLUT,然后根据某些变量来选表,选邻近表进行插值,得到需要的3DLUT,然后配置到硬件,比如ISP通路里,一般是根据色温或者光源类型,照度等信息,配置不同的3DLUT,然后插值得到需要3DLUT。显示通路里,一般就是配置某些色域转换的3DLUT,当输入不同色域图片时,都转到屏幕对应的色域,这个过程基本上选固定表即可,一般不用插值,除非两种情况过渡时。

            3DLUT相关介绍这里就不展开讲,可以参考        三维查找表3DLUT_图像算法菜鸟的博客-CSDN博客_3dlut三维查找表简称3DLUT,是一个对应数值的列表,可以通过它查询任何输入值及其所对应的输出值,是色彩转换技术中常用的一种技术。其核心思想是,将源色彩空间进行分割,划分为一个个规则的立方体,每个立方体的八个顶点的数据是己知的,将所有源空间的已知点构成一张三维查找表。一般就是把RGB三个通道分别等分为N个顶点,组成NxNxN个节点,在使用时,其他节点就直接插值得到,常用的有9x9x9,17x17x17,33x33x33,65x65x65等。在ISP算法和显示算法里,一般都是用17x17x17。...https://blog.csdn.net/zhognsc08/article/details/122788033?spm=1001.2014.3001.5502        而近几年,随着AI技术的应用,出现了不少基于AI的3DLUT生成算法,基本上都是图像自适应的,而不是静态的了,下面就分享几篇3DLUT相关的论文,主要是用来做图像增强,包括颜色和亮度。

    AI3DLUT

    论文名称:Learning Image-adaptive 3D Lookup Tables for High Performance Photo Enhancement in Real-time

    论文地址:https://arxiv.org/pdf/2009.14468v1.pdf

    代码地址:https://github.com/HuiZeng/Image-Adaptive-3DLUT

            这篇论文是2020年发表在IEEE上,算是比较早的用AI来做3DLUT的。

            论文的思想是,学出N个Basis 3DLUTs, 网络每次只要输出对应的N个权重,对N个3DLUTs进行加权求和,得到一个3DLUT,这样就可以做到图像自适应,最后进行3DLUT插值,得到输出图像,即按(5)式计算,而不是按(4)式那样通过3DLUTs插值之后的图像进行加权,那样计算量较大。 由于网络前向时,只需要输出N个权重,网络可以做得很轻量,参数量不多600K,3DLUT插值也很快,对4K图,只需要2ms。 论文还支持paired数据和unpaired数据,unpaired数据时,使用了GAN loss,也不算创新,估计效果也一般,主要还是paired数据的情况。

    7ab66d440fd8424ca1d7855f7eb27994.png

    71e57a55874a49eab635f6b6d5d2519c.png

    e611a363e4b743d28606d6d7007fcc94.png

    网络很轻量,结构如下图所示。

    b34974abcd984a23b8b6aed7269b1fe2.png

             文章的loss如下,除了使用L2作为准确性loss,还加入了正则项loss,其中

            (11)式是平滑性loss,主要保证3DLUT邻近节点之间是平滑的,不存在突兀的情况。

            (12)式主要是为了限制权重不能太大

            (13)式是为了保证LUT的单调性

            其中(12)式要重点注意下,网络输出的是一组权重,对basis 3DLUT进行加权,但论文也没说这些权重和为1,实际上就是不为1,如果强制限制权重和为1,则可能出现两种情况,一种就是选择了某一张表,即其他权重为0,一个权重为1,另一种情况就是所有权重接近平均,取了所有3DLUT的平均。(12)式(代码里实际是可以单独控制权重和loss的权重,(11)式的loss单独控制,而不是包含在(12)式)的权重加大,则会出现第二种情况,权重较小就会出现第一种情况。

            由于权重和不为1,其实学出来的basis 3DLUTs也不标准,这里的判断3DLUT是否标准的原则是,把3DLUT作用到图像上,看图像是否正常,一般手调的3DLUT是标准的。学出来的N个3DLUT中,可能就1个接近标准,而其他都是不标准的,标准的那个权重一般会比较大,而不标准的,权重会小一些,这些不标准的就相当于delta 3DLUT,是用来微调的。

            当然,上述结论是在我自己的数据集上训练出来的,不是用的论文里说的数据,但好像也说的过去。

    da5431e6827d4d349926c0bfadff86f2.png

    82bb846b75664cd6ab53577a3227923c.png

     812a74350da446199fd8b48a48c037af.png

     8cde46b88c164fa9ae51857935784077.png

     152ba3192c774983b62f819b27f234b4.png

             作者提供了3DLUT插值部分的代码是C++ cuda代码,但3DLUT插值可以用系统自带的grid_sample函数实现,具体实现如下,其中align_corners=True是必须的。

    1. class Apply3DLUT(nn.Module):
    2. def __init__(self):
    3. super(Apply3DLUT, self).__init__()
    4. def forward(self, lutRGB, imgRGB):
    5. N, C, H, W = imgRGB.size()
    6. guide = imgRGB.permute(0, 2, 3, 1)
    7. guide = guide.view(N, 1, H, W, C)
    8. guide = guide[:, :, :, :, (2, 1, 0)] # RGB2BGR
    9. guide = guide * 2 - 1
    10. bins = lutRGB.size(2)
    11. grid = lutRGB.view(-1, 3, bins, bins, bins)
    12. out = F.grid_sample(grid, guide, padding_mode='border', align_corners=True)
    13. out = out.squeeze(2)
    14. return out

             正则项loss的实现作者也提供了,我认为写得不准确,很简单的判断方法就是,给定一个没有任何效果的3DLUT,这个肯定是满足平滑性和单调性的,那么对应的loss就应该为0,但作者提供的代码并不为0,我写得如下所示。

    1. class Loss3DLUT(nn.Module):
    2. def __init__(self, dim=17):
    3. super(Loss3DLUT, self).__init__()
    4. self.l2 = nn.MSELoss()
    5. self.weight_r = torch.ones(1, 3, dim, dim, dim - 1, dtype=torch.float)
    6. self.weight_r[:, :, :, :, (0, dim - 2)] *= 2.0
    7. self.weight_g = torch.ones(1, 3, dim, dim - 1, dim, dtype=torch.float)
    8. self.weight_g[:, :, :, (0, dim - 2), :] *= 2.0
    9. self.weight_b = torch.ones(1, 3, dim - 1, dim, dim, dtype=torch.float)
    10. self.weight_b[:, :, (0, dim - 2), :, :] *= 2.0
    11. self.relu = torch.nn.ReLU()
    12. self.nodeLen = 1 / (dim - 1)
    13. self.dim = dim
    14. def forward(self, lut):
    15. # LUT 1 3(rgb) dimR dimG dimB
    16. dif_b = lut[:, :, :, :, :-1] - lut[:, :, :, :, 1:]
    17. dif_g = lut[:, :, :, :-1, :] - lut[:, :, :, 1:, :]
    18. dif_r = lut[:, :, :-1, :, :] - lut[:, :, 1:, :, :]
    19. dif_b[:, 2, :, :, :] += self.nodeLen
    20. dif_g[:, 1, :, :, :] += self.nodeLen
    21. dif_r[:, 0, :, :, :] += self.nodeLen
    22. dif_b_mn = lut[:, 2, :, :, :-1] - lut[:, 2, :, :, 1:]
    23. dif_g_mn = lut[:, 1, :, :-1, :] - lut[:, 1, :, 1:, :]
    24. dif_r_mn = lut[:, 0, :-1, :, :] - lut[:, 0, 1:, :, :]
    25. nn = lut.size(0)
    26. weight_r = self.weight_r.repeat(nn, 1, 1, 1, 1)
    27. weight_g = self.weight_g.repeat(nn, 1, 1, 1, 1)
    28. weight_b = self.weight_b.repeat(nn, 1, 1, 1, 1)
    29. weight_r = weight_r.to(lut)
    30. weight_g = weight_g.to(lut)
    31. weight_b = weight_b.to(lut)
    32. tv = torch.mean(torch.mul((dif_r ** 2), weight_r)) + torch.mean(
    33. torch.mul((dif_g ** 2), weight_g)) + torch.mean(torch.mul((dif_b ** 2), weight_b))
    34. mn = torch.mean(self.relu(dif_r_mn)) + torch.mean(self.relu(dif_g_mn)) + torch.mean(self.relu(dif_b_mn))
    35. return tv, mn

             3DLUT虽然做到了图像自适应,但是全局操作,不具有局部处理能力,工程上一般都是用来处理颜色,处理亮度用1DLUT更加合适。3DLUT的输入要求为0~1的范围,这样基本上只能放到后处理模块上,所以一般在ISP通路里,也是放到了亮度调节模块之后ToneMapping,这个之后的数据基本上是0~1了,而再往前,多帧融合的结果基本上都是超过1的。论文是学出来一组权重,但权重和并不为1,这个就和实际工程中使用的有点差异,可调性和可解释性没那么强。

            在工程上,有时不希望3DLUT改变AWB的效果,因为如果改变了,二者就耦合了,调试时就分不清到底是谁不准确。3DLUT如果不动AWB,可以考虑再加入AWBloss,就是保证灰色点尽量还是灰色,大致代码如下。

    1. awbloss = 0
    2. for i in range(self.dim):
    3. rdiff = self.l2(lut[:, 0, i, i, i], lut[:, 1, i, i, i])
    4. bdiff = self.l2(lut[:, 2, i, i, i], lut[:, 1, i, i, i])
    5. awbloss += (rdiff + bdiff)

    AdaInt

    论文题目:AdaInt: Learning Adaptive Intervals for 3D Lookup Tables on Real-time Image Enhancement

    论文地址:https://arxiv.org/pdf/2204.13983.pdf

    代码地址:https://github.com/ImCharlesY/AdaInt

    bfa3368b813d48ae8d3ce5f7150a8c44.png

            这是CVPR2022的一篇论文。

            普通的3DLUT都是均匀间隔采样的,文章提出使用非均匀间隔采样的3DLUT,这样可以控制的更加精确,可以在感兴趣的区域划分的更细,控制的更加精准,比如人脸区域,在肤色范围,更多的节点来控制肤色,这样可以更加精准。

            相比AI3DLUT,网络除了输出权重,还需要输出3个1DLUT,用来控制非均匀的间隔采样。 由于3DLUT插值部分是非均匀间隔采样,不能调用系统函数,需要自己写3DLUT插值的代码,作者提供了C++ cuda代码,可以实现非均匀间隔采样的3DLUT

            文章中3DLUT使用的33个节点,1DLUT是32个节点,补了一个0。

            loss上,和AI3DLUT的基本类似。

    59edfa18a3df4e60ae34e171dfa4c0c1.png

    b58618ee6c5b4c35b6daacec5170d428.png

            效果上,对比AI3DLUT,效果提升了大概0.2db左右。

            RGB三个轴的采样也可以一样,不过文章分析了,三个一样的话,称之为shared-AdaInt,不相同的称为AdaInt。

            非均匀间隔采样和先经过3个1DLUT,再经过均匀的3DLUT插值,对于节点上的点来说,是可以做到一样的,完全等价的,但对于插值的点,二者还是有差别的。不过,如果先过3个1DLUT,再做3DLUT,应该也能达到类似的效果。

            由于是非均匀间隔采样,AdaInt可以适当处理超过0~1的输入图像,相当于把0~max的范围,映射到0~1的范围内,因为不是均匀采样,所以不是线性压缩,要比AI3DLUT有优势。 

    SeqLUT

    论文题目:SepLUT: Separable Image-adaptive Lookup Tables for Real-time Image

    Enhancement

    论文地址:https://arxiv.org/pdf/2207.08351.pdf

    6d78578a715246e29bb33b5571165d0e.png

                    

            这是ECCV2022一篇论文。

            SeqLUT使用3个1DLUT+1个3DLUT来做图像增强,图像先经过学出来的3个1DLUT,再经过一个均匀的3DLUT,相当于把AdaInt分两步来实现了。

            不同于AI3DLUT和AdaInt,SeqLUT是直接输出一个3DLUT,而不是学出一个权重。 backbone提取出图像特征,然后分两个分支,分别输出3个1DLUT和1个3DLUT,该过程是使用了全连接结构,参数量分别对应table 2和table 3,3DLUT分支,如果使用1层全连接结构,参数量为32m*3*St*St*St,参数量很大,作者为了做到参数量小一些,使用了2层结构,这样参数量变为K*(32m + 3*St*St*St),文章中取K取3或5,m=6或8,St取9或17,这样参数量只有50K(m=6, S0=St=9, ours-S)或者120K(m=8, S0=St=17, ours-S)左右,比AI3DLUT要少很多。

    e2ef591b4c324f85baffa9eac88b5f49.png

    a9437072c76e4e8ea3a3c5eb78a27704.png

    b7575e11cf5e48e287f76b8128fd4703.png

            从客观指标上看,SeqLUT的效果比AI3DLUT要好,差不多也是高0.2db左右,和AdaInt效果差不多。

            这里也证实了,虽然AdaInt和SeqLUT实现方式不一样,但都是3个1DLUT+1个3DLUT,达到的效果也是相当的,估计SeqLUT中,为了减少参数量,也牺牲了部分效果。

            在工程上,比如ISP通路里,3个1DLUT就相当于gamma变换,只是一般情况下,调试上是不会取调整gamma,会使用标准的gamma,SeqLUT相当于把gamma和3DLUT部分联合起来,使用AI的方法做到了图像自适应,实用性比AdaInt高些。

            论文里输出3DLUT是靠全连接实现的,这样参数量就会很大,作者想办法把参数量减下来了,也可以考虑不用全连接结构,可以把通道数变成St*St,即H*W*C,其中C=St*St,然后reshape成St*St*C’,其中C’=H*W,再经过卷积,把通道数降为3*St即可。

            loss上没有使用正则项loss,加上正则项loss,效果应该更加稳定。 

    2575513704da41be9814693d82329c63.png

    3033ac5bae364d428878d55cfd44373a.png

     4D LUT

    论文题目:4D LUT: Learnable Context-Aware 4D Lookup Table for Image Enhancement

    论文地址:https://arxiv.org/pdf/2209.01749.pdf

    cf5d3bc4d8e84eb59697470a8fa70525.png

    f352de788b214ce993f3e3d06476db37.png

            4DLUT不同于3DLUT,多加了一个context map通道,和RGB组合成4D,context map通道不完全等价于语言,并不需要监督,由网络自动学。同时,和AI3DLUT有点类似,有个Parameter Encoder分支,输出一组参数,训练时还会输出一组basis 4DLUTs,文章中取3个,即Nlut=3,但又不同于AI3DLUT,参数分为weight和bias,并不是只有weight,大小也不同,其中weight的大小为3N2lut,bias的大小为Nlut,然后按7式计算出4DLUT,再进行4DLUT插值,4DLUT的bin为33,4DLUT插值部分也是写c++ cuda代码实现。 

            Loss和AI3DLUT类似,也使用了平滑性loss,单调性loss,和权重限制loss,只是扩展到了4维 。

    19f487e24ab84eae9eea8450698f0ab0.png

    bc834137e8a14ab2a1b46ebbd3b716c8.png

    45c633d268634e779b4a478ca88d6905.png

    4da36cc38f4c44569a64b6cbbb07241d.png

     e92cb813b8ee44f480b0c019eea466fc.png

     098cf0843307409b95d1f542034902e0.png

             客观指标上,比AI3DLUT高0.3db左右,这里没有直接和SeqLUT和AdaInt对比,差不多比他们好一点点。

    a5aa6829e80d4986a75dc07c9e08bfcd.png

    39bc96d6c1894c5e9af926e15738e5bc.png

    90d449b71570474b9b820fcb90b61eab.png

            4DLUT由于多了空间上的一个维度,可以处理局部,空间上,不同区域,可以使用不同的3DLUT来做增强,比如蓝天和阴影下,有时阴影会偏蓝,如果把蓝天调的更蓝,那么阴影偏蓝就会加重,而4DLUT可以让蓝天更蓝,而阴影区域可以减蓝,相当于可以实现局部的AWB效果。 

    CLUT-Net

    论文题目:CLUT-Net: Learning Adaptively Compressed Representations of 3DLUTs for Lightweight Image Enhancement

    论文地址:https://cslinzhang.gitee.io/home/ACMMM2022/fengyi.pdf

    852c563820ac474d952a7ab02368cb6b.png

    7720513913874eac93f6f19de3c17643.png

    818f183871b147a7844e4ae1c82da724.png

    cfb4ed2a74614d81b7aa2a237476471e.png

    e36c4b7b6a3d43fda44d024e833ecd75.png

     02a6e5824ff440e7b6ceaebaa746b49a.png

            这是ACM-MM2022的一篇论文 。

            CLUT-Net主要是针对AI3DLUT那篇论文,从图上也看的出来,二者很接近,主要是认为AI3DLUT网络输出的参数量太大了,只考虑3DLUT部分的参数量,AI3DLUT的参数量为N*3*D*D*D,其中N为basis 3DLUTs的数目,D为3DLUT的节点数目,文章把N个Basis CLUTs,大小为3*S*W,1个Ms矩阵,大小为D*S,1个Mw,大小为W*D*D,对应为Comprehension-adaptive Transformation Matrices模块,按(1)式,把三者乘起来,就可以得到N个3DLUT,对应为Reconstruction模块。可以看到,这样就把N*3*D*D*D的参数量变成了N*3*S*W+D*S+W*D*D大小的参数量,满足S<

            虽然论文确实用更少的参数实现了N个3DLUT,但文章缺乏严格的数学证明,能否把N个3DLUT,拆分乘N个3*S *W的矩阵+Ms矩阵+Mw矩阵,也没提供拆分的方法,拆分之后二者能否严格相等,如果拆不出来,或者不能严格相等,那么意义就没那么大了,如果能等价拆出来,那在工程上就有很大的意义。

            实际工程中,3DLUT插值部分基本上都是硬化到了芯片,由硬件来处理,真正的瓶颈是把一个3DLUT灌给硬件时,参数量较大,一般3DLUT都是用的17个节点,如果33个节点,或者4DLUT,那就会很大,如果能够把3DLUT等价拆分成几个小矩阵,那么参数就可以减少很多,意义就很大,而AI3DLUT网络中,输出参数大,这些都是软件操作,参数量大应该不是瓶颈。

    总结

            这几篇论文在工程上还是具有较大的参考意义,正则项loss,非均匀间隔采样,以及4DLUT和参数压缩,在不同的具体工程上,应该有借鉴意义。论文之间也有一定的继承性和扩展性。

             

  • 相关阅读:
    Ubuntu安装docker及docker-compose
    Android Framework实战:AMS HOOK实现集中登陆
    Redis的内存淘汰策略(简单版)
    基于BP神经网络和小波变换特征提取的烟草香型分类算法matlab仿真,分为浓香型,清香型和中间香型
    visual studio 2019中文乱码
    10、MySQL-索引
    c++数组的 堆初始化方式
    【算法】使数组有序的最小交换次数
    idea技巧--debug使用技巧
    WebRTC REMB 算法
  • 原文地址:https://blog.csdn.net/zhognsc08/article/details/127043755