写点什么

TensorFlow 工程实战(五):构建 DeblurGAN 模型,将模糊相片变清晰

  • 2019-08-15
  • 本文字数:10926 字

    阅读完需:约 36 分钟

TensorFlow工程实战(五):构建DeblurGAN模型,将模糊相片变清晰

在拍照时,常常因为手抖或补光不足,导致拍出的照片很模糊。本文将介绍如何利用 DeblurGAN 模型将模糊的照片变清晰。

本文摘选自电子工业出版社出版、李金洪编著的《深度学习之TensorFlow工程化项目实战》一书的实例 54:TensorFlow 构建 DeblurGAN 模型,将模糊相片变清晰。


DeblurGAN 模型是一个对抗神经网络模型,由生成器模型和判别器模型组成。


  • 生成器模型,根据输入的模糊图片模拟生成清晰的图片。

  • 判别器模型,用在训练过程中,帮助生成器模型达到更好的效果。具体可以参考论文

实例描述

有一套街景拍摄的照片数据集,其中包含清晰照片和模糊照片。要求:


(1)用该数据集训练 DeblurGAN 模型,使模型具有将模糊图片转成清晰图片的能力。


(2)DeblurGAN 模型能将数据集之外的模糊照片变清晰。


本实例的代码用 tf.keras 接口编写。具体过程如下。

一、获取样本

本实例使用 GOPRO_Large 数据集作为训练样本。GOPRO_Large 数据集里包含高帧相机拍摄的街景图片(其中的照片有的清晰,有的模糊)和人工合成的模糊照片。样本中每张照片的尺寸为 720 pixel×1280 pixel。

1. 下载 GOPRO_Large 数据集

可以通过以下链接获取原始的 GOPRO_Large 数据集:


https://drive.google.com/file/d/1H0PIXvJH4c40pk7ou6nAwoxuR4Qh_Sa2/view

2. 部署 GOPRO_Large 数据集

在 GOPRO_Large 数据集中有若干套实景拍摄的照片。每套照片中包含有 3 个文件夹:


  • 在 blur 文件夹中,放置了模糊的照片。

  • 在 sharp 文件夹中,放置了清晰的照片。

  • 在 blur_gamma 文件夹中,放置了人工合成的模糊照片。


从 GOPRO_Large 数据集的 blur 与 sharp 文件夹里,各取出 200 张模糊与清晰的图片,放到本地代码的同级目录 image 文件夹下用作训练。其中,模糊的图片放在 image/train/A 文件夹下,清晰的图片在 image/train/B 文件夹下。

二、准备 SwitchableNorm 算法模块

SwitchableNorm 算法与其他的归一化算法一样,可以被当作函数来使用。由于在当前的 API 库里没有该代码的实现,所以需要自己编写一套这样的算法。


SwitchableNorm 算法的实现不是本节重点,其原理已经在见《深度学习之 TensorFlow 工程化项目实战》一书的 10.1.6 小节介绍。这里直接使用《深度学习之 TensorFlow 工程化项目实战》一书配套资源代码“switchnorm. py”即可。


直接将该代码放到本地代码文件夹下,然后将其引入。


提示:

在 SwitchableNorm 算法的实现过程中,定义了额外的变量参数。所以在运行时,需要通过会话中的 tf.global_variables_initializer 函数对其进行初始化,否则会报“SwitchableNorm 类中的某些张量没有初始化”之类的错误。

三、代码实现:构建 DeblurGAN 中的生成器模型

DeblurGAN 中的生成器模型是使用残差结构来实现的。其模型的层次结构顺序如下:


(1)通过 1 层卷积核为 7×7、步长为 1 的卷积变换。保持输入数据的尺寸不变。


(2)将第(1)步的结果进行两次卷积核为 3×3、步长为 2 的卷积操作,实现两次下采样效果。


(3)经过 5 层残差块。其中,残差块是中间带有 Dropout 层的两次卷积操作。


