图像生成与翻译:GAN与AutoEncoder实战
1. 复杂彩色图像生成:以动漫图像为例
在之前,我们已经学会了如何创建手写数字和字母的图像。现在,让我们尝试使用已学的技术来创建像动漫这样的复杂彩色图像。
1.1 数据下载
Kaggle网站上有大量的动漫角色数据集,我们已经为这个项目准备了数据集,并将其放在指定的下载网站上。可以使用
wget
工具将数据下载到项目中:
! wget --no-check-certificate -r 'https://drive.google.com/uc?export=download&id=1z7rXRIFtRBFZHt-Mmti4HxrxHqUfG3Y8' -O tf-book.zip
!unzip tf-book.zip
1.2 创建数据集
编写一个函数来创建数据集:
import numpy as np
from PIL import Image
import glob
def load_dataset(batch_size, img_shape, data_dir = None):
# Create a tuple of size(30000,64,64,3)
sample_dim = (batch_size,) + img_shape
# Create an uninitialized array of shape (30000,64,64,3)
sample = np.empty(sample_dim, dtype=np.float32)
# Extract all images from our file
all_data_dirlist = list(glob.glob(data_dir))
# Randomly select an image file from our data list
sample_imgs_paths = np.random.choice(all_data_dirlist,batch_size)
for index,img_filename in enumerate(sample_imgs_paths):
# Open the image
image = Image.open(img_filename)
# Resize the image
image = image.resize(img_shape[:-1])
# Convert the input into an array
image = np.asarray(image)
# Normalize data
image = (image/127.5) -1
# Assign the preprocessed image to our sample
sample[index,...] = image
print("Data loaded")
return sample
x_train=load_dataset(30000,(64,64,3), "/content/tf-book/chapter13/anime/data/*.png")
BUFFER_SIZE = 30000
BATCH_SIZE = 256
import tensorflow as tf
train_dataset = tf.data.Dataset.from_tensor_slices(x_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
1.3 显示图像
可以通过打印数据集中的一些图像来检查数据集是否正确加载:
import matplotlib.pyplot as plt
n = 10
f = plt.figure(figsize=(15,15))
for i in range(n):
f.add_subplot(1, n, i + 1)
plt.subplot(1, n, i+1 ).axis("off")
plt.imshow(x_train[i])
plt.show()
print(x_train.shape)
输出结果显示,训练数据包含30000张大小为64x64的RGB图像。
1.4 模型训练与输出
训练模型以生成逼真的图像需要运行10000个或更多的epoch。在GPU上,每个epoch大约需要16秒。以下是不同epoch生成的示例图像:
| Epoch | 示例图像 |
| — | — |
| 1 | [Epoch 1生成的图像] |
| 50 | [Epoch 50生成的图像] |
| 100 | [Epoch 100生成的图像] |
| 200 | [Epoch 200生成的图像] |
| 300 | [Epoch 300生成的图像] |
| 400 | [Epoch 400生成的图像] |
| 500 | [Epoch 500生成的图像] |
| 600 | [Epoch 600生成的图像] |
| 700 | [Epoch 700生成的图像] |
| 800 | [Epoch 800生成的图像] |
| 900 | [Epoch 900生成的图像] |
| 1000 | [Epoch 1000生成的图像] |
可以看到,大约到1000个epoch时,网络已经学会了相当多的内容来重现原始卡通图像。
2. 图像翻译:黑白图像彩色化
现在,让我们学习如何使用深度神经网络将黑白图像转换为彩色图像。这里我们将使用一种名为AutoEncoders的网络架构。
2.1 AutoEncoders简介
AutoEncoders由两部分组成:编码器(Encoder)和解码器(Decoder)。其工作原理如下:
graph LR
A[黑白图像] --> B[编码器]
B --> C[降维表示]
C --> D[解码器]
D --> E[彩色图像]
编码器通过一系列卷积层处理图像并缩小图像尺寸,以学习输入图像的降维表示。解码器则通过另一系列卷积层尝试再生图像,在这个过程中放大图像并添加颜色。
2.2 颜色空间
为了理解如何对图像进行彩色化,我们需要先了解颜色空间。常见的颜色空间有:
-
RGB
:最常用的颜色空间,包含红(R)、绿(G)和蓝(B)三个通道,每个通道由8位表示,可表示超过1600万种颜色。
-
YCbCr
:JPEG和MPEG格式使用的颜色空间,更适合数字传输和存储。Y通道表示灰度图像的亮度,Cb和Cr表示蓝色和红色的色差分量。
-
Lab
:由国际照明委员会(CIE)设计的颜色空间,L通道表示亮度,取值范围为0到100;“a”通道从绿色(-)到红色(+)编码,“b”通道从蓝色(-)到黄色(+)编码。在我们的项目中,使用Lab颜色空间,因为通过分离表示亮度的灰度分量,网络只需学习剩下的两个通道进行彩色化,有助于减小网络规模并加快收敛速度。
2.3 网络配置
AutoEncoder网络可以通过以下三种不同的方式进行配置:
-
Vanilla模型
:编码器有一系列带步长的卷积层用于缩小图像和提取特征,解码器也有卷积层用于放大图像和彩色化。但这种自编码器的编码器不够深,无法提取图像的全局特征。
-
Merged模型
:由Lizuka等人在论文“Let there be Color!”中提出。使用一个八层的编码器提取中级表示,第六层的输出分叉并通过另一个七层网络提取全局特征,然后通过融合网络将两个输出连接起来并输入到解码器。
-
使用预训练网络的Merged模型
:由Baldassarre等人在论文“Deep Koalarization: Image Colorization using CNNs and Inception-Resnet-v2”中提出。特征提取由预训练的ResNet完成。
2.4 实践:使用Vanilla AutoEncoder
在这个项目中,我们将使用Vanilla自编码器。首先,打开一个新的Colab笔记本并将其重命名为“AutoEncoder – Custom”,然后添加以下导入:
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
from tqdm import tqdm
from itertools import chain
通过以上步骤,我们可以看到如何使用GAN生成复杂的动漫图像,以及如何使用AutoEncoder将黑白图像转换为彩色图像。这些技术在图像生成和处理领域有着广泛的应用。
3. 动漫图像生成的完整代码实现
以下是生成动漫图像的完整代码,包含数据下载、数据集创建、模型定义、训练等步骤:
import tensorflow as tf
import numpy as np
import sys
import os
import cv2
import glob
from PIL import Image
import matplotlib.pyplot as plt
import time
from tensorflow import keras
from tensorflow.keras import layers
from keras.layers import UpSampling2D, Conv2D
# 下载数据
! wget --no-check-certificate -r 'https://drive.google.com/uc?export=download&id=1z7rXRIFtRBFZHt-Mmti4HxrxHqUfG3Y8' -O tf-book.zip
!unzip tf-book.zip
# 创建数据集函数
def load_dataset(batch_size, img_shape, data_dir = None):
# Create a tuple of size(30000,64,64,3)
sample_dim = (batch_size,) + img_shape
# Create an uninitialized array of shape (30000,64,64,3)
sample = np.empty(sample_dim, dtype=np.float32)
# Extract all images from our file
all_data_dirlist = list(glob.glob(data_dir))
# Randomly select an image file from our data list
sample_imgs_paths = np.random.choice(all_data_dirlist,batch_size)
for index,img_filename in enumerate(sample_imgs_paths):
# Open the image
image = Image.open(img_filename)
# Resize the image
image = image.resize(img_shape[:-1])
# Convert the input into an array
image = np.asarray(image)
# Normalize data
image = (image/127.5) -1
# Assign the preprocessed image to our sample
sample[index,...] = image
print("Data loaded")
return sample
x_train=load_dataset(30000,(64,64,3), "/content/tf-book/chapter13/anime/data/*.png")
BUFFER_SIZE = 30000
BATCH_SIZE = 256
train_dataset = tf.data.Dataset.from_tensor_slices(x_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
# 显示图像
n = 10
f = plt.figure(figsize=(15,15))
for i in range(n):
f.add_subplot(1, n, i + 1)
plt.subplot(1, n, i+1 ).axis("off")
plt.imshow(x_train[i])
plt.show()
print(x_train.shape)
# 生成器模型
gen_model = tf.keras.Sequential()
# seed image of size 4x4
gen_model.add(tf.keras.layers.Dense(64*4*4, use_bias=False, input_shape=(100,)))
gen_model.add(tf.keras.layers.BatchNormalization())
gen_model.add(tf.keras.layers.LeakyReLU())
gen_model.add(tf.keras.layers.Reshape((4,4,64)))
# size of output image is still 4x4
gen_model.add(tf.keras.layers.Conv2DTranspose(256, (5, 5), strides=(1, 1), padding='same', use_bias=False))
gen_model.add(tf.keras.layers.BatchNormalization())
gen_model.add(tf.keras.layers.LeakyReLU())
# size of output image is 8x8
gen_model.add(tf.keras.layers.Conv2DTranspose(128, (5, 5), strides=(2, 2), padding='same', use_bias=False))
gen_model.add(tf.keras.layers.BatchNormalization())
gen_model.add(tf.keras.layers.LeakyReLU())
# size of output image is 16x16
gen_model.add(tf.keras.layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
gen_model.add(tf.keras.layers.BatchNormalization())
gen_model.add(tf.keras.layers.LeakyReLU())
# size of output image is 32x32
gen_model.add(tf.keras.layers.Conv2DTranspose(32, (5, 5), strides=(2, 2), padding='same', use_bias=False))
gen_model.add(tf.keras.layers.BatchNormalization())
gen_model.add(tf.keras.layers.LeakyReLU())
# size of output image is 64x64
gen_model.add(tf.keras.layers.Conv2DTranspose(3, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
gen_model.summary()
noise = tf.random.normal([1, 100])
generated_image = gen_model(noise, training=False)
plt.imshow(generated_image[0, :, :, 0] )
# 判别器模型
discri_model = tf.keras.Sequential()
discri_model.add(tf.keras.layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same', input_shape=[64,64,3]))
discri_model.add(tf.keras.layers.LeakyReLU())
discri_model.add(tf.keras.layers.Dropout(0.3))
discri_model.add(tf.keras.layers.Conv2D(256, (5, 5), strides=(2, 2), padding='same'))
discri_model.add(tf.keras.layers.LeakyReLU())
discri_model.add(tf.keras.layers.Dropout(0.3))
discri_model.add(tf.keras.layers.Flatten())
discri_model.add(tf.keras.layers.Dense(1))
discri_model.summary()
tf.keras.utils.plot_model(discri_model)
decision = discri_model(generated_image)
print (decision)
# 损失函数
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
def generator_loss(generated_output):
return cross_entropy(tf.ones_like(generated_output), generated_output)
def discriminator_loss(real_output, generated_output):
# compute loss considering the image is real [1,1,...,1]
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
# compute loss considering the image is fake[0,0,...,0]
generated_loss = cross_entropy(tf.zeros_like(generated_output), generated_output)
# compute total loss
total_loss = real_loss + generated_loss
return total_loss
# 优化器
gen_optimizer = tf.optimizers.Adam(1e-4)
discri_optimizer = tf.optimizers.Adam(1e-4)
epoch_number = 0
EPOCHS = 10000
noise_dim = 100
seed = tf.random.normal([1, noise_dim])
checkpoint_dir = '/content/drive/My Drive/GAN3/Checkpoint'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=gen_optimizer, discriminator_optimizer=discri_optimizer, generator= gen_model, discriminator = discri_model)
from google.colab import drive
drive.mount('/content/drive')
%cd '/content/drive/My Drive/GAN3'
# 梯度调整
def gradient_tuning(images):
# create a noise vector.
noise = tf.random.normal([16, noise_dim])
# Use gradient tapes for automatic differentiation
with tf.GradientTape() as generator_tape, tf.GradientTape() as discriminator_tape:
# ask genertor to generate random images
generated_images = gen_model(noise, training=True)
# ask discriminator to evalute the real images and generate its output
real_output = discri_model(images, training=True)
# ask discriminator to do the evlaution on generated (fake) images
fake_output = discri_model(generated_images, training=True)
# calculate generator loss on fake data
gen_loss = generator_loss(fake_output)
# calculate discriminator loss as defined earlier
disc_loss = discriminator_loss(real_output, fake_output)
# calculate gradients for generator
gen_gradients = generator_tape.gradient(gen_loss, gen_model.trainable_variables)
# calculate gradients for discriminator
discri_gradients = discriminator_tape.gradient(disc_loss, discri_model.trainable_variables)
# use optimizer to process and apply gradients to variables
gen_optimizer.apply_gradients(zip(gen_gradients, gen_model.trainable_variables))
# same as above to discriminator
discri_optimizer.apply_gradients(zip(discri_gradients, discri_model.trainable_variables))
def generate_and_save_images(model, epoch, test_input):
global epoch_number
epoch_number = epoch_number + 1
# set training to false to ensure inference mode
predictions = model(test_input, training=False)
# display and save image
fig = plt.figure(figsize=(4,4))
for i in range(predictions.shape[0]):
plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
plt.axis('off')
plt.savefig('image_at_epoch_{:01d}.png'.format(epoch_number))
plt.show()
# 训练函数
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
gradient_tuning(image_batch)
# Produce images as we go
generate_and_save_images(gen_model, epoch + 1, seed)
# save checkpoint data
checkpoint.save(file_prefix = checkpoint_prefix)
print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
train(train_dataset, EPOCHS)
# 恢复模型
try:
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))
except Exception as error:
print("Error loading in model : {}".format(error))
train(train_dataset, EPOCHS)
4. 不同网络配置的AutoEncoder对比
| 网络配置 | 优点 | 缺点 |
|---|---|---|
| Vanilla模型 | 结构简单,易于实现 | 编码器不够深,无法提取图像全局特征 |
| Merged模型 | 能提取中级表示和全局特征,提高彩色化效果 | 模型复杂度较高 |
| 使用预训练网络的Merged模型 | 利用预训练网络的特征提取能力,加快训练速度 | 需要依赖预训练模型,灵活性相对较低 |
5. 总结与展望
Generative Adversarial Network (GAN) 和AutoEncoders在图像生成和翻译领域展现出了强大的能力。GAN通过生成器和判别器的对抗训练,能够生成逼真的复杂图像,如动漫角色。而AutoEncoders则可以将黑白图像转换为彩色图像,为图像的处理和修复提供了有效的解决方案。
在实际应用中,GAN可以用于创建大规模的图像数据集、生成名人的人脸图像、制作卡通角色等。AutoEncoders则在图像彩色化、图像去噪等方面有着广泛的应用。
未来,随着技术的不断发展,这些模型有望在更多领域得到应用,如虚拟现实、增强现实、影视制作等。同时,我们也可以通过不断优化模型结构、调整参数等方式,进一步提高模型的性能和效果。
总之,掌握GAN和AutoEncoders的原理和实现方法,将为我们在图像领域的研究和应用带来更多的可能性。
超级会员免费看

被折叠的 条评论
为什么被折叠?



