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 KL
from tensorflow.keras import models as KM
from 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 os
import datetime
import numpy as np
import tqdm
import tensorflow as tf
import glob
from tensorflow.python.keras.applications.vgg16 import VGG16
from functools import partial
from tensorflow.keras import models as KM
from tensorflow.keras import backend as K 		#載入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/'
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 = 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)
	
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 np
from PIL import Image
import glob
import os
import 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 = 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
    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 = True
config.gpu_options.per_process_gpu_memory_fraction = 0.5
sess = tf.Session(config=config)				#建立session
sess.run( iterator.initializer )
ii= 0
while 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工程化項目實戰》一書,更多實戰內容點此查看。

本文經授權發佈,轉載請聯繫電子工業出版社。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章