(4)仿照(1)和(2)步的逆操作,进行两次上采样,再来一个卷积操作。


(5)将(1)的输入与(4)的输出加在一起,完成一次残差操作。


该结构使用“先下采样,后上采样”的卷积处理方式,这种方式可以表现出样本分布中更好的潜在特征。具体代码如下:


代码 1 deblurmodel


from tensorflow.keras import layers as KLfrom tensorflow.keras import models as KMfrom switchnorm import SwitchNormalization   #载入SwitchableNorm算法ngf = 64            #定义生成器模型原始卷积核个数ndf = 64            #定义判别器模型原始卷积核个数input_nc = 3          #定义输入通道output_nc = 3        #定义输出通道n_blocks_gen = 9        #定义残差层数量
#定义残差块函数def res_block(input, filters, kernel_size=(3, 3), strides=(1, 1), use_dropout=False): x = KL.Conv2D(filters=filters, #使用步长为1的卷积操作,保持输入数据的尺寸不变 kernel_size=kernel_size, strides=strides, padding='same')(input)
x = KL.SwitchNormalization()(x) x = KL.Activation('relu')(x)
if use_dropout: #使用dropout方法 x = KL.Dropout(0.5)(x) x = KL.Conv2D(filters=filters, #再做一次步长为1的卷积操作 kernel_size=kernel_size, strides=strides,padding='same')(x) x = KL.SwitchNormalization()(x) #将卷积后的结果与原始输入相加 merged = KL.Add()([input, x]) #残差层 return merged
def generator_model(image_shape ,istrain = True): #构建生成器模型 #构建输入层(与动态图不兼容) inputs = KL.Input(shape=(image_shape[0],image_shape[1], input_nc)) #使用步长为1的卷积操作,保持输入数据的尺寸不变 x = KL.Conv2D(filters=ngf, kernel_size=(7, 7), padding='same')(inputs) x = KL.SwitchNormalization()(x) x = KL.Activation('relu')(x)
n_downsampling = 2 for i in range(n_downsampling): #两次下采样 mult = 2**i x = KL.Conv2D(filters=ngf*mult*2, kernel_size=(3, 3), strides=2, padding='same')(x) x = KL.SwitchNormalization()(x) x = KL.Activation('relu')(x)
mult = 2**n_downsampling for i in range(n_blocks_gen): #定义多个残差层 x = res_block(x, ngf*mult, use_dropout= istrain)
for i in range(n_downsampling): #两次上采样 mult = 2**(n_downsampling - i) #x = KL.Conv2DTranspose(filters=int(ngf * mult / 2), kernel_size=(3, 3), strides=2, padding='same')(x) x = KL.UpSampling2D()(x) x = KL.Conv2D(filters=int(ngf * mult / 2), kernel_size=(3, 3), padding='same')(x) x = KL.SwitchNormalization()(x) x = KL.Activation('relu')(x)
#步长为1的卷积操作 x = KL.Conv2D(filters=output_nc, kernel_size=(7, 7), padding='same')(x) x = KL.Activation('tanh')(x)
outputs = KL.Add()([x, inputs]) #与最外层的输入完成一次大残差 #防止特征值域过大,进行除2操作(取平均数残差) outputs = KL.Lambda(lambda z: z/2)(outputs) #构建模型 model = KM.Model(inputs=inputs, outputs=outputs, name='Generator') return model
复制代码


代码第 11 行,通过定义函数 res_block 搭建残差块的结构。


代码第 32 行,通过定义函数 generator_model 构建生成器模型。由于生成器模型输入的是模糊图片,输出的是清晰图片,所以函数 generator_model 的输入与输出具有相同的尺寸。


代码第 65 行,在使用残差操作时,将输入的数据与生成的数据一起取平均值。这样做是为了防止生成器模型的返回值的值域过大。在计算损失时,一旦生成的数据与真实图片的像素数据值域不同,则会影响收敛效果。

四、代码实现:构建 DeblurGAN 中的判别器模型

判别器模型的结构相对比较简单。


(1)通过 4 次下采样卷积(见代码第 74~82 行),将输入数据的尺寸变小。


(2)经过两次尺寸不变的 1×1 卷积(见代码第 85~92 行),将通道压缩。


(3)经过两层全连接网络(见代码第 95~97 行),生成判别结果(0 还是 1)。


具体代码如下。


代码 1 deblurmodel(续)


def discriminator_model(image_shape):#构建判别器模型
n_layers, use_sigmoid = 3, False inputs = KL.Input(shape=(image_shape[0],image_shape[1],output_nc)) #下采样卷积 x = KL.Conv2D(filters=ndf, kernel_size=(4, 4), strides=2, padding='same')(inputs) x = KL.LeakyReLU(0.2)(x)
nf_mult, nf_mult_prev = 1, 1 for n in range(n_layers):#继续3次下采样卷积 nf_mult_prev, nf_mult = nf_mult, min(2**n, 8) x = KL.Conv2D(filters=ndf*nf_mult, kernel_size=(4, 4), strides=2, padding='same')(x) x = KL.BatchNormalization()(x) x = KL.LeakyReLU(0.2)(x)
#步长为1的卷积操作,尺寸不变 nf_mult_prev, nf_mult = nf_mult, min(2**n_layers, 8) x = KL.Conv2D(filters=ndf*nf_mult, kernel_size=(4, 4), strides=1, padding='same')(x) x = KL.BatchNormalization()(x) x = KL.LeakyReLU(0.2)(x) #步长为1的卷积操作,尺寸不变。将通道压缩为1 x = KL.Conv2D(filters=1, kernel_size=(4, 4), strides=1, padding='same')(x) if use_sigmoid: x = KL.Activation('sigmoid')(x)
x = KL.Flatten()(x) #两层全连接,输出判别结果 x = KL.Dense(1024, activation='tanh')(x) x = KL.Dense(1, activation='sigmoid')(x)
model = KM.Model(inputs=inputs, outputs=x, name='Discriminator') return model
复制代码


代码 13 行(书中第 81 行),调用了批量归一化函数,使用了参数 trainable 的默认值 True。


代码 31 行(书中第 99 行),用 tf.keras 接口的 Model 类构造判别器模型 model。在使用 model 时,可以设置 trainable 参数来控制模型的内部结构。

五、代码实现:搭建 DeblurGAN 的完整结构

将判别器模型与生成器模型结合起来,构成 DeblurGAN 模型的完整结构。具体代码如下:


代码 1 deblurmodel(续)


def g_containing_d_multiple_outputs(generator, discriminator,image_shape):    inputs = KL.Input(shape=(image_shape[0],image_shape[1],input_nc)  )    generated_image = generator(inputs)      #调用生成器模型    outputs = discriminator(generated_image)   #调用判别器模型    #构建模型    model = KM.Model(inputs=inputs, outputs=[generated_image, outputs])    return model
复制代码


函数 g_containing_d_multiple_outputs 用于训练生成器模型。在使用时,需要将判别器模型的权重固定,让生成器模型不断地调整权重。

六、代码实现:引入库文件,定义模型参数

编写代码实现如下步骤:


(1)载入模型文件——代码文件“10-1 deblurmodel”。


(2)定义训练参数。


(3)定义函数 save_all_weights,将模型的权重保存起来。


具体代码如下:


代码 2 训练 deblur


import osimport datetimeimport numpy as npimport tqdmimport tensorflow as tfimport globfrom tensorflow.python.keras.applications.vgg16 import VGG16from functools import partialfrom tensorflow.keras import models as KMfrom tensorflow.keras import backend as K     #载入Keras的后端实现deblurmodel = __import__("10-1  deblurmodel")   #载入模型文件generator_model = deblurmodel.generator_modeldiscriminator_model = deblurmodel.discriminator_modelg_containing_d_multiple_outputs = deblurmodel.g_containing_d_multiple_outputs
RESHAPE = (360,640) #定义处理图片的大小epoch_num = 500 #定义迭代训练次数
batch_size =4 #定义批次大小critic_updates = 5 #定义每训练一次生成器模型需要训练判别器模型的次数#保存模型BASE_DIR = 'weights/'def save_all_weights(d, g, epoch_number, current_loss): now = datetime.datetime.now() save_dir = os.path.join(BASE_DIR, '{}{}'.format(now.month, now.day)) os.makedirs(save_dir, exist_ok=True) #创建目录 g.save_weights(os.path.join(save_dir, 'generator_{}_{}.h5'.format(epoch_number, current_loss)), True) d.save_weights(os.path.join(save_dir, 'discriminator_{}.h5'.format(epoch_number)), True)
复制代码


代码第 16 行将输入图片的尺寸设为(360,640),使其与样本中图片的高、宽比例相对应(样本中图片的尺寸比例为 720∶1280)。


提示:

在 TensorFlow 中,默认的图片尺寸顺序是“高”在前,“宽”在后。

七、代码实现:定义数据集,构建正反向模型

本小节代码的步骤如下:


(1)用 tf.data.Dataset 接口完成样本图片的载入(见代码第 1~26 行,书中第 29~54 行)。


(2)将生成器模型和判别器模型搭建起来。


(3)构建 Adam 优化器,用于生成器模型和判别器模型的训练过程。


(4)以 WGAN 的方式定义损失函数 wasserstein_loss,用于计算生成器模型和判别器模型的损失值。其中,生成器模型的损失值是由 WGAN 损失与特征空间损失两部分组成。


(5)将损失函数 wasserstein_loss 与优化器一起编译到可训练的判别器模型中(见代码第 42 行,书中第 70 行)。


具体代码如下:


代码 2 训练 deblur(续)


path = r'./image/train'A_paths, =os.path.join(path, 'A', "*.png")      #定义样本路径B_paths = os.path.join(path, 'B', "*.png")#获取该路径下的png文件A_fnames, B_fnames = glob.glob(A_paths),glob.glob(B_paths)#生成Dataset对象dataset = tf.data.Dataset.from_tensor_slices((A_fnames, B_fnames))
def _processimg(imgname): #定义函数调整图片大小 image_string = tf.read_file(imgname) #读取整个文件 image_decoded = tf.image.decode_image(image_string) image_decoded.set_shape([None, None, None])#形状变化,否则下面会转化失败 #变化尺寸 img =tf.image.resize( image_decoded,RESHAPE) image_decoded = (img - 127.5) / 127.5 return image_decoded def _parseone(A_fname, B_fname): #解析一个图片文件 #读取并预处理图片 image_A,image_B = _processimg(A_fname),_processimg(B_fname) return image_A,image_B
dataset = dataset.shuffle(buffer_size=len(B_fnames))dataset = dataset.map(_parseone) #转化为有图片内容的数据集dataset = dataset.batch(batch_size) #将数据集按照batch_size划分dataset = dataset.prefetch(1)
#定义模型g = generator_model(RESHAPE) #生成器模型d = discriminator_model(RESHAPE) #判别器模型d_on_g = g_containing_d_multiple_outputs(g, d,RESHAPE) #联合模型
#定义优化器d_opt = tf.keras.optimizers.Adam(lr=1E-4, beta_1=0.9, beta_2=0.999, epsilon=1e-08)d_on_g_opt = tf.keras.optimizers.Adam(lr=1E-4, beta_1=0.9, beta_2=0.999, epsilon=1e-08)
#WGAN的损失def wasserstein_loss(y_true, y_pred): return tf.reduce_mean(y_true*y_pred)
d.trainable = Trued.compile(optimizer=d_opt, loss=wasserstein_loss) #编译模型d.trainable = False
复制代码


代码第 42 行(书中第 70 行),用判别器模型对象的 compile 方法对模型进行编译。之后,将该模型的权重设置成不可训练。这是因为,在训练生成器模型时,需要将判别器模型的权重固定。只有这样,在训练生成器模型过程中才不会影响到判别器模型。

八、代码实现:计算特征空间损失,并将其编译到生成器模型的训练模型中

生成器模型的损失值是由 WGAN 损失与特征空间损失两部分组成。本小节将实现特征空间损失,并将其编译到可训练的生成器模型中去。

1. 计算特征空间损失的方法

计算特征空间损失的方法如下:


(1)用 VGG 模型对目标图片与输出图片做特征提取,得到两个特征数据。


(2)对这两个特征数据做平方差计算。

2. 特征空间损失的具体实现

在计算特征空间损失时,需要将 VGG 模型嵌入到当前网络中。这里使用已经下载好的预训练模型文件“vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5”。读者可以自行下载,也可以在《深度学习之 TensorFlow 工程化项目实战》一书的配套资源中找到。


将预训练模型文件放在当前代码的同级目录下,并利用 tf.keras 接口将其加载。

3. 编译生成器模型的训练模型

将 WGAN 损失函数与特征空间损失函数放到数组 loss 中,调用生成器模型的 compile 方法将损失值数组 loss 编译进去,实现生成器模型的训练模型。


具体代码如下:


代码 2 训练 deblur(续)


#计算特征空间损失def perceptual_loss(y_true, y_pred,image_shape):    vgg = VGG16(include_top=False,weights="vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5",                input_shape=(image_shape[0],image_shape[1],3) )        loss_model = KM.Model(inputs=vgg.input, outputs=vgg.get_layer('block3_conv3').output)    loss_model.trainable = False    return tf.reduce_mean(tf.square(loss_model(y_true) - loss_model(y_pred)))
myperceptual_loss = partial(perceptual_loss, image_shape=RESHAPE)myperceptual_loss._name_= 'myperceptual_loss'#构建损失loss = [myperceptual_loss, wasserstein_loss]loss_weights = [100, 1] #将损失调为统一数量级d_on_g.compile(optimizer=d_on_g_opt, loss=loss, loss_weights=loss_weights)d.trainable = True
output_true_batch, output_false_batch = np.ones((batch_size, 1)), -np.ones((batch_size, 1))
#生成数据集迭代器iterator = dataset.make_initializable_iterator()datatensor = iterator.get_next()
复制代码


代码第 14 行(书中第 85 行),在计算生成器模型损失时,将损失值函数 myperceptual_loss 与损失值函数 wasserstein_loss 一起放到列表里。


代码第 15 行(书中第 86 行),定义了损失值的权重比例[100,1]。这表示最终的损失值是:函数 myperceptual_loss 的结果乘上 100,将该积与函数 wasserstein_loss 的结果相加所得到和。


提示:

权重比例是根据每个函数返回的损失值得来的。

将 myperceptual_loss 的结果乘上 100,是为了让最终的损失值与函数 wasserstein_loss 的结果在同一个数量级上。


损失值函数 myperceptual_loss、wasserstein_loss 分别与模型 d_on_g 对象的输出值 generated_image、outputs 相对应。

九、代码实现:按指定次数训练模型

按照指定次数迭代调用训练函数 pre_train_epoch,然后在函数 pre_train_epoch 内遍历整个 Dataset 数据集,并进行训练。步骤如下:


(1)取一批次数据。


(2)训练 5 次判别器模型。


(3)将判别器模型权重固定,训练一次生成器模型。


(4)将判别器模型设为可训练,并循环第(1)步,直到整个数据集遍历结束。


具体代码如下:


代码 2 训练 deblur(续)


#定义配置文件config = tf.ConfigProto()config.gpu_options.allow_growth = Trueconfig.gpu_options.per_process_gpu_memory_fraction = 0.5sess = tf.Session(config=config)        #建立会话(session)  def pre_train_epoch(sess, iterator,datatensor):  #迭代整个数据集进行训练    d_losses = []    d_on_g_losses = []    sess.run( iterator.initializer )
while True: try: #获取一批次的数据 (image_blur_batch,image_full_batch) = sess.run(datatensor) except tf.errors.OutOfRangeError: break #如果数据取完则退出循环 generated_images = g.predict(x=image_blur_batch, batch_size=batch_size) #将模糊图片输入生成器模型
for _ in range(critic_updates): #训练5次判别器模型 d_loss_real = d.train_on_batch(image_full_batch, output_true_batch) #训练,并计算还原样本的loss值
d_loss_fake = d.train_on_batch(generated_images, output_false_batch) #训练,并计算模拟样本的loss值 d_loss = 0.5 * np.add(d_loss_fake, d_loss_real)#二者相加,再除以2 d_losses.append(d_loss)
d.trainable = False #固定判别器模型参数 d_on_g_loss = d_on_g.train_on_batch(image_blur_batch, [image_full_batch, output_true_batch]) #训练并计算生成器模型loss值 d_on_g_losses.append(d_on_g_loss)
d.trainable = True #恢复判别器模型参数可训练的属性 if len(d_on_g_losses)%10== 0: print(len(d_on_g_losses),np.mean(d_losses), np.mean(d_on_g_losses)) return np.mean(d_losses), np.mean(d_on_g_losses)#初始化SwitchableNorm变量K.get_session().run(tf.global_variables_initializer())for epoch in tqdm.tqdm(range(epoch_num)): #按照指定次数迭代训练 #迭代训练一次数据集 dloss,gloss = pre_train_epoch(sess, iterator,datatensor) with open('log.txt', 'a+') as f: f.write('{} - {} - {}\n'.format(epoch, dloss, gloss)) save_all_weights(d, g, epoch, int(gloss)) #保存模型sess.close() #关闭会话
复制代码


代码第 36 行(书中第 130 行),进行全局变量的初始化。初始化之后,SwitchableNorm 算法就可以正常使用了。


提示:

即便是 tf.keras 接口,其底层也是通过静态图上的会话(session)来运行代码的。

在代码第 36 行(书中第 130 行)中演示了一个用 tf.keras 接口实现全局变量初始化的技巧:

(1)用 tf.keras 接口的后端类 backend 中的 get_session 函数,获取 tf.keras 接口当前正在使用的会话(session)。

(2)拿到 session 之后,运行 tf.global_variables_initializer 方法进行全局变量的初始化。

(3)代码运行后,输出如下结果:

1%|          | 6/50 [15:06<20:43:45, 151.06s/it]10 >-0.4999978220462799 678.8936
20 -0.4999967348575592 680.67926
……
1%|         | 7/50 [17:29<20:32:16, 149.97s/it]10 >-0.49999643564224244 737.67645
20 -0.49999758243560793 700.6202
30 -0.4999980672200521 672.0518
40 -0.49999826729297636 666.23425
50 -0.4999982775449753 665.67645
……


同时可以看到,在本地目录下生成了一个 weights 文件夹,里面放置的便是模型文件。

十、代码实现:用模型将模糊相片变清晰

在权重 weights 文件夹里找到以“generator”开头并且是最新生成(按照文件的生成时间排序)的文件。将其复制到本地路径下(作者本地的文件名称为“generator_499_0.h5”)。这个模型就是 DeblurGAN 中的生成器模型部分。


在测试集中随机复制几个图片放到本地 test 目录下。与 train 目录结构一样:A 放置模糊的图片,B 放置清晰的图片。


下面编写代码来比较模型还原的效果。具体如下:


代码 3 使用 deblur 模型


import numpy as npfrom PIL import Imageimport globimport osimport tensorflow as tf              #载入模块deblurmodel = __import__("10-1  deblurmodel")generator_model = deblurmodel.generator_model
def deprocess_image(img): #定义图片的后处理函数 img = img * 127.5 + 127.5 return img.astype('uint8')
batch_size = 4RESHAPE = (360,640) #定义要处理图片的大小
path = r'./image/test'A_paths, B_paths = os.path.join(path, 'A', "*.png"), os.path.join(path, 'B', "*.png")#获取该路径下的png文件A_fnames, B_fnames = glob.glob(A_paths),glob.glob(B_paths)#生成Dataset对象dataset = tf.data.Dataset.from_tensor_slices((A_fnames, B_fnames))
def _processimg(imgname): #定义函数调整图片大小 image_string = tf.read_file(imgname) #读取整个文件 image_decoded = tf.image.decode_image(image_string) image_decoded.set_shape([None, None, None]) #形状变化,否则下面会转化失败 #变化尺寸 img =tf.image.resize( image_decoded,RESHAPE)#[RESHAPE[0],RESHAPE[1],3]) image_decoded = (img - 127.5) / 127.5 return image_decoded def _parseone(A_fname, B_fname): #解析一个图片文件 #读取并预处理图片 image_A,image_B = _processimg(A_fname),_processimg(B_fname) return image_A,image_B
dataset = dataset.map(_parseone) #转化为有图片内容的数据集dataset = dataset.batch(batch_size) #将数据集按照batch_size划分dataset = dataset.prefetch(1)
#生成数据集迭代器iterator = dataset.make_initializable_iterator()datatensor = iterator.get_next()g = generator_model(RESHAPE,False) #构建生成器模型g.load_weights("generator_499_0.h5") #载入模型文件
#定义配置文件config = tf.ConfigProto()config.gpu_options.allow_growth = Trueconfig.gpu_options.per_process_gpu_memory_fraction = 0.5sess = tf.Session(config=config) #建立sessionsess.run( iterator.initializer )ii= 0while True: try: #获取一批次的数据 (x_test,y_test) = sess.run(datatensor) except tf.errors.OutOfRangeError: break #如果数据取完则退出循环 generated_images = g.predict(x=x_test, batch_size=batch_size) generated = np.array([deprocess_image(img) for img in generated_images]) x_test = deprocess_image(x_test) y_test = deprocess_image(y_test) print(generated_images.shape[0]) for i in range(generated_images.shape[0]): #按照批次读取结果 y = y_test[i, :, :, :] x = x_test[i, :, :, :] img = generated[i, :, :, :] output = np.concatenate((y, x, img), axis=1) im = Image.fromarray(output.astype(np.uint8)) im = im.resize( (640*3, int( 640*720/1280) ) ) print('results{}{}.png'.format(ii,i)) im.save('results{}{}.png'.format(ii,i)) #将结果保存起来 ii+=1
复制代码


代码第 44 行,在定义生成器模型时,需要将其第 2 个参数 istrain 设为 False。这么做的目的是不使用 Dropout 层。


代码执行后,系统会自动在本地文件夹的 image/test 目录下加载图片,并其放到模型里进行清晰化处理。最终生成的图片如图 1 所示。



图 1 DeblurGAN 的处理结果


图 1 中有 3 个子图。左、中、右依次为原始、模糊、生成后的图片。比较图 1 中的原始图片(最左侧的图片)与生成后的图片(最右侧的图片)可以发现,最右侧模型生成的图片比中间的模糊图片更为清晰。


本文摘选自电子工业出版社出版、李金洪编著的《深度学习之TensorFlow工程化项目实战》一书,更多实战内容点此查看。



本文经授权发布,转载请联系电子工业出版社。


系列文章:


TensorFlow 工程实战(一):用 TF-Hub 库微调模型评估人物年龄


TensorFlow 工程实战(二):用 tf.layers API 在动态图上识别手写数字


TensorFlow 工程实战(三):结合知识图谱实现电影推荐系统


TensorFlow 工程实战(四):使用带注意力机制的模型分析评论者是否满意


TensorFlow 工程实战(五):构建 DeblurGAN 模型,将模糊相片变清晰(本文)


TensorFlow 工程实战(六):在 iPhone 手机上识别男女并进行活体检测


2019-08-15 12:1016208

评论

发布
暂无评论
发现更多内容

在线正则表达式可视化测试工具

入门小站

工具

开源数据库TDSQL PG版再升级:分区表性能提升超10倍

腾讯云数据库

tdsql 国产数据库

企业聊天APP-有什么作用,可以带来哪些便利?WorkPlus即时通讯

BeeWorks

前端开发之VUE基础面试题分享

@零度

Vue 前端开发

使用CRM系统改善客户关系的方法

低代码小观

企业管理 CRM ERP CRM系统 企业管理工具

建议收藏 | SpringBoot 元数据配置原来可以这么玩!

李尚智

spring springboot SpringBoot 2 java 编程 1月月更

隐喻回顾会

Bruce Talk

敏捷 Agile 回顾会 Coach/Facilitate

大数据开发之Hadoop高频面试题

@零度

大数据 hadoop

🍃【Spring专题】「技术原理」为大家介绍一下Spring中的Ant路径匹配工具组件AntPathMatcher

码界西柚

spring 1月月更 SpringFramework AntPathMatcher

实现分区表性能提升超10倍,解密TDSQL PG版开源升级特性

腾讯云数据库

tdsql 国产数据库

无服务器应用DevOps最新实践(内附完整演讲+视频)

亚马逊云科技 (Amazon Web Services)

计算

全面容器化之后,来电科技如何实现微服务治理?

阿里巴巴云原生

阿里云 微服务 云原生 实践

通过 Amazon CloudWatch 配合 Amazon ElastiCache for Redis 遵循监控最佳实践

亚马逊云科技 (Amazon Web Services)

计算

高成长、高潜力,火线安全入选2021中国新锐技术先锋企业20强!

火线安全

针对jQuery的优化方法有哪些

编程江湖

jquery

知识中台,驱动产业智能化升级

百度大脑

人工智能

中小型企业过等保困难有哪些?如何解决?

行云管家

网络安全 企业 过等保

使用Amazon CDK部署基于Amazon Fargate的高可用、易扩展的Airflow集群

亚马逊云科技 (Amazon Web Services)

计算

增效降本开源节流,2022年技术趋势前瞻(异步编程/容器技术)

刘悦的技术博客

容器 性能 异步IO 异步削峰 成本优化

腾讯云TDSQL在PostgreSQL领域的‘‘再次突破’’

腾讯云数据库

tdsql 国产数据库

【Redis集群原理专题】分析一下相关的Redis服务分片技术和Hash Tag

码界西柚

redis redis cluster redis架构 1月月更

Tomcat系统架构分析-Service

编程江湖

tomcat

拍乐云首发音视频「分组讨论」开放能力,开启线上群聊互动新玩法

拍乐云Pano

音视频 RTC 视频会议 泛娱乐 分组讨论

选择“难而正确”的道路,国内VPN距离突破瓶颈还有多远?

科技热闻

4种高速安全混合云解决方案,助力您的云迁移之旅!

亚马逊云科技 (Amazon Web Services)

计算

青藤成功举办“ATT&CK应用发展论坛”,并发布《ATT&CK框架实践指南》

青藤云安全

Linux之du命令

入门小站

Linux

Mybatis如何执行批量操作

编程江湖

mybatis

趋势:2022 年 AI 五大预测

BeeWorks

TDengine在蓝深远望电机物联网监测预警与预测性维护平台中的应用

TDengine

数据库 大数据 tdengine 物联网

理清逻辑,确保云原生时代应用开发的全生命周期安全

华为云开发者联盟

网络安全 安全 应用开发 安全防守

TensorFlow工程实战(五):构建DeblurGAN模型,将模糊相片变清晰_AI&大模型_李金洪_InfoQ精选文章