1. 简介
在计算机视觉领域,纹理生成是一个重要的任务。在这里,本文将介绍如何使用Python对图片进行纹理生成。纹理生成是指从一张图片中合成出随机的、看上去像是真实的纹理。这个纹理可以被用于一些应用中,比如游戏或者电影中的贴图。
2. 相关概念
2.1 GAN
GAN是生成对抗网络(Generative Adversarial Networks)的简称。GAN的运行过程是通过两个深度神经网络进行博弈:生成器(generator)和判别器(discriminator)。生成器的目的是伪造和真实图像相似的图像,判别器的目的是判断一个图像是真是假。具体地说,判别器会返回一个值,用于表示给定的图像是真实图像的概率。而生成器会尝试生成更加真实的图像,使得判别器的预测结果越接近1越好。
GAN的训练过程是通过反向传播算法进行的。也就是说,首先在真实图像中挑选一部分图像进行训练,将生成器和判别器网络进行构建;然后,每次训练的过程中,先让判别器判断一批(通常为32张)图像的真假;接着,让生成器尝试生成一批与真实图像相似的图像,再让判别器判断这批图像的真假,如此交替进行,直至可以输出非常真实的图像。
2.2 VAE
VAE是变分自编码器(Variational Auto-Encoder)的简称。它是一种神经网络模型,通常用于对数据的压缩和提取特征。
3. 材料准备
在本文中,我们将使用GAN来生成纹理。具体来说,我们将使用keras-tqdm、keras和tensorflow2.0。
在使用keras-tqdm的构建进度条时,需要安装相应的依赖。可以使用以下命令进行安装:
!pip install keras-tqdm
4. 图片预处理
在本文中,我们将使用python的imageio库来读取图片,并使用Pillow库来缩放图像。以下是如何使用文件路径加载图像:
import imageio
import numpy as np
from PIL import Image
def load_image(path):
img = Image.open(path)
img = np.array(img)
return img
由于GAN模型只能处理较小的图像,为了将图像调整到合适的大小,可以使用Pillow库的resize方法:
from PIL import Image
def resize_image(img, size):
img = Image.fromarray(img)
img = img.resize(size)
img = np.array(img)
return img
5. 纹理生成器的构建
在本文中,我们将使用DCGAN(深度卷积生成对抗网络, Deep Convolutional Generative Adversarial Networks)网络来构建纹理生成器。其大致结构如下:
具体来讲,主要由以下部分组成:
一个全连接层,用于从输入的潜在空间
后续是一系列反卷积操作、批量归一化操作和ReLU激活函数,用于将尺寸缩放成28 * 28 * 1;
最后一个反卷积操作用于将尺寸调整到所需的大小(通常是64 * 64 * 1)。
下面是我们实现DCGAN的生成器的代码:
from keras.models import Sequential
from keras.layers import Dense, Reshape, Conv2DTranspose, BatchNormalization, ReLU
def build_generator():
generator = Sequential()
# 一对全连接层将潜在空间映射到尺寸为7 * 7 * 256的张量上
generator.add(Dense(units=7*7*256, activation='relu', input_shape=(100,)))
generator.add(Reshape(target_shape=(7, 7, 256)))
generator.add(Conv2DTranspose(filters=128, kernel_size=(5,5), strides=(1,1), padding='SAME',
activation=None, use_bias=False))
generator.add(BatchNormalization())
generator.add(ReLU())
# 这里使用反卷积操作将尺寸缩放成28 * 28 * 1
generator.add(Conv2DTranspose(filters=64, kernel_size=(5,5), strides=(2,2), padding='SAME',
activation=None, use_bias=False))
generator.add(BatchNormalization())
generator.add(ReLU())
# 最后一个反卷积操作将尺寸调整到64 * 64 * 1
generator.add(Conv2DTranspose(filters=1, kernel_size=(5,5), strides=(2,2), padding='SAME',
activation='tanh', use_bias=False))
return generator
6. 判别器的构建
判别器的构建与生成器类似,主要由以下部分组成:
由一系列卷积层、批量归一化层和LeakyReLU激活函数组成,用于逐步缩小输入图像的尺寸;
最后是一个全连接层,用于生成网络输出值(0到1之间的概率值)。
以下是我们实现的判别器的代码:
from keras.layers import Conv2D, LeakyReLU, Flatten
def build_discriminator():
discriminator = Sequential()
# 这一层卷积层负责将输入缩小到尺寸为14 * 14 * 64
discriminator.add(Conv2D(filters=64, kernel_size=(5,5), strides=(2,2), padding='SAME',
activation=None, input_shape=(64, 64, 1)))
discriminator.add(LeakyReLU(alpha=0.2))
# 接下来的几层都用卷积+批量归一化+LeakyReLU,将尺寸不断缩小
discriminator.add(Conv2D(filters=128, kernel_size=(5,5), strides=(2,2), padding='SAME',
activation=None))
discriminator.add(BatchNormalization())
discriminator.add(LeakyReLU(alpha=0.2))
discriminator.add(Conv2D(filters=256, kernel_size=(5,5), strides=(2,2), padding='SAME',
activation=None))
discriminator.add(BatchNormalization())
discriminator.add(LeakyReLU(alpha=0.2))
# 最后一个全连接层,输出0到1之间的概率值
discriminator.add(Flatten())
discriminator.add(Dense(units=1, activation='sigmoid'))
return discriminator
7. GAN模型的合成
现在看一下我们的GAN是如何运作的:由于我们的目的是生成纹理,因此我们将生成器的输出作为最终输出。我们使用生成器来生成图像,并且通过将判别器应用于生成器输出的图像,检测它是否“真实”。因此,我们需要同时训练判别器和生成器。
具体来说,首先训练判别器。在这个阶段,我们将使用????给定的真实输入图像,以及从生成器中生成的虚假输入图像,同时训练判别器。目标是最小化训练数据和生成器生成的数据之间的“距离”。
在判别器和生成器分别训练完之后,我们可以将它们合并在一起,通过在预测中使用生成器,来让GAN生成纹理。这时我们可以使用训练好的全局模型获取真实的纹理数据。
下面是我们实现的GAN的代码:
from keras.optimizers import Adam
# discriminator的优化器和损失函数
dis_opt = Adam(lr=0.0002, beta_1=0.5, decay=8e-8)
discriminator.compile(loss='binary_crossentropy', optimizer=dis_opt, metrics=['accuracy'])
# 需要训练的参数的数量,为了训练生成器
discriminator.trainable = False
# 生成器和GAN的优化器
gen_opt = Adam(lr=0.0002, beta_1=0.5, decay=8e-8)
gan.compile(loss='binary_crossentropy', optimizer=gen_opt, metrics=['accuracy'])
8. 训练模型
在准备好数据、定义模型,并且设置好优化器和损失函数之后,现在可以直接训练模型并查看训练进程的实时动态了。
在这里,我们将G和D迭代数目都设置为10000,并使用Python的tqdm("")函数实现一个进度条,可以精细地跟踪训练的进度:
from tqdm import tqdm
def train(generator, discriminator, gan, iterations, batch_size, sample_interval):
# 加载数据
X_train = load_image('path_to_image')
X_train = resize_image(X_train, size=(64, 64))
X_train = np.expand_dims(X_train, axis=0)
X_train = (X_train.astype(np.float32) / 127.5) - 1.
# 指定标准分布的大小 (i.e. : latent space vector -> noise)
sample_size = 100
# 迭代数
for iteration in tqdm(range(iterations)):
# 从真实图像中随机选择一些数据
idx = np.random.randint(0, X_train.shape[0], batch_size)
real_images = X_train[idx]
# 生成噪音数据
noise = np.random.normal(0, 1, (batch_size, sample_size))
# 由生成器产生假图像
generated_images = generator.predict(noise)
# 把所有的数据堆在一起
x_combined_batch = np.concatenate((real_images, generated_images))
y_combined_batch = np.concatenate((np.ones((batch_size, 1)), np.zeros((batch_size, 1))))
# 通过随机梯度下降法(SGD)训练判别器
d_loss = discriminator.train_on_batch(x_combined_batch, y_combined_batch)
# 再次生成噪声数据,并将其与真实标签互换。请注意,我们不使用样本梯度之外的梯度来更新生成器权重,因此 discriminator.trainable 设置为 False
noise = np.random.normal(0, 1, (batch_size, sample_size))
# 误差数据,标签数据
y_fake = np.ones((batch_size, 1))
# 让生成器学习纹理
gan_loss = gan.train_on_batch(noise, y_fake)
# 打印损失和精度
if (iteration + 1) % sample_interval == 0:
print(f"Epoch: {iteration + 1}, GAN Loss: {gan_loss[0]}, Discriminator Loss: {d_loss[0]}")
总结
在本文中,我们介绍了如何使用GAN来生成纹理。我们实现了一个深度卷积生成器网络和一个深度卷积判别器网络,并使用这两个网络,并结合GAN来训练和评估参数,实现了纹理生成。我们还使用tqdm库来实现了一个进度条,精细地跟踪了我们的GAN的训练进程。