这篇文章是2021年CVPR会议论文,核心思想是用CNN网络训练一个LUT-Based的超分方法;由于表中存储了输入像素对应的HR像素值,故在测试的时候,我们只需要从表中进行读取(当然实践中还会增加插值操作,后续会讲到)。这篇文章是查表法在SR领域的首篇应用,推出的SR-LUT算法以快速实现 L R → S R LR\to SR LR→SR为特点,当然也存在着一些可改进点。
参考文档:
①源码(Pytorch)
②【CVPR2021】Practical Single-Image Super-Resolution Using Look-Up Table
Practical Single-Image Super-Resolution Using Look-Up Table
Abstract
- 文章受启发于SR于移动端速度欠缺的问题,提出了一种可以在测试阶段脱离CNN来实现超分的LUT算法。之前所提出的一系列基于CNN的超分方法虽然实现了较高的表现力(一般),但是CNN会在推断时候依赖神经网络,而在移动端的设备一般都没有配备GPU,这样就CNN-based的实用性就会大打折扣。因此基于实用性的基本要求,我们需要一个可以脱离神经网络来将移动端中的 L R LR LR图像转换为 H R HR HR图像的算法——SR-LUT。
- SR-LUT是将CNN的输出结果保存在LUT中,然后在query-set上不需要CNN,直接查表获取对应高分辨率的像素值来弥补低分辨率图像损失的细节。
- 此外由于不需要太多的浮点运算,故查表的过程也不会太慢。在SR-LUT的CNN中,网络输入一个较小的感受野,以及模型较少的layer个数使得模型的训练时间也比之前CNN-Based方法快很多。
综上所述,SR-LUT是一种结合CNN和LUT在SR领域的实用性算法,它以较快的执行( L R → S R LR\to SR LR→SR)速度以及适中的表现力为推送点(具体的相关实验见后续内容)。
1 Introduction
在SR-LUT的训练期间,我们训练一个输入具有较小感受野的像素(如
2
×
2
2\times 2
2×2)的CNN网络,输入端会经过self-ensemble处理来扩大感受野,然后将网络的训练结果存到一张查找表中,LUT的索引就是输入像素的值,其对应的内容就是SR像素。这样带来的问题就是由于你要考虑各种小感受野的输入,那么这张表就会很大,造成内存消耗太多,一般的移动端都吃不消;因此在实际中,我们会生成一张sampled-LUT,其本质就是通过下采样的方式来减小查找表占用的内存。
在SR-LUT测试期间,我们可以直接脱离CNN,对每一个输入像素,直接到Sampled-LUT中查找出来,然后通过四面体插值来得出最终的SR像素值。
下图是本文推出的3种SR-LUT变体和其余几种插值方法、CNN方法的对比:
从上图中可以看出SR-LUT以较快的实现速度完成了合适的PSNR优化。
总结一下SR-LUT:
- 这篇文章是第一次将CNN和LUT结合在SR中的应用。
- SR-LUT可以以较快的执行速度将LR图像恢复到SR,并且可以在手机端实现出来。
- SR-LUT不仅执行速度快,并且他可以与一些高表现的CNN方法进行比较,即其本身的表现力并不差。
- SR-LUT可以直接脱离CNN,通过查表的方式获取SR像素,这直接带来了免GPU的便利。
- SR-LUT的实践版本Sampled-LUT只需要占用较少的内存就可以近似实现全LUT本身的功能。
2 Related Work
3 Method
从整体来看,SR-LUT可以分为训练和测试2个阶段:
训练阶段可进一步分为CNN训练(3.1小节)和HR像素值存储(3.2小节)2个部分。
测试阶段(3.3小节)可进一步分为查表和四面体插值2个部分。
想要搞清楚SR-LUT,首先得明白理解2个问题:
- 什么是SR-LUT?
- 输入像素和存到SR-LUT中的值是什么对应的?
Q1:什么是SR-LUT?
如上图所示SR-LUT就是一张表格,表格的索引值代表着输入
L
R
LR
LR像素的像素值,我们以感受野为
2
×
2
,
r
=
2
2\times 2, r=2
2×2,r=2且每个像素都是8-bins(8-bits input image)为例,那么对于一个全LUT而言,SR-LUT就是一个6维的张量,前四维分别是四个输入像素值,根据乘法原理,一共有
2
8
×
2
8
×
2
8
×
2
8
=
(
2
8
)
4
2^8\times 2^8\times 2^8\times 2^8=(2^8)^4
28×28×28×28=(28)4种组合,又因为CNN网络的输出是
r
2
r^2
r2张feature map,所以上图配有
2
2
=
4
2^2=4
22=4张LUT。既然是Full-LUT
,自然就要考虑到所有情况;后面的2维表示的是输出4个像素值的序号,如“[0][0]表示HR像素值
V
0
V_0
V0”。我们可以计算一下一个这样的感受野产生的LUT有多大:
(
2
8
)
(
2
×
2
)
×
r
2
×
8
(
b
i
t
s
)
=
64
(
G
B
)
(2^8)^{(2\times 2)} \times r^2 \times 8(bits) = 64(GB)
(28)(2×2)×r2×8(bits)=64(GB)从这里我们也可以看出感受野的增加会直接导致LUT呈指数式增加!
Q2:输入像素和存到SR-LUT中的值是什么对应的?
作者在网络的最后一层设置了深度为
r
2
r^2
r2的feature map,也就是说对每一个
2
×
2
2\times 2
2×2的输入,有
2
2
=
4
2^2=4
22=4张size为
2
×
2
2\times 2
2×2的特征图产生,然后最后来一步PixelShuffle,让深度转换为空间平面状,将输出的4张feature map平铺开来,变成一张输入size为
4
×
4
4\times 4
4×4的HR图像,从输入和输出对比来看,放大了
r
=
2
r=2
r=2倍。具体如下图所示:
之所以输出通道数为 r 2 r^2 r2是因为图像的宽和高都被放大了 r r r倍,而深度转空间就是个张量reshape的过程,pixelshuffle之后长和宽各乘上 r r r倍就可以实现放大的效果了,我们用代码来表示为:
out = out.view(batch*channels, 1, r*h, r*w )
Note:
- 这里 h h h和 w w w可以理解为输入图像的大小。
- 第一步前向的过程并不改变图像的size,但是增加了通道数。
- 在文章中,作者将RF=2,3,4输入规模的SR-LUT模型分别称之为Ours-V,Ours-F, Ours-S,前2者执行速度更快,但Ours-S可实现更高的PSNR。
3.1 Training Deep SR Network
整个训练过程分为2个部分,首先是CNN训练,其完成之后再进行LUT的写入操作。
CNN网络结构
简要描述下网络的组成:
- 由于输入的感知野很小,所以网络不需要太多的层数去堆叠,作者采用了6层CNN网络。
- 除了第一层使用 2 × 2 2\times 2 2×2,其余几层都是用 1 × 1 1\times 1 1×1的卷积核;此外除了最后一层CNN以外,其余的CNN层都外接ReLU来增加模型非线性度。
- 输入和输出的图像size保持不变。
- 最后一层CNN输出深度为 r 2 r^2 r2的feature map,为的是在图像size保持不变的基础上通过深度到平面空间的调整,使得输入图像的高和宽各自乘以 r r r倍来实现分辨率的提升。
- 网络最后一层使用PixelShuffle,主要目的在于调整网络输出为1张高为 r H rH rH,宽为 r W rW rW的高分辨率图像。
- 作者将自集成的技巧用于CNN的训练中,主要目的在于在不增加LUT存储量的基础上扩大感受野。
Self-Ensemble
自集成的方法其实就是对图像做一些增强,之后对网络的输出做反增强操作,最后的输出就是几种反增强操作的平均,作者在文章采用了4种增强方式,分别是旋转90°、180°、270°,具体如下:
y
^
i
=
1
4
∑
j
=
0
3
R
j
−
1
(
f
(
R
j
(
x
i
)
)
)
\hat{y}_i = \frac{1}{4}\sum^3_{j=0}R_j^{-1}(f(R_j(x_i)))
y^i=41j=0∑3Rj−1(f(Rj(xi)))
Note:
- Self-Ensemble的放法一般用在测试中,比如EDSR论文中就这样用,并得到了表现力的增强。而在本文中,作者将自集成用于模型的训练,好处在于在不增加LUTsize的情况下提升了输入的感受野,增加了模型的表现力。
- 将自集成之后的输出值和标签 y y y做loss之后就可以反向传播更新参数了。
3.2 Transferring to LUT
LUT的写入操作在CNN训练完成之后进行。在了解了第三节开头之后关于LUT的两个问题之后,我们就可以开始本节的学习了!
这一步就是将之前CNN训练的结果以输入像素值为索引,CNN输出结果为内容写道LUT中;同时为了避免LUT多大,作者提出了sampled-LUT去缓解这一问题,简单来说全LUT到sampled-LUT的变换就是(默认
R
F
=
4
,
W
=
2
4
RF=4,W=2^4
RF=4,W=24):
L
U
T
[
256
]
[
256
]
[
256
]
[
256
]
→
L
U
T
[
17
]
[
17
]
[
17
]
[
17
]
LUT[256][256][256][256]\to LUT[17][17][17][17]
LUT[256][256][256][256]→LUT[17][17][17][17]Note:
- 采样点为: { 0 , 2 4 , 32 , 48 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 176 , 192 , 208 , 224 , 240 , 255 } \{0,2^4,32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 255\} {0,24,32,48,64,80,96,112,128,144,160,176,192,208,224,240,255}
- 在训练的时候,我们将采样空间进行 W = 2 4 W=2^4 W=24等分,落在某个区间内的像素值作为一个采样点像素索引存入LUT。
- 在测试的时候,对于非采样点,我们会根据距离这个非采样点最近的几个采样点通过插值的方式读取,具体如何读取见3.3节。
3.3 Testing Using SR-LUT
在测试阶段,我们就可以完全脱离CNN,在不需要GPU的情况下只用查表得方式来恢复出
L
R
LR
LR图像欠缺的细节,产生高分辨率的图像。
如果是对于理论上的Full-LUT,那么测试的时候,只需要根据输入像素查表得出结果就好了,但在实际中,由于
2
×
2
2\times 2
2×2的感受野都会带来64GB的存储消耗,故我们可以采用上一节训练好的sampled-LUT来做测试阶段图像细节的恢复。
在SR-LUT中,恢复出SR图像的核心思想就是如果输入像素值是非采样点,那么就根据距离它最近的采样点来进行插值得出该输入点对应的SR像素值。也就是说我们在测试阶段需要使用查表+插值2个操作!
关于插值算法,最简单直接的就是线性插值,如bilinear(双线性插值)、trilinear(三线性插值)、tetralinear(四线性插值)、pentallinear(五线性插值)。但是这些线性插值的运算操作较多,执行速度并不是最快的,既然我们追求在移动端设备上的执行速度,自然应该选择一些更快的插值算法,如下表所示:在输入为3D的感受野的时候,tetrahefral(四面体插值)在乘法操作和比较操作次数上要小于三线性插值,因此我们不妨采用四面体插值的方式作为SR-LUT-baseline的插值算法。
Triangular Interpolation
为了理解4D下的四面体插值算法,作者介绍了在2D下的三角插值算法(两种插值算法原理相似,可以互相迁移):
目前我们的输入是一个
1
×
2
1\times 2
1×2的图像,那么我们就需要
(
2
+
1
)
(2+1)
(2+1)个顶点做一些运算得到我们非采样点的像素值,这几个顶点就是距离这个非采样点最近的3个采样点,具体来看:
①:设输入为
I
0
=
24
,
I
1
=
60
I_0=24,I_1=60
I0=24,I1=60,转换成二进制就是
I
0
=
(
0001
_
1000
)
2
,
I
1
=
(
0011
_
1100
)
2
I_0=(0001\_1000)_2,I_1=(0011\_1100)_2
I0=(0001_1000)2,I1=(0011_1100)2
②:取两个像素的高四位(MSB)作为第一个顶点
P
00
P_{00}
P00的索引值,我们通过查表来得出结果:
P
00
=
L
U
T
[
1
]
[
3
]
P_{00} = LUT[1][3]
P00=LUT[1][3],根据就近原则,对角的那个值可以作为第二个顶点
P
11
=
L
U
T
[
1
+
1
]
[
3
+
1
]
=
L
U
T
[
2
]
[
4
]
P_{11} = LUT[1+1][3+1]=LUT[2][4]
P11=LUT[1+1][3+1]=LUT[2][4]。
③:第三个顶点由输入二进制后四位(LSB)组成,2D输入情况下,我们只有
{
P
00
,
P
01
,
P
10
,
P
11
}
\{P_{00}, P_{01},P_{10}, P_{11}\}
{P00,P01,P10,P11}四种结果,那么到底是
P
01
P_{01}
P01还是
P
10
P_{10}
P10呢?这取决于LSB,
I
0
I_0
I0的LSB记为
L
x
=
8
L_x=8
Lx=8,则
I
1
I_1
I1的LSB为
L
y
=
12
L_y=12
Ly=12,然后查下面这个表(虽然是给四面体插值用的,但是三角形插值也适用):比较
L
x
、
L
y
L_x、L_y
Lx、Ly可知,因为
L
x
<
L
y
L_x<L_y
Lx<Ly,所以取
O
1
=
P
01
、
O
2
=
P
11
O_1=P_{01}、O_2=P_{11}
O1=P01、O2=P11,从这里也验证了第②步中为何取对角顶点。故第三个顶点就是
P
01
=
L
U
T
[
1
]
[
4
]
P_{01}=LUT[1][4]
P01=LUT[1][4]。
④:获取了3个采样点的像素值之后,接下来就要获取3个对应的权重值,也是查询上表:
W
−
L
y
=
4
,
L
y
−
L
x
=
4
,
L
x
=
8
W-L_y=4,L_y-L_x=4, L_x=8
W−Ly=4,Ly−Lx=4,Lx=8,具体如下所示:
⑤:最后的插值结果就是将3个顶点进行加权平均: V ^ = 1 W ∑ i = 0 2 w i O i . \hat{V} = \frac{1}{W}\sum^2_{i=0}w_iO_i. V^=W1i=0∑2wiOi.其中 w i 、 O i w_i、O_i wi、Oi分别是权重和顶点像素值。
对于3D输入的正四面体插值,我们需要找到4个顶点,那么对于4D感受野,我们需要找到5个顶点,以此类推。特别的,对于4D输入,则插值结果为:
V
^
=
1
W
∑
i
=
0
4
w
i
O
i
.
\hat{V} = \frac{1}{W}\sum^4_{i=0}w_iO_i.
V^=W1i=0∑4wiOi.其中
O
0
=
P
0000
,
O
4
=
P
1111
O_0=P_{0000},O_4= P_{1111}
O0=P0000,O4=P1111,也就是不管是四面体插值还是三角插值,最初2个顶点是可以固定的,就是MSB得出的初始点和其对角顶点。
Note:
- 上述讲的是2D输入下的三角形插值算法,对于更高的3D和4D也是一样的算法。
- 输入感受野越大,则插值的复杂度越高!
4 Experiments
4.1 Experimental Setting
- 使用DIV2K作为训练集;使用Set5、Set14、BSD100、Urban100、Manga109作测试集。
- 使用PNSR/SSIM作为评价指标。
- upscaling factor=4。
- mini-batch=32。
- 学习率 l r = 1 0 − 4 lr=10^{-4} lr=10−4。
4.2 Comparison with Others
本节是将SR-LUT3个变体和其他几种算法在训练集上的比较,其中Ours-V表示使用 R F = 1 × 2 RF=1\times 2 RF=1×2和Full-LUT;Ours-F表示使用 R F = 1 × 3 RF=1\times 3 RF=1×3和Sampled-LUT;Ours-S表示使用 2 × 2 2\times 2 2×2和Sampled-LUT。
具体结果如下图表所示:
从图中我们可以得出以下结论:
- Table 4中的测试时间是基于 320 × 180 → 1280 × 720 320\times 180\to 1280\times 720 320×180→1280×720的转换测试的。
- Ours-V实现了几乎是最少的执行时间,同时PSNR也还不错;Ours-F相比速度慢了一些且LUT内存上升,但是表现力提升了;至于Ours-S也保持着runtime和LUT内存的上升,同时也带来了PSNR的提升。因此我们可以说SR-LUT实现了较快的执行速度,同时表现力也挺合适。
- 基于DNN的一些方式,虽然达到了最佳的表现力,但是所消耗的时间是SR-LUT的好几倍,显然SR-LUT在实际移动端快速实现SR的优势体现了出来。
- 在Figure 4中,Ours-V和Ours-F在横向具有比Ours-S更好的表现,可能是因为其感受野形状的原因;而Ours-S的 2 × 2 2\times 2 2×2感受更喜欢捕捉对角方向的细节特征。
4.3 Analysis
RF Size and Kernel Shape
研究的是感受野的个数以及RF形状(即Kernel Shape)对于runtime以及PSNR的影响,实验在Set5数据集上测试,结果如下所示:
- RF-Size的研究:从Ours-V、Ours-F、B可以看出越大的感受野会产生更高质量的图像,但是带来的测试时间也会更久,因为size越大,插值带来的计算复杂度越高,因为顶点的个数会增多。。
- Kernel-Shape的研究:从A、Ours-F对比来看,后者有更好的表现,我个人认为是后者包含的感受野较多;从B和Ours-S对比来看,两者数值上差不多,但是后者在视觉上更加舒适,可能和其 2 × 2 2\times 2 2×2的kernel形状有关,这种形状更加考虑相邻像素的关系。
- 从Ours-S、Ours-S w/o RE对比来看,前者有更好的表现,说明了
self-ensemble
对表现力的提升还是有帮助的。
Sampling Interval
这里研究的是采样间隔
W
W
W对模型表现力的影响。一般来说采样间隔越大,LUT规模越小,带来存储量越小,但是其采样点越稀疏,由于测试阶段非采样点的值需要通过采样点插值计算出来,因此采样点分布越系数,对于插值的影响越坏,所以直接导致表现力受损。
下图是作者进行的实验结果:
- 从上图可以看出, W W W越大,LUT存储量越低,但是图像质量的恢复效果越差。
- 此外,在 W ≤ 2 4 W\leq 2^4 W≤24的时候,PSNR的下降不大,但是存储量却少了很多;超过这个值之后,PSNR就小幅下降了,因此作者会采用 W = 2 4 W=2^4 W=24作为采样间隔。
5 Conclusion
文章最大的贡献在于推出了一种CNN和LUT结合的SR方法——SR-LUT
:
- SR-LUT分为训练、存表、测试三个步骤。其中
训练部分
就是训练一个6层的CNN,输入是比较小感受野的图像,输出需要经过PixelShuffle来将深度转换为图像平面从而产生分辨率放大的效果,此外作者采用了self-ensemble来增大模型的表现力,其在不增加LUT存储量的基础上增加感受野的技巧;存表
是在训练结束后,将输入作为LUT的索引,CNN输出就是我们要往表里填写的内容;测试部分
是脱离CNN,直接喂进去输入像素值,查表读出SR像素值的过程。 - 为了减小LUT的存储消耗,作者采用
Sampled-LUT
而不是Full-LUT,这就是需要在测试阶段,对于非采样点根据采样点进行插值计算得到最后的输出结果。 - SR-LUT最大的价值在于其在测试阶段脱离CNN使用,避免了GPU的配置,从而可以更方便的在移动端、电视端使用。此外,其执行( L R → S R LR\to SR LR→SR)速度也比传统的插值办法快很多,可以说SR-LUT在满足较快的执行速度下同时兼具适中的表现力(PSNR)。