TensorFlow 10——ch07-图像风格迁移

代码:https://github.com/MONI-JUAN/Tensorflow_Study/tree/master/TensorFlow/ch07-图像风格迁移

一、概念

  • 内容损失和风格损失

    • 内容损失(Content Loss) L c o n t e n t L_{content} Lcontent描述原始图像和生成图像在 内容 上的差异;
    • 风格损失(Style Loss)Gram矩阵(卷积层特征)描述原始图片中的 风格
  • 用途

    • 利用内容损失还原图像内容;

    • 利用封给说你是还原图像风格;

  • 风格迁移:还原图像的时候还原令一张图像的风格

  • 原始图像风格迁移 对比 快速图像风格

    • 原始:

      L t o t a l ( p → , a → , x → ) L_{total}(\overrightarrow{p},\overrightarrow{a},\overrightarrow{x}) Ltotal(p ,a ,x ) 衡量 x → \overrightarrow{x} x 是否成功组合了 p → \overrightarrow{p} p a → \overrightarrow{a} a 的风格,以 L t o t a l L_{total} Ltotal 为目标进行梯度下降迭代 x → \overrightarrow{x} x ,速度慢。

    • 快速:

      使用神经网络直接生成 x → \overrightarrow{x} x ,速度快。
      在这里插入图片描述

    类型损失定义是否需要训练新网络生成图像的方法
    原始内容损失 L c o n t e n t L_{content} Lcontent
    风格损失 L s t y l e L_{style} Lstyle
    否,只需要预训练好的VGGNet利用损失,通过梯度下降计算合适的图像
    快速内容损失 L c o n t e n t L_{content} Lcontent
    风格损失 L s t y l e L_{style} Lstyle
    是,除了预训练好的VGGNet还需要训练图像生成网络利用训练好的图像生成网络直接生成

二、实现快速风格迁移

1.下载训练好的图像生成网络

百度云链接:https://pan.baidu.com/s/11t5Vs3GHryyF1EHwow0dkA
提取码:amka

新建一个文件夹models,把七个model放进去

cubist.ckpt-done
denoised_starry.ckpt-done
feathers.ckpt-done
mosaic.ckpt-done
scream.ckpt-done
udnie.ckpt-done
wave.ckpt-done

2.修改代码

eval.py:

def main():
	#输出res的时候记录模型和原图的名字
    name_model = FLAGS.model_file.split('/')[-1].split('.')[0]
    name_img = FLAGS.image_file.split('/')[-1].split('.')[0]
	'''
	'''
    generated_file = 'generated/res_[%s]_[%s].jpg' % (name_model, name_img)

model.py:

def conv2d(x, input_filters, output_filters, kernel, strides, mode='REFLECT'):
    with tf.variable_scope('conv'):
        shape = [kernel, kernel, input_filters, output_filters]
        weight = tf.Variable(tf.truncated_normal(shape, stddev=0.1), name='weight')
        # kernel 是float类型的,在新版本的tensorflow需要np.int()一下才行
        x_padded = tf.pad(x, [[0, 0], [np.int(kernel / 2), np.int(kernel / 2)], [np.int(kernel / 2), np.int(kernel / 2)], [0, 0]], mode=mode)
        return tf.nn.conv2d(x_padded, weight, strides=[1, strides, strides, 1], padding='VALID', name='conv')

3.生成风格图像

python eval.py --model_file models/wave.ckpt-done --image_file img/test.jpg

在这里插入图片描述

在这里插入图片描述

例子中还有四个原始图
在这里插入图片描述
其中风格分别为:

  • cubist

    在这里插入图片描述
  • denoised_starry

    在这里插入图片描述
  • feathers

    在这里插入图片描述
  • mosaic

    在这里插入图片描述
  • scream

在这里插入图片描述
  • udnie

    在这里插入图片描述
  • wave

    在这里插入图片描述

这里列出了五张图片在七种模型下的所有结果

在这里插入图片描述

python eval.py --model_file models/wave.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/wave.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/wave.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/wave.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/wave.ckpt-done --image_file img/test5.jpg

python eval.py --model_file models/cubist.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/cubist.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/cubist.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/cubist.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/cubist.ckpt-done --image_file img/test5.jpg

python eval.py --model_file models/denoised_starry.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/denoised_starry.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/denoised_starry.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/denoised_starry.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/denoised_starry.ckpt-done --image_file img/test5.jpg

python eval.py --model_file models/feathers.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/feathers.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/feathers.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/feathers.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/feathers.ckpt-done --image_file img/test5.jpg

python eval.py --model_file models/mosaic.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/mosaic.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/mosaic.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/mosaic.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/mosaic.ckpt-done --image_file img/test5.jpg

python eval.py --model_file models/scream.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/scream.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/scream.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/scream.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/scream.ckpt-done --image_file img/test5.jpg

python eval.py --model_file models/udnie.ckpt-done --image_file img/test1.jpg
python eval.py --model_file models/udnie.ckpt-done --image_file img/test2.jpg
python eval.py --model_file models/udnie.ckpt-done --image_file img/test3.jpg
python eval.py --model_file models/udnie.ckpt-done --image_file img/test4.jpg
python eval.py --model_file models/udnie.ckpt-done --image_file img/test5.jpg

三、训练自己的模型

1.下载预训练模型和数据集

  • 下载VGG16模型

    地址: http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz

    百度云链接:https://pan.baidu.com/s/11t5Vs3GHryyF1EHwow0dkA
    提取码:amka

    新建文件夹 pretrained ,把 vgg_16.ckpt 放进去

  • 下载COCO数据集,有12.6G

    地址: http://msvocds.blob.core.windows.net/coco2014/train2014.zip

    解压放到文件夹 train2014

    Linux可以不用移动,直接建立连接:

    ln -s <到 train2014 文件路径> train2014
    

2.训练wave模型

  • 训练模型:
python train.py -c conf/wave.yml

​ 其中 conf/wave.yml 是配置文件:

## Basic configuration
style_image: img/wave.jpg # 输入原始风格图像的文件名
naming: "wave" # 风格/模型的名字
model_path: models  # checkpoint 和 events 文件保存的根目录

## Weight of the loss
content_weight: 1.0  # 内容损失的权重
style_weight: 220.0  # 风格损失的权重
tv_weight: 0.0  # total variation loss

## The size, the iter number to run
image_size: 256   # 原始图片的大小
batch_size: 4     # 一次 batch 的样本数
epoch: 2  # epoch 的运行次数

## Loss Network
loss_model: "vgg_16" # 使用 vgg_16 的模型
content_layers:  # 使用 conv3_3 定义内容损失
  - "vgg_16/conv3/conv3_3"
style_layers:  # 使用 conv1_2、conv2_2、conv3_3、conv4_3 定义风格损失
  - "vgg_16/conv1/conv1_2"
  - "vgg_16/conv2/conv2_2"
  - "vgg_16/conv3/conv3_3"
  - "vgg_16/conv4/conv4_3"
checkpoint_exclude_scopes: "vgg_16/fc"  # 只需要卷积层,不需要fc层
loss_model_file: "pretrained/vgg_16.ckpt"  # 预训练对应的位置

​ 读者如果希望训练新的“风格”,可以选取一张风格图片,并编写新的 yml 配置文件。其中,需要把 style_image 修改为新图片所在的位置,并修改对应的 naming 。 这样就可以进行训练了。最后可以使用训练完成的 checkpoint 生成图片。
​ 在训练 、新的“风格”时,有可能会需要调整各个损失之间的权重。

  • 查看训练情况:
tensorboard --logdir models/wave/
  • 可能需要调整权重
    • content_weight 过大,图像会更接近原始图像
    • style_weight 过大,图像的风格更接近原始图像

3.实现细节

但是因为数据集太大,一直都下不下来,这里就先放弃了。只看了看代码

有百度云资源的可以发一下!感激涕零!

1)生成网络的定义

models.py

def net(image, training):
    # 图片加上一圈边框,消除边缘效应
    image = tf.pad(image, [[0, 0], [10, 10], [10, 10], [0, 0]], mode='REFLECT')

    # 三层卷积层
    with tf.variable_scope('conv1'):
        conv1 = relu(instance_norm(conv2d(image, 3, 32, 9, 1)))
    with tf.variable_scope('conv2'):
        conv2 = relu(instance_norm(conv2d(conv1, 32, 64, 3, 2)))
    with tf.variable_scope('conv3'):
        conv3 = relu(instance_norm(conv2d(conv2, 64, 128, 3, 2)))
    
    # 仿照 ResNet 定义一些跳过的链接
    with tf.variable_scope('res1'):
        res1 = residual(conv3, 128, 3, 1)
    with tf.variable_scope('res2'):
        res2 = residual(res1, 128, 3, 1)
    with tf.variable_scope('res3'):
        res3 = residual(res2, 128, 3, 1)
    with tf.variable_scope('res4'):
        res4 = residual(res3, 128, 3, 1)
    with tf.variable_scope('res5'):
        res5 = residual(res4, 128, 3, 1)
    # print(res5.get_shape())

    # 定义反卷积,先放大再卷积可以消除噪声
    with tf.variable_scope('deconv1'):
        # deconv1 = relu(instance_norm(conv2d_transpose(res5, 128, 64, 3, 2))) #不直接转置
        deconv1 = relu(instance_norm(resize_conv2d(res5, 128, 64, 3, 2, training)))
    with tf.variable_scope('deconv2'):
        # deconv2 = relu(instance_norm(conv2d_transpose(deconv1, 64, 32, 3, 2))) #不直接转置
        deconv2 = relu(instance_norm(resize_conv2d(deconv1, 64, 32, 3, 2, training)))
    with tf.variable_scope('deconv3'):
        # deconv_test = relu(instance_norm(conv2d(deconv2, 32, 32, 2, 1))) #不直接转置
        deconv3 = tf.nn.tanh(instance_norm(conv2d(deconv2, 32, 3, 9, 1)))

    # 经过了 tanh 激活,将[-1,1]缩放到[0,255]像素值范围
    y = (deconv3 + 1) * 127.5

    # 去除边框
    height = tf.shape(y)[1]
    width = tf.shape(y)[2]
    y = tf.slice(y, [0, 10, 10, 0], tf.stack([-1, height - 20, width - 20, -1]))
    
    return y
2)生成网络的引用

train.py

"""Build Network"""
# 损失网络
network_fn = nets_factory.get_network_fn(
	FLAGS.loss_model,
	num_classes=1,
	is_training=False) # 不需要对损失函数训练

# 图像和与处理函数,不需要训练
image_preprocessing_fn, image_unprocessing_fn = preprocessing_factory.get_preprocessing(
	FLAGS.loss_model, is_training=False)

# 读入训练图像
processed_images = reader.image(FLAGS.batch_size, FLAGS.image_size, FLAGS.image_size,
								'train2014/', image_preprocessing_fn, epochs=FLAGS.epoch)

# 引用生成网络,生成图像,这里需要训练
generated = model.net(processed_images, training=True)

# 将生成图像使用 image_preprocessing_fn 处理
processed_generated = [
	image_preprocessing_fn(image, FLAGS.image_size, FLAGS.image_size)
	for image in tf.unstack(generated, axis=0, num=FLAGS.batch_size)
]
processed_generated = tf.stack(processed_generated)

# 将原始图和生成图送到损失网络,加快速度
_, endpoints_dict = network_fn(tf.concat([processed_generated, processed_images], 0), spatial_squeeze=False)

# Log the structure of loss network
tf.logging.info('Loss network layers(You can define them in "content_layers" and "style_layers"):')
for key in endpoints_dict:
	tf.logging.info(key)
3)内容损失

loss.py

# endpoints_dict 是损失网络各层的计算结果
# content_layers 是定义使用哪些层的差距计算损失
def content_loss(endpoints_dict, content_layers):
    content_loss = 0
    for layer in content_layers:
        generated_images, content_images = tf.split(endpoints_dict[layer], 2, 0)# 把生成图像分开
        size = tf.size(generated_images)
        # 生成图片的激活 与 原始图片的激活 的L^2距离
        content_loss += tf.nn.l2_loss(generated_images - content_images) * 2 / tf.to_float(size)  
    return content_loss
4)风格损失

loss.py

# endpoints_dict 是损失网络各层的计算结果
# style_features_t 是利用原始的风格图片计算的层的激活
# style_layers 是定义使用哪些层计算损失
def style_loss(endpoints_dict, style_features_t, style_layers):
    style_loss = 0
    style_loss_summary = {} # 为tensorboard服务的
    for style_gram, layer in zip(style_features_t, style_layers):
        generated_images, _ = tf.split(endpoints_dict[layer], 2, 0) # 计算风格损失
        size = tf.size(generated_images)
        # 计算 Gram 矩阵, L^2(Loss)
        layer_style_loss = tf.nn.l2_loss(gram(generated_images) - style_gram) * 2 / tf.to_float(size)
        style_loss_summary[layer] = layer_style_loss
        style_loss += layer_style_loss
    return style_loss, style_loss_summary
5)调用损失

train.py

"""Build Losses"""
# 定义内容损失
content_loss = losses.content_loss(endpoints_dict, FLAGS.content_layers)
# 定义风格损失
style_loss, style_loss_summary = losses.style_loss(endpoints_dict, style_features_t, FLAGS.style_layers)
# 定义tv损失,但是因为tv_weight=0,所以没用
tv_loss = losses.total_variation_loss(generated)  
# 总损失
loss = FLAGS.style_weight * style_loss + FLAGS.content_weight * content_loss + FLAGS.tv_weight * tv_loss
6)定义训练、保存的变量

train.py

"""Prepare to Train"""
global_step = tf.Variable(0, name="global_step", trainable=False)

variable_to_train = [] # 找出需要训练的变量,append进去
for variable in tf.trainable_variables():
	if not(variable.name.startswith(FLAGS.loss_model)):
		variable_to_train.append(variable)
# 定义, global_step=global_step 不会训练损失网络
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss, global_step=global_step, var_list=variable_to_train)

variables_to_restore = [] # 找出需要保存的变量,append进去
for v in tf.global_variables():
	if not(v.name.startswith(FLAGS.loss_model)):
		variables_to_restore.append(v)
# 定义,只保存 variables_to_restore
saver = tf.train.Saver(variables_to_restore, write_version=tf.train.SaverDef.V1)

sess.run([tf.global_variables_initializer(), tf.local_variables_initializer()])
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值