AwesomeAI之图像超分(1)——RDN

AwesomeAI之图像超分(1)——RDN原论文:ResidualDenseNetworkforImageSuper-Resolution数据集DIV2KDIV2K中共有1000张2K分辨率图像。其中,训练用图像800张,验证用图像100张,测试用图像100张。如何从HR(HighResolution,高分辨率)得到LR(LowResolution,低分辨率)图像?训练输入LR的图片使用该2k图片通过下面3种处理得到:BI方式:主要通过Bicubic下采样得到,缩小比例为x2,x3,x4;BD方式:先对原始图片做(7*7

大家好,又见面了,我是你们的朋友全栈君。

原论文:Residual Dense Network for Image Super-Resolution

数据集 DIV2K

DIV2K中共有1000张2K分辨率图像。其中,训练用图像800张,验证用图像100张,测试用图像100张。

如何从HR(High Resolution,高分辨率,Super Resolution,SR,超分辨率)得到LR(Low Resolution,低分辨率)图像?训练输入LR的图片使用该2k图片通过下面3种处理得到:

  • BI方式:主要通过Bicubic下采样得到,缩小比例为x2,x3,x4;
  • BD方式:先对原始图片做(7*7卷积,1.6方差)高斯滤波,再对滤波后图片做下采样;
  • DN方式:先做Bicubic下采样,再加30%的高斯噪声。

网络结构

AwesomeAI之图像超分(1)——RDN

图1:RDN

输入的是低分辨率图像——LR,输出的是高分辨率图像——HR。
上面的网络称为RDN(Residual Dense Network),其包括4个模块:

  • shallow feature extraction net (SFENet)
    网络的前2个卷积层
  • redidual dense blocks (RDBs)
    RDB = Residual Block + Dense Block,是两者的整合。单个RDB的内部结构如下图:
    AwesomeAI之图像超分(1)——RDN
图2:RDB

RDB的CM机制(contiguous memory mechanism)其实就是图2中的一个个Conv+ReLU。比如对于第 d d d个RDB,其有 C C C个Conv+ReLU,那么:
对于第1个Conv+ReLU的CM机制是指:
F d , 1 = R e L U ( W d , 1 T ∗ F d − 1 ) F_{d,1}=ReLU(W_{d,1}^T*F_{d-1}) Fd,1=ReLU(Wd,1TFd1)
对于第2个Conv+ReLU的CM机制是指:
F d , 2 = R e L U ( W d , 2 T ∗ [ F d − 1 , F d , 1 ] ) F_{d,2}=ReLU(W_{d,2}^T*[F_{d-1},F_{d,1}]) Fd,2=ReLU(Wd,2T[Fd1,Fd,1])
对于第 c c c个Conv+ReLU的CM机制是指:
F d , c = R e L U ( W d , c T ∗ [ F d − 1 , F d , 1 , . . . , F d , c − 1 ] ) , 1 ≤ c ≤ C F_{d,c}=ReLU(W_{d,c}^T*[F_{d-1},F_{d,1},…,F_{d,c-1}]), 1 \leq c \leq C Fd,c=ReLU(Wd,cT[Fd1,Fd,1,...,Fd,c1]),1cC

local feature fusion (LFF)就是图2中的 F d , L F = H L F F d ( [ F d − 1 , F d , 1 , . . . , F d , c , . . . , F d , C ] ) F_{d,LF}=H_{LFF}^d([F_{d-1},F_{d,1,…,F_{d,c,…,F_{d,C}}}]) Fd,LF=HLFFd([Fd1,Fd,1,...,Fd,c,...,Fd,C]),也就是Concat+1*1Conv

local residual learning (GRL)就是图2中的 F d = F d − 1 + F d , L F F_d=F_{d-1}+F_{d,LF} Fd=Fd1+Fd,LF
3者的区别如下图:
AwesomeAI之图像超分(1)——RDN

图3:RB、DB及RDB对比
  • dense feature fusion (DFF)
    global feature fusion (GFF)
    就是图1中的Concat+1*1Conv部分,即 F G F = H G F F ( [ F 1 , . . . , F D ] ) F_{GF}=H_{GFF}([F_1,…,F_D]) FGF=HGFF([F1,...,FD])
    global residual learning (GRL)
    就是图1中的 F D F = F − 1 + F G F F_{DF}=F_{-1}+F_{GF} FDF=F1+FGF
    所以DFF = GFF+GRL
  • up-sampling net (UPNet)
    表示RDN网络最后的上采样+卷积操作。实现了输入图片的放大操作。

RDN和DenseNet block的区别:
(1)RDN中的RDB模块去掉了DenseNet每个block中的batchnorm
(2)RDN中的RDB模块去掉了DenseNet每个block中的pooling
(3)DenseNet中每一个dense block的输出都是concat起来的。而RDB将d-1层的特征也和1到d层的特征做了局部特征融合(local feature fusion (LFF)),更好的保证了信息流的贯通。
(4)在整个RDN网络上,每一个RDB模块的输出都会最终被concat起来利用。而DenseNet 整个网络中只使用每一个DenseBlock最后的输出。

==================================
https://blog.csdn.net/qq_14845119/article/details/81459859

RDN和DenseNet block的区别:
(1)RDN中的RDB模块去掉了DenseNet每个block中的batchnorm
(2)RDN中的RDB模块去掉了DenseNet每个block中的pooling
(3)DenseNet中每一个dense block的输出都是concat起来的。而RDB将d-1层的特征也和1到d层的特征做了局部特征融合(local feature fusion (LFF)),更好的保证了信息流的贯通。
(4)在整个RDN网络上,每一个RDB模块的输出都会最终被concat起来利用。而DenseNet 整个网络中只使用每一个DenseBlock最后的输出。

RDN和SRDenseNet 的区别:
(1)RDN通过3个方面改进SRDenseNet 中使用的传统DenseBlock模块。1,加入了contiguous memory (CM) mechanism 使得先前的RDB模块和当前的RDB模块都有直接接触。2,得益于local feature fusion (LFF) ,RDB模块可以容许更大的增长率。3,RDB中Local residual
learning (LRL) 的应用增加了信息和梯度的流动。
(2)RDB内部没有稠密连接。
(3)SRDenseNet 使用L2 loss,RDB使用L1 loss。

RDN和MemNet 的区别:
(1)MemNet 需要对原始图片使用Bicubic插值方式进行上采样,而RDN直接使用原始低分辨图片,优势就是可以减少计算量和提高效果。
(2)MemNet 中包含逆向和门限单元的模块就不再接受先前模块的输入,而RDB各个模块之间是有信息流交互的。
(3)MemNet 没有全部利用中间的特征信息,而RDN通过Global Residual Learning 将所有信息都利用起来。

==================================

实现细节

github实现: 论文作者实现第三方实现。这里以第三方实现代码进行分析。

构建网络

def model(self):
    F_1 = tf.nn.conv2d(self.images, self.weightsS['w_S_1'], strides=[1,1,1,1], padding='SAME') + self.biasesS['b_S_1']
    F0 = tf.nn.conv2d(F_1, self.weightsS['w_S_2'], strides=[1,1,1,1], padding='SAME') + self.biasesS['b_S_2']

    FD = self.RDBs(F0)

    FGF1 = tf.nn.conv2d(FD, self.weightsD['w_D_1'], strides=[1,1,1,1], padding='SAME') + self.biasesD['b_D_1']
    FGF2 = tf.nn.conv2d(FGF1, self.weightsD['w_D_2'], strides=[1,1,1,1], padding='SAME') + self.biasesD['b_D_2']

    FDF = tf.add(FGF2, F_1)

    FU = self.UPN(FDF)
    # FU = self.UPN(F_1)
    IHR = tf.nn.conv2d(FU, self.weight_final, strides=[1,1,1,1], padding='SAME') + self.bias_final

    return IHR

输入的LR图片shape=(32,32);
w_S_1=[ks, ks, self.c_dim, G0]=(3,3,3,64);
w_S_2=[ks, ks, G0, G]=(3,3,64,64)
b_S_1=[G0]=(64,)
b_S_2=[G]=(64,)

w_D_1=[1, 1, G * D, G0]=(1,1,64*16,64)=(1,1,1024,64)
w_D_2=[ks, ks, G0, G0]=(3,3,64,64)
b_D_1=[G0]=(64,)
b_D_2=[G0]=(64,)

这里重点看下RDBs的实现:

def RDBs(self, input_layer):
       rdb_concat = list()
       rdb_in = input_layer
       for i in range(1, self.D+1):
           x = rdb_in
           for j in range(1, self.C+1):
               tmp = tf.nn.conv2d(x, self.weightsR['w_R_%d_%d' %(i, j)], strides=[1,1,1,1], padding='SAME') + self.biasesR['b_R_%d_%d' % (i, j)]
               tmp = tf.nn.relu(tmp)
               x = tf.concat([x, tmp], axis=3)

           x = tf.nn.conv2d(x, self.weightsR['w_R_%d_%d' % (i, self.C+1)], strides=[1,1,1,1], padding='SAME') +  self.biasesR['b_R_%d_%d' % (i, self.C+1)]
           rdb_in = tf.add(x, rdb_in)
           rdb_concat.append(rdb_in)

       return tf.concat(rdb_concat, axis=3)

一共有self.D=16个RDB,每一个RDB有self.C=8个卷积层,所有的卷积都是padding=“SAME”,即每一层卷积的输出=输入。RBDs输出的就是图1中的Concat后的结果,其实,RDBs的实现是相当简单的。

再来看看最后的UPNet实现:

def UPN(self, input_layer):
    x = tf.nn.conv2d(input_layer, self.weightsU['w_U_1'], strides=[1,1,1,1], padding='SAME') + self.biasesU['b_U_1']
    x = tf.nn.relu(x)
    x = tf.nn.conv2d(x, self.weightsU['w_U_2'], strides=[1,1,1,1], padding='SAME') + self.biasesU['b_U_2']
    x = tf.nn.relu(x)
    x = tf.nn.conv2d(x, self.weightsU['w_U_3'], strides=[1,1,1,1], padding='SAME') + self.biasesU['b_U_3']

    x = self.PS(x, self.scale)

    return x

w_U_1=[5, 5, G0, 64]=[5, 5, 64, 64]
w_U_2=[3, 3, 64, 32]
w_U_3=[3, 3, 32, self.c_dim * self.scale * self.scale ]=(3, 3, 32, 333)=(3, 3, 32, 27)

b_U_1=[64]
b_U_2=[32]
b_U_3=[self.c_dim * self.scale * self.scale]=[27]

此处UPNet的实现和图1优点不同。其次,SFENet中的两个卷积层是没有经过ReLU的,这和图1是一致的。UPNet中的卷积结果要经过ReLU,这个图1是不同的。代码中的UPN只到图1的Upscale部分,图1中Upscale下面的卷积不在上面的UPN代码中,而在model代码中。所以self.PS就对应图1中的Upscale。

构建模型

def build_model(self, images_shape, labels_shape):
    self.images = tf.placeholder(tf.float32, images_shape, name='images')
    self.labels = tf.placeholder(tf.float32, labels_shape, name='labels')

    self.weightsS, self.biasesS = self.SFEParams()
    self.weightsR, self.biasesR = self.RDBParams()
    self.weightsD, self.biasesD = self.DFFParams()
    self.weightsU, self.biasesU = self.UPNParams()
    self.weight_final = tf.Variable(tf.random_normal([self.kernel_size, self.kernel_size, self.c_dim, self.c_dim], stddev=np.sqrt(2.0/9/3)), name='w_f')
    self.bias_final = tf.Variable(tf.zeros([self.c_dim], name='b_f')),
    
    self.pred = self.model()
    # self.loss = tf.reduce_mean(tf.square(self.labels - self.pred))
    self.loss = tf.reduce_mean(tf.abs(self.labels - self.pred))
    self.summary = tf.summary.scalar('loss', self.loss)

    self.model_name = "%s_%s_%s_%s_x%s" % ("rdn", self.D, self.C, self.G, self.scale)
    self.saver = tf.train.Saver(max_to_keep=10)

这里的损失函数就是 l 1 l_1 l1损失即 L = 1 n ∑ i = 1 n ∣ y i − H i ∣ L=\frac{1}{n}\sum_{i=1}^n|y_i-H_i| L=n1i=1nyiHi

训练模型

def train(self, config):
print("\nPrepare Data...\n")
data = input_setup(config)
if len(data) == 0:
print("\nCan Not Find Training Data!\n")
return
data_dir = get_data_dir(config.checkpoint_dir, config.is_train, config.scale)
data_num = get_data_num(data_dir)
batch_num = data_num // config.batch_size
images_shape = [None, self.image_size, self.image_size, self.c_dim]
labels_shape = [None, self.image_size * self.scale, self.image_size * self.scale, self.c_dim]
self.build_model(images_shape, labels_shape)
counter = self.load(config.checkpoint_dir, restore=False)
epoch_start = counter // batch_num
batch_start = counter % batch_num
global_step = tf.Variable(counter, trainable=False)
learning_rate = tf.train.exponential_decay(config.learning_rate, global_step, config.lr_decay_steps*batch_num, config.lr_decay_rate, staircase=True)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
learning_step = optimizer.minimize(self.loss, global_step=global_step)
tf.global_variables_initializer().run(session=self.sess)
merged_summary_op = tf.summary.merge_all()
summary_writer = tf.summary.FileWriter((os.path.join(config.checkpoint_dir, self.model_name, "log")), self.sess.graph)
self.load(config.checkpoint_dir, restore=True)
print("\nNow Start Training...\n")
for ep in range(epoch_start, config.epoch):
# Run by batch images
for idx in range(batch_start, batch_num):
batch_images, batch_labels = get_batch(data_dir, data_num, config.batch_size)
counter += 1
_, err, lr = self.sess.run([learning_step, self.loss, learning_rate], feed_dict={ 
self.images: batch_images, self.labels: batch_labels})
if counter % 10 == 0:
print("Epoch: [%4d], batch: [%6d/%6d], loss: [%.8f], lr: [%.6f], step: [%d]" % ((ep+1), (idx+1), batch_num, err, lr, counter))
if counter % 10000 == 0:
self.save(config.checkpoint_dir, counter)
summary_str = self.sess.run(merged_summary_op, feed_dict={ 
self.images: batch_images, self.labels: batch_labels})
summary_writer.add_summary(summary_str, counter)
if counter > 0 and counter == batch_num * config.epoch:
self.save(config.checkpoint_dir, counter)
break
summary_writer.close()

1、数据输入
images_shape=(32,32,3)
labels_shape=(96,96,3)
首先要明白输入的样本是什么,已经label是什么,这段代码在utils.py中:

def preprocess(path, scale = 3, eng = None, mdouble = None):
img = imread(path)
label_ = modcrop(img, scale)
if eng is None:
# input_ = cv2.resize(label_, None, fx=1.0/scale, fy=1.0/scale, interpolation=cv2.INTER_CUBIC)
input_ = PIL_resize(label_, 1.0/scale, PIL.Image.BICUBIC)
else:
input_ = np.asarray(eng.imresize(mdouble(label_.tolist()), 1.0/scale, 'bicubic'))
input_ = input_[:, :, ::-1]
label_ = label_[:, :, ::-1]
return input_, label_

数据集下载:http://data.vision.ee.ethz.ch/cvl/DIV2K/DIV2K_train_HR.zip
我们下载的是800张HR图片。我们对这800张HR图片按比例1.0/scale= 1 3 \frac{1}{3} 31进行缩小(即下采样),并采用BICUBIC插值法进行差值,之后得到的就是有锯齿状的LR图片。我们的样本就是原始的HR图片。
2、导入模型
3、计算epoch和batch
4、引入指数衰减的学习率:
decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
5、使用AdamOptimizer进行优化;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/150443.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • PHP进销存erp源码库存管理系统

    PHP进销存erp源码库存管理系统PHP进销存erp源码库存管理系统(2次开发另外收费)本系统开发PHP+MySQL采用CI2.x框架本系统运行环境php5.3+mysql5.5支持IIS、apache不支持nginx源码网站:www.phprr.com演示地址:http://www.phprr.com/show-55账号:admin密码:jxc888888…

  • vue报错cannot read property_vue3 ref 数组

    vue报错cannot read property_vue3 ref 数组当函数执行到this.agents.splice()时,我设置了断点。发现传参index是0,但是页面上的列表项对应的第一行数据没有被删除,WTF!!!这是什么鬼!然后我打开VueDevtools,然后刷新了一下,发现那个数组的第一项还是存在的removeOneAgentByIndex:function(index){this.agents.splice(index,1)…

  • Java程序员常用软件

    Java程序员常用软件目录1、IDE2、应用服务器3、分布式版本控制4、项目管理5、数据库管理工具6、Web服务器7、接口测试工具8、SSH工具9、抓包工具10、其他一些软件工欲善其事必先利其器,作为有多年开发经验的Java程序员,应该都会有一些常用的软件来辅助自己的工作,下面分享从业几年来一直在用的一些软件。1、IDEEclipseEclipse是一…

  • sql清空表数据命令

    sql清空表数据命令有三种清空方式:1.delete逐行删除表数据速度比较慢,不适合删除数据量大的表。2.truncate删除表中所有数据并且保留表结构,但是不能撤消还原。3.drop表数据和表结构一起删除,在实践过程中删除大数据量表数据。使用1,2这两种方法需要等待好久才能清空完成。有一个较快的方法是先导出表结构,首先对原先表进行删除,然后再重建。…

  • Python终将成为最火爆的编程语言,因为它是属于大众的「建议收藏」

    Python终将成为最火爆的编程语言,因为它是属于大众的「建议收藏」很多培训机构宣称py是人工智能必备的编程语言,打着速成的旗号来引诱学者学习python。事实却并不是这样的,万丈高台平地起,不论你想从事怎样的编程工作,都是从最基本的编程技巧开始的;Python并不适合所有人,如果你是一个编程类专业的学生,适度了解python是有必要的(python的第三方库的爆发造就了不少C/C++程序员的就业),但如果你作为一个非编程类专业但又需要了解编程的人…

  • SpringMVC面试题总结「建议收藏」

    SpringMVC面试题总结「建议收藏」前言:SpringMVC的面试题常见的也就那几种,本文我打算分为两个方向为大家介绍SpringMVC的面试题。第一部分将从源码的执行的角度分析SpringMVC(以后简称MVC)第二部分将从面试官常问的SpringMVC面试题取介绍SpringMVC源码介绍1.http://localhost:8000/hello这个路径的执行流程是怎么走的流程大致分析一下:首先会请求会进入前…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号