Введение
Представьте модель pix2pix на основе CGAN, которую можно использовать для реализации различных задач перевода парных изображений.
принцип
Перевод парных изображений включает в себя множество сценариев применения, вход и выход — изображения одинакового размера.
- уличные лейблы, уличные сцены
- Строительный лейбл, создание реальной сцены
- Черно-белые фотографии, цветные фотографии
- спутниковая карта, простая карта
- день Ночь
- край, настоящий
pix2pix предоставляет общую техническую основу для выполнения различных задач перевода парных изображений.
Автор также предоставляет онлайн-демонстрацию, в том числе некогда популярный edge2cat,affinelayer.com/pixsrv/
Принцип pix2pix следующий, типичная структура CGAN, но G принимает только фиксированный вход X, что можно понимать как условие C, то есть случайный шум не требуется, а затем выводится переведенная версия Y
D принимает X (C в CGAN) и Y (истинные и ложные образцы) и оценивает, являются ли X и Y парными переводами.
В дополнение к стандартной функции потерь GAN, pix2pix также рассматривает расстояние L1 между сгенерированными образцами и реальными образцами как потери.
Потеря GAN отвечает за захват высокочастотных характеристик изображения, а потеря L1 отвечает за захват низкочастотных характеристик, что делает сгенерированные результаты реалистичными и четкими.
Генератор G реализован с использованием Unet, в основном с использованием Skip-Connection для изучения сопоставления между парными изображениями.
Дискриминатор D использует идеюPatchGAN. Раньше он давал оценку всему изображению.PatchGAN делит изображение на множество блоков и дает оценку каждому блоку.
выполнить
На код ссылаются следующие проекты,GitHub.com/аффинный слой…, предоставляет множество удобных и простых в использовании функций
- Несколько предварительно обученных моделей для различных задач перевода изображений
- Обучите модель перевода изображения на ваших собственных парных данных изображения (две папки с одинаковым именем и размером изображения).
- Обучите модель окраски на собственных данных изображения (одна папка для хранения цветных изображений, поскольку черно-белые изображения могут быть автоматически получены из цветных изображений)
Ссылка для скачивания набора данных,people.ee CS.Berkeley Quota/~ Довольно хорошо притворяется/Боится…, включая пять наборов данных: Здания, Просмотры улиц, Карты, Обувь, Сумки.
Если взять в качестве примера данные фасадов зданий, то train, val и test включают соответственно 400, 100 и 106 изображений, каждое из которых состоит из двух частей, соответствующих двум версиям до и после перевода.
загрузить библиотеку
# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from imageio import imread, imsave, mimsave
import glob
import os
from tqdm import tqdm
Загружайте изображения, используйте train и val, всего 500 изображений
images = glob.glob('data/train/*.jpg') + glob.glob('data/val/*.jpg')
print(len(images))
Организуйте данные, разделите X и Y на каждом изображении, B2A означает справа налево
X_all = []
Y_all = []
WIDTH = 256
HEIGHT = 256
for image in images:
img = imread(image)
img = (img / 255. - 0.5) * 2
# B2A
X_all.append(img[:, WIDTH:, :])
Y_all.append(img[:, :WIDTH, :])
X_all = np.array(X_all)
Y_all = np.array(Y_all)
print(X_all.shape, Y_all.shape)
Определите некоторые константы, сетевые тензоры, вспомогательные функции, здесьbatch_size
Установите на 1, чтобы каждое обучение было однозначным переводом изображения.
batch_size = 1
LAMBDA = 100
OUTPUT_DIR = 'samples'
if not os.path.exists(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
X = tf.placeholder(dtype=tf.float32, shape=[None, HEIGHT, WIDTH, 3], name='X')
Y = tf.placeholder(dtype=tf.float32, shape=[None, HEIGHT, WIDTH, 3], name='Y')
k_initializer = tf.random_normal_initializer(0, 0.02)
g_initializer = tf.random_normal_initializer(1, 0.02)
def lrelu(x, leak=0.2):
return tf.maximum(x, leak * x)
def d_conv(inputs, filters, strides):
padded = tf.pad(inputs, [[0, 0], [1, 1], [1, 1], [0, 0]], mode='CONSTANT')
return tf.layers.conv2d(padded, kernel_size=4, filters=filters, strides=strides, padding='valid', kernel_initializer=k_initializer)
def g_conv(inputs, filters):
return tf.layers.conv2d(inputs, kernel_size=4, filters=filters, strides=2, padding='same', kernel_initializer=k_initializer)
def g_deconv(inputs, filters):
return tf.layers.conv2d_transpose(inputs, kernel_size=4, filters=filters, strides=2, padding='same', kernel_initializer=k_initializer)
def batch_norm(inputs):
return tf.layers.batch_normalization(inputs, axis=3, epsilon=1e-5, momentum=0.1, training=True, gamma_initializer=g_initializer)
def sigmoid_cross_entropy_with_logits(x, y):
return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y)
В дискриминаторной части X и Y сращиваются по каналам, и получаются после множественных сверток30*30*1
Дискриминантная карта, которая является идеей PatchGAN, и Dense только с одним нейроном до
def discriminator(x, y, reuse=None):
with tf.variable_scope('discriminator', reuse=reuse):
x = tf.concat([x, y], axis=3)
h0 = lrelu(d_conv(x, 64, 2)) # 128 128 64
h0 = d_conv(h0, 128, 2)
h0 = lrelu(batch_norm(h0)) # 64 64 128
h0 = d_conv(h0, 256, 2)
h0 = lrelu(batch_norm(h0)) # 32 32 256
h0 = d_conv(h0, 512, 1)
h0 = lrelu(batch_norm(h0)) # 31 31 512
h0 = d_conv(h0, 1, 1) # 30 30 1
h0 = tf.nn.sigmoid(h0)
return h0
В части генератора передняя и задняя части Unet содержат по 8 слоев свертки, а первые три слоя свертки во второй половине используют Dropout, Слой Dropout случайным образом удаляет некоторые нейроны с определенной вероятностью в процессе обучения, чтобы предотвратить эффект переобучения
def generator(x):
with tf.variable_scope('generator', reuse=None):
layers = []
h0 = g_conv(x, 64)
layers.append(h0)
for filters in [128, 256, 512, 512, 512, 512, 512]:
h0 = lrelu(layers[-1])
h0 = g_conv(h0, filters)
h0 = batch_norm(h0)
layers.append(h0)
encode_layers_num = len(layers) # 8
for i, filters in enumerate([512, 512, 512, 512, 256, 128, 64]):
skip_layer = encode_layers_num - i - 1
if i == 0:
inputs = layers[-1]
else:
inputs = tf.concat([layers[-1], layers[skip_layer]], axis=3)
h0 = tf.nn.relu(inputs)
h0 = g_deconv(h0, filters)
h0 = batch_norm(h0)
if i < 3:
h0 = tf.nn.dropout(h0, keep_prob=0.5)
layers.append(h0)
inputs = tf.concat([layers[-1], layers[0]], axis=3)
h0 = tf.nn.relu(inputs)
h0 = g_deconv(h0, 3)
h0 = tf.nn.tanh(h0, name='g')
return h0
Функция потерь, потери G плюс L1
g = generator(X)
d_real = discriminator(X, Y)
d_fake = discriminator(X, g, reuse=True)
vars_g = [var for var in tf.trainable_variables() if var.name.startswith('generator')]
vars_d = [var for var in tf.trainable_variables() if var.name.startswith('discriminator')]
loss_d_real = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_real, tf.ones_like(d_real)))
loss_d_fake = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_fake, tf.zeros_like(d_fake)))
loss_d = loss_d_real + loss_d_fake
loss_g_gan = tf.reduce_mean(sigmoid_cross_entropy_with_logits(d_fake, tf.ones_like(d_fake)))
loss_g_l1 = tf.reduce_mean(tf.abs(Y - g))
loss_g = loss_g_gan + loss_g_l1 * LAMBDA
Определите оптимизатор
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
optimizer_d = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_d, var_list=vars_d)
optimizer_g = tf.train.AdamOptimizer(learning_rate=0.0002, beta1=0.5).minimize(loss_g, var_list=vars_g)
Обучите модель
sess = tf.Session()
sess.run(tf.global_variables_initializer())
loss = {'d': [], 'g': []}
for i in tqdm(range(100000)):
k = i % X_all.shape[0]
X_batch, Y_batch = X_all[k:k + batch_size, :, :, :], Y_all[k:k + batch_size, :, :, :]
_, d_ls = sess.run([optimizer_d, loss_d], feed_dict={X: X_batch, Y: Y_batch})
_, g_ls = sess.run([optimizer_g, loss_g], feed_dict={X: X_batch, Y: Y_batch})
loss['d'].append(d_ls)
loss['g'].append(g_ls)
if i % 1000 == 0:
print(i, d_ls, g_ls)
gen_imgs = sess.run(g, feed_dict={X: X_batch})
result = np.zeros([HEIGHT, WIDTH * 3, 3])
result[:, :WIDTH, :] = (X_batch[0] + 1) / 2
result[:, WIDTH: 2 * WIDTH, :] = (Y_batch[0] + 1) / 2
result[:, 2 * WIDTH:, :] = (gen_imgs[0] + 1) / 2
plt.axis('off')
plt.imshow(result)
imsave(os.path.join(OUTPUT_DIR, 'sample_%d.jpg' % i), result)
plt.show()
plt.plot(loss['d'], label='Discriminator')
plt.plot(loss['g'], label='Generator')
plt.legend(loc='upper right')
plt.savefig('Loss.png')
plt.show()
Результат показан на следующем рисунке Три изображения слева направо: исходное изображение, реальное изображение и сгенерированное изображение.
Сохраните модель для использования на одной машине
saver = tf.train.Saver()
saver.save(sess, './pix2pix_diy', global_step=100000)
Загрузите модель на одну машину и переведите картинки в вал
# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np
from imageio import imread, imsave
import glob
images = glob.glob('data/val/*.jpg')
X_all = []
Y_all = []
WIDTH = 256
HEIGHT = 256
N = 10
images = np.random.choice(images, N, replace=False)
for image in images:
img = imread(image)
img = (img / 255. - 0.5) * 2
# B2A
X_all.append(img[:, WIDTH:, :])
Y_all.append(img[:, :WIDTH, :])
X_all = np.array(X_all)
Y_all = np.array(Y_all)
print(X_all.shape, Y_all.shape)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver = tf.train.import_meta_graph('./pix2pix_diy-100000.meta')
saver.restore(sess, tf.train.latest_checkpoint('./'))
graph = tf.get_default_graph()
g = graph.get_tensor_by_name('generator/g:0')
X = graph.get_tensor_by_name('X:0')
gen_imgs = sess.run(g, feed_dict={X: X_all})
result = np.zeros([N * HEIGHT, WIDTH * 3, 3])
for i in range(N):
result[i * HEIGHT: i * HEIGHT + HEIGHT, :WIDTH, :] = (X_all[i] + 1) / 2
result[i * HEIGHT: i * HEIGHT + HEIGHT, WIDTH: 2 * WIDTH, :] = (Y_all[i] + 1) / 2
result[i * HEIGHT: i * HEIGHT + HEIGHT, 2 * WIDTH:, :] = (gen_imgs[i] + 1) / 2
imsave('facades翻译结果.jpg', result)
встроенное колесо
Посмотрите, какие встроенные колеса предусмотрены проектом,GitHub.com/аффинный слой…
обработать изображение в256*256
размер,input_dir
представляет исходный каталог изображений,output_dir
Указывает каталог изображений после обработки единого размера
python tools/process.py --input_dir input_dir --operation resize --output_dir output_dir
Подготовьте данные сопряжения X и Y (две папки хранят X и Y соответственно, имена и размеры соответствующих изображений одинаковы) и объедините изображения попарно, как фасады.
python tools/process.py --input_dir X_dir --b_dir Y_dir --operation combine --output_dir combine_dir
получитьcombine_dir
Затем можно обучить модель перевода парного изображения pix2pix.
python pix2pix.py --mode train --output_dir model_dir --max_epochs 200 --input_dir combine_dir --which_direction AtoB
-
mode
: рабочий режим,train
Представляет обученную модель -
output_dir
: Выходной путь модели -
max_epochs
: Количество тренировочных раундов (разница между эпохой и итерацией) -
input_dir
: Комбинированный путь к изображению -
which_direction
: Направление перевода, слева направо или справа налево
Во время обучения модели и после обучения модели вы можете использовать тензорную доску для просмотра деталей обучения.
tensorboard --logdir=model_dir
После обучения модели переведите тестовые данные
python pix2pix.py --mode test --output_dir output_dir --input_dir input_dir --checkpoint model_dir
-
mode
: рабочий режим,test
означает тест -
output_dir
: путь вывода результата перевода -
input_dir
: путь к тестируемому изображению. -
checkpoint
: Путь модели, полученный в результате предыдущего обучения.
Если вы хотите обучить цветовую модель, вам не нужен вышеупомянутый шаг объединения изображений, вам нужно только предоставить папку с цветным изображением, потому что соответствующее изображение в градациях серого может быть автоматически извлечено из цветного изображения.
python pix2pix.py --mode train --output_dir model_dir --max_epochs 200 --input_dir combine_dir --lab_colorization
Проект также предоставляет несколько обученных парных моделей перевода изображений.
- Здания: от аннотации к реальности
- Просмотр улиц: двусторонний
- Карта: двусторонняя
- Обувь: от грани к реальности
- Сумки: от грани к реальности
раскрашивание пейзажных картинок
Используя следующий набор данных,Lear.in ria LP ES Поместите /~ в /data…, некоторые фотографии туристических пейзажей, которые были обработаны в256*256
Размер, разделенный на две части: обучающая и тестовая, содержащие 750 и 62 изображения соответственно.
Используйте изображения в поезде для обучения модели раскрашивания
python pix2pix.py --mode train --output_dir photos/model --max_epochs 200 --input_dir photos/data/train --lab_colorization
Используйте изображения в тесте для проверки, модель будет генерировать изображения в оттенках серого и цветные изображения, соответствующие каждому цветному изображению, и записывать все результаты окрашивания на веб-страницу.
python pix2pix.py --mode test --output_dir photos/test --input_dir photos/data/test --checkpoint photos/model
Результаты окрашивания следующие: слева направо: изображение в градациях серого, цветное изображение и исходное изображение.
Ссылаться на
- Преобразование изображения в изображение с условными состязательными сетями:phillipi.github.io/pix2pix/
- pix2pix-тензорный поток:GitHub.com/аффинный слой…