Press "Enter" to skip to content

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

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

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

 

本文摘选自电子工业出版社出版、李金洪编着的 《深度学习之 TensorFlow 工程化项目实战》 一书的实例 54: 构建 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.kerasimportlayers as KL
from tensorflow.kerasimportmodels as KM
from switchnormimportSwitchNormalization#载入 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)
ifuse_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 iinrange(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 iinrange(n_blocks_gen):#定义多个残差层
x= res_block(x, ngf*mult,use_dropout=istrain)
for iinrange(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 ninrange(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)
ifuse_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

 

 

importos
importdatetime
importnumpyasnp
importtqdm
importtensorflowastf
importglob
fromtensorflow.python.keras.applications.vgg16importVGG16
fromfunctoolsimportpartial
fromtensorflow.kerasimportmodelsasKM
fromtensorflow.kerasimportbackendasK#载入 Keras 的后端实现
deblurmodel = __import__("10-1 deblurmodel")#载入模型文件
generator_model = deblurmodel.generator_model
discriminator_model = deblurmodel.discriminator_model
g_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/'
defsave_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= True
d.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 =True
config.gpu_options.per_process_gpu_memory_fraction =0.5
sess = tf.Session(config=config)#建立会话(session)
defpre_train_epoch(sess, iterator,datatensor):#迭代整个数据集进行训练
d_losses = []
d_on_g_losses = []
sess.run( iterator.initializer )
whileTrue:
try:#获取一批次的数据
(image_blur_batch,image_full_batch) = sess.run(datatensor)
excepttf.errors.OutOfRangeError:
break#如果数据取完则退出循环
generated_images = g.predict(x=image_blur_batch, batch_size=batch_size)#将模糊图片输入生成器模型
for_inrange(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#恢复判别器模型参数可训练的属性
iflen(d_on_g_losses)%10==0:
print(len(d_on_g_losses),np.mean(d_losses), np.mean(d_on_g_losses))
returnnp.mean(d_losses), np.mean(d_on_g_losses)
#初始化 SwitchableNorm 变量
K.get_session().run(tf.global_variables_initializer())
forepochintqdm.tqdm(range(epoch_num)):#按照指定次数迭代训练
#迭代训练一次数据集
dloss,gloss = pre_train_epoch(sess, iterator,datatensor)
withopen('log.txt','a+')asf:
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.4999978220462799678.8936
20-0.4999967348575592680.67926
……
1%| |7/50[17:29<20:32:16,149.97s/it]10>-0.49999643564224244737.67645
20-0.49999758243560793700.6202
30-0.4999980672200521672.0518
40-0.49999826729297636666.23425
50-0.4999982775449753665.67645
……

 

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

 

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

 

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

 

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

 

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

 

代码 3 使用 deblur 模型

 

 

importnumpyasnp
fromPILimportImage
importglob
importos
importtensorflowastf#载入模块
deblurmodel = __import__("10-1 deblurmodel")
generator_model = deblurmodel.generator_model
defdeprocess_image(img):#定义图片的后处理函数
img = img *127.5+127.5
returnimg.astype('uint8')
batch_size =4
RESHAPE = (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
returnimage_decoded
def_parseone(A_fname, B_fname):#解析一个图片文件
#读取并预处理图片
image_A,image_B = _processimg(A_fname),_processimg(B_fname)
returnimage_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 =True
config.gpu_options.per_process_gpu_memory_fraction =0.5
sess = tf.Session(config=config)#建立 session
sess.run( iterator.initializer )
ii=0
whileTrue:
try:#获取一批次的数据
(x_test,y_test) = sess.run(datatensor)
excepttf.errors.OutOfRangeError:
break#如果数据取完则退出循环
generated_images = g.predict(x=x_test, batch_size=batch_size)
generated = np.array([deprocess_image(img)forimgingenerated_images])
x_test = deprocess_image(x_test)
y_test = deprocess_image(y_test)
print(generated_images.shape[0])
foriinrange(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 中的原始图片(最左侧的图片)与生成后的图片(最右侧的图片)可以发现,最右侧模型生成的图片比中间的模糊图片更为清晰。

Be First to Comment

发表评论

邮箱地址不会被公开。 必填项已用*标注