**

基于Tensorflow 2.0实现的图片风格迁移

**

  • 摘 要 神经风格迁移是一种优化技术,用于将两个图像(一个内容图像和一个风格参考图像)混合在一起,使输出的噪声图像看起来像内容图像,
    但是使用了风格参考图像的风格。 这是通过优化输出图像以匹配内容图像的内容统计数据和风格参考图像的风格统计数据来实现的。
    这些统计数据可以使用卷积网络从图像中提取。
    本次论文采用Tensorflow2.0实现简单的图片风格迁移,采用官方VGG19训练模型,对内容、风格图片进行训练,通过增加训练次数与噪声图片生成次数达到减小图片内容损失以及风格损失的目的;实验表明通过增加训练次数与噪声图片生成次数可以明显的减小图片的内容以及风格损失。

  • 关键词:神经网络;风格迁移;卷积网络;VGG19;Tensorflow2.0

**

1 绪论

**

  • 所谓图像风格迁移,即给定内容图片A,风格图片B,能够生成一张具有A图片内容和B图片风格的图片C。比如说,我们可以使用梵高先生的名画《星夜》
  • 作为风格图片,来与其他图片生成具有《星夜》风格新图片。举两个简单的例子:利用梵高的名画——《星夜》作为风格图片,一个常见的动漫图片作为内容图片。分别计算出内容图片与风格图片的内容特征,再基于风格图片生成的的噪声图片风格特征与内容图片融合生成一张新的噪声图片即风格迁移后的图片。

图1-1 风格图片以及迁移前后对比图

  • 针对原Demo对其训练次数以及训练后融合次数进行修改,将训练次数改为150次(原Demo为100次),融合次数改为30次(原Demo为20次)。随着训练次数和融合次数增加图片的内容损失以及风格损失有着明显的降低。下图为修改后对比图:

图1-2 训练次数与融合次数不同对比图
**

2 项目

**

2.1 项目器材

  • 联想拯救者Y7000笔记本Windows10系统,16GB内存,主硬盘128GB,CPU(Intel@Core i5-8300H),显卡(NVIDIA GTX 1050 Ti 4GB)

2.2 项目环境

  • Anaconda3-5.1.0+PyCharm+Python3.6

2.3 项目内容

2.3.1 项目包含文件

  • 本项目一共包含四个Python文件以及一个TXT文件,其中model.py文件为模型初始化文件,即调用官方VGG19模型文件;settings.py文件为整个项目设置相关参数的重要文件,主要设置内容特征层loss加权系数、风格特征层loss加权系数、内容图片和风格图片输入路径、生成图片输出路径、内容和特征总loss加权系数、输出图片大小以及训练次数等;utils.py文件主要作用为对图片进行解码、归一化处理;train.py文件主要作用为计算风格和内容特征、计算风格和内容loss、加速训练以及生成噪声图片(即风格迁移后的图片);requirements.txt文件可通过修改文件中涉及相关Python包的版本,达到在不同电脑上也能正常运行的目的。

2.3.2 实验过程

  • 首先查看运行设备的Python环境以及项目涉及的相关附件包版本,通过修改requirements.txt文件,将涉及包版本修改与运行设备中相关版本。如下图所示:

图2.3.2—1 修改requirements.txt文件

  • 其次通过settings.py文件设置图片的输入与输出位置和输出图片的大小以及相关训练epoch个数以及epoch训练次数。如下图所示:

图2.3.2—2 修改相关参数

  • 运行train.py文件,等待模型训练,训练结束后可在输出文件夹中查看输出风格迁移后的图片。如下图:

图2.3.2—3 风格迁移后的输出图片
**

3 项目所遇问题

**

3.1 版本兼容性问题

  • 由于python以及相关附件包版本不同,部分相关函数的调用方法有所改变。以至于在初步运行Demo时总是报错,其中报错最多的为找不到某一个附件包内所涉及的函数调用。通过在CSDN网站内大量查询解决办法,最终确定为版本不兼容,更新项目所涉及相关附件包后得以解决。
    其中在正式确定本次项目Demo前,在运行其它类似项目时遇到以下问题:

  • Tensorflow与keras版本有相关对应关系[1]。

  • 例如keras2.3.1对应tensorflow2.1、2.0.1.15.

  • Tensorflow与python版本有对应关系。[2]

  • 例如windows环境下tensorflow1.6.0对应python3.5-3.6

  • Pytorch和Torchvision以及python版本有严格对应关系[3]。

  • 例如torch1.4.0对应torchvision0.5.0,python2.7或者python3.5—3.8。

3.2 图片输出问题

  • 在原项目源码中发现对于风格迁移后的噪声图片输出大小做出了严格的限制(即将图片压缩后输出),在查询相关知识点后,发现可以通过python的图像处理库——PIL修改输出图片的大小[4];PIL提供了功能丰富的方法,比如格式转换、旋转、裁剪、改变尺寸、像素处理、图片合并等。源码将输出图片大小定为450*300,通过PIL库中Image读取内容噪声图片,再通过相关方法将输出噪声图片大小改为与内容图片大小。

图3.2—1 修改噪声图片输出大小

3.3 epoch训练问题

  • 原项目中epoch训练个数为20个,训练次数为100次,从输出的噪声图片看,输出的噪声图片对风格、内容特征的损失比较少,但再将epoch训练个数增加至30个、训练次数增加至150次后,输出的噪声图片的内容、风格特征损失达到一个理想值,当继续增加epoch训练个数和训练次数时提升并不明显,最终确定epoch训练个数为30个训练次数为150次。

结 论

  • 原项目中[5]重点提到了以梵高名画《星空》作为风格图片进行相关的图像风格融合,且风格与内容的损失比较少。但该项目对于其它风格的图像迁移也有着不错的表现,例如赛博朋克风格、素描风格等,其中赛博朋克风格在建筑上的表现更佳,对于其它类型的图片表现不够理想,素描风格的整体表现都不错。

  • 该项目所训练的模型不止能对于绘画内的风格迁移,摄影类也能很好的进行风格迁移,但对于摄影类(即真实类)的风格、内容损失较大,能够明显的感受到有很大的突兀感,不够真实。其绘画(素描)类的风格迁移表现不够理想,突兀感很强烈,风格损失过大。

赛博朋克凤格:

素描风格:

  • 该项目还有着很大的优化空间,当风格、内容图像特点比较多时,项目训练时间也会更长。后续可以通过提升电脑性能以及优化源码完成对训练时间的缩短。

参考文献

  • [1]tensorflow和keras版本对应关系(blog.csdn.net/weixin_40109345/article/details/106730050?spm=1001.2014.3001.5502)
  • [2]各操作系统与Tensorflow版本配套关系(blog.csdn.net/u014797226/article/details/80632440?spm=1001.2014.3001.5502)
  • [3]Pytorch和Torchvision版本对应(blog.csdn.net/qq_40263477/article/details/106577790?spm=1001.2014.3001.5502)
  • [4]Python「PIL」:调整图片大小(blog.csdn.net/qq_41297934/article/details/105302393?spm=1001.2014.3001.5502)
  • [5]有趣的深度学习——使用TensorFlow 2.0实现图片神经风格迁移
    (https://blog.csdn.net/aaronjny/article/details/104879258)

附录

  • model.py
import typing
import tensorflow as tf
import settings
def get_vgg19_model(layers):"""创建并初始化vgg19模型:return:"""# 加载imagenet上预训练的vgg19vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')# 提取需要被用到的vgg的层的outputoutputs = [vgg.get_layer(layer).output for layer in layers]# 使用outputs创建新的模型model = tf.keras.Model([vgg.input, ], outputs)# 锁死参数,不进行训练model.trainable = Falsereturn model
class NeuralStyleTransferModel(tf.keras.Model):def __init__(self, content_layers: typing.Dict[str, float] = settings.CONTENT_LAYERS,style_layers: typing.Dict[str, float] = settings.STYLE_LAYERS):super(NeuralStyleTransferModel, self).__init__()# 内容特征层字典 Dict[层名,加权系数]self.content_layers = content_layers# 风格特征层self.style_layers = style_layers# 提取需要用到的所有vgg层layers = list(self.content_layers.keys()) + list(self.style_layers.keys())# 创建layer_name到output索引的映射self.outputs_index_map = dict(zip(layers, range(len(layers))))# 创建并初始化vgg网络self.vgg = get_vgg19_model(layers)def call(self, inputs, training=None, mask=None):        outputs = self.vgg(inputs)# 分离内容特征层和风格特征层的输出,方便后续计算 typing.List[outputs,加权系数]content_outputs = []for layer, factor in self.content_layers.items():content_outputs.append((outputs[self.outputs_index_map[layer]][0], factor))style_outputs = []for layer, factor in self.style_layers.items():style_outputs.append((outputs[self.outputs_index_map[layer]][0], factor))# 以字典的形式返回输出return {'content': content_outputs, 'style': style_outputs}
  • settings.py
from PIL import Image
# 内容特征层及loss加权系数
CONTENT_LAYERS = {'block4_conv2': 0.5, 'block5_conv2': 0.5}
# 风格特征层及loss加权系数
STYLE_LAYERS = {'block1_conv1': 0.2, 'block2_conv1': 0.2, 'block3_conv1': 0.2, 'block4_conv1': 0.2,'block5_conv1': 0.2}
# 内容图片路径
CONTENT_IMAGE_PATH = './images/test_0.jpg'
# 风格图片路径
STYLE_IMAGE_PATH = './images/style_2.jpeg'
# 生成图片的保存目录
OUTPUT_DIR = './output/'# 内容loss总加权系数
CONTENT_LOSS_FACTOR = 1
# 风格loss总加权系数
STYLE_LOSS_FACTOR = 100# 加载内容图片
img_pillow = Image.open(CONTENT_IMAGE_PATH)# 图片宽度
#WIDTH = 450
WIDTH = img_pillow.width
# 图片高度
#HEIGHT = 300
HEIGHT = img_pillow.height
# 训练epoch数
EPOCHS = 1
# 每个epoch训练多少次
STEPS_PER_EPOCH = 100
# 学习率
LEARNING_RATE = 0.03
  • utils.py
import tensorflow as tf
import settings
# 我们准备使用经典网络在imagenet数据集上的与训练权重,所以归一化时也要使用imagenet的平均值和标准差
image_mean = tf.constant([0.485, 0.456, 0.406])
image_std = tf.constant([0.299, 0.224, 0.225])
def normalization(x):"""对输入图片x进行归一化,返回归一化的值"""return (x - image_mean) / image_std
def load_images(image_path, width=settings.WIDTH, height=settings.HEIGHT):"""加载并处理图片:param image_path: 图片路径:param width: 图片宽度:param height: 图片长度:return: 一个张量"""# 加载文件x = tf.io.read_file(image_path)# 解码图片x = tf.image.decode_jpeg(x, channels=3)# 修改图片大小x = tf.image.resize(x, [height, width])x = x / 255.# 归一化x = normalization(x)x = tf.reshape(x, [1, height, width, 3])# 返回结果return xdef save_image(image, filename):x = tf.reshape(image, image.shape[1:])x = x * image_std + image_meanx = x * 255.x = tf.cast(x, tf.int32)x = tf.clip_by_value(x, 0, 255)x = tf.cast(x, tf.uint8)x = tf.image.encode_jpeg(x)tf.io.write_file(filename, x)
  • train.py
import os
import numpy as np
from tqdm import tqdm
import tensorflow as tf
from model import NeuralStyleTransferModel
import settings
import utils
# 创建模型
model = NeuralStyleTransferModel()
# 加载内容图片
content_image = utils.load_images(settings.CONTENT_IMAGE_PATH)
# 风格图片
style_image = utils.load_images(settings.STYLE_IMAGE_PATH)
# 计算出目标内容图片的内容特征备用
target_content_features = model([content_image, ])['content']
# 计算目标风格图片的风格特征
target_style_features = model([style_image, ])['style']
M = settings.WIDTH * settings.HEIGHT
N = 3
def _compute_content_loss(noise_features, target_features):"""计算指定层上两个特征之间的内容loss:param noise_features: 噪声图片在指定层的特征:param target_features: 内容图片在指定层的特征"""content_loss = tf.reduce_sum(tf.square(noise_features - target_features))# 计算系数x = 2. * M * Ncontent_loss = content_loss / xreturn content_loss
def compute_content_loss(noise_content_features):"""计算并当前图片的内容loss:param noise_content_features: 噪声图片的内容特征"""# 初始化内容损失content_losses = []# 加权计算内容损失for (noise_feature, factor), (target_feature, _) in zip(noise_content_features, target_content_features):layer_content_loss = _compute_content_loss(noise_feature, target_feature)content_losses.append(layer_content_loss * factor)return tf.reduce_sum(content_losses)
def gram_matrix(feature):"""计算给定特征的格拉姆矩阵"""# 先交换维度,把channel维度提到最前面x = tf.transpose(feature, perm=[2, 0, 1])# reshape,压缩成2dx = tf.reshape(x, (x.shape[0], -1))# 计算x和x的逆的乘积return x @ tf.transpose(x)def _compute_style_loss(noise_feature, target_feature):"""计算指定层上两个特征之间的风格loss:param noise_feature: 噪声图片在指定层的特征:param target_feature: 风格图片在指定层的特征"""noise_gram_matrix = gram_matrix(noise_feature)style_gram_matrix = gram_matrix(target_feature)style_loss = tf.reduce_sum(tf.square(noise_gram_matrix - style_gram_matrix))# 计算系数x = 4. * (M ** 2) * (N ** 2)return style_loss / xdef compute_style_loss(noise_style_features):"""计算并返回图片的风格loss:param noise_style_features: 噪声图片的风格特征"""style_losses = []for (noise_feature, factor), (target_feature, _) in zip(noise_style_features, target_style_features):layer_style_loss = _compute_style_loss(noise_feature, target_feature)style_losses.append(layer_style_loss * factor)return tf.reduce_sum(style_losses)def total_loss(noise_features):"""计算总损失:param noise_features: 噪声图片特征数据"""content_loss = compute_content_loss(noise_features['content'])style_loss = compute_style_loss(noise_features['style'])return content_loss * settings.CONTENT_LOSS_FACTOR + style_loss * settings.STYLE_LOSS_FACTOR# 使用Adma优化器
optimizer = tf.keras.optimizers.Adam(settings.LEARNING_RATE)# 基于内容图片随机生成一张噪声图片
noise_image = tf.Variable((content_image + np.random.uniform(-0.2, 0.2, (1, settings.HEIGHT, settings.WIDTH, 3))) / 2)# 使用tf.function加速训练
@tf.function
def train_one_step():"""一次迭代过程"""# 求losswith tf.GradientTape() as tape:noise_outputs = model(noise_image)loss = total_loss(noise_outputs)# 求梯度grad = tape.gradient(loss, noise_image)# 梯度下降,更新噪声图片optimizer.apply_gradients([(grad, noise_image)])return loss# 创建保存生成图片的文件夹
if not os.path.exists(settings.OUTPUT_DIR):os.mkdir(settings.OUTPUT_DIR)# 共训练settings.EPOCHS个epochs
for epoch in range(settings.EPOCHS):# 使用tqdm提示训练进度with tqdm(total=settings.STEPS_PER_EPOCH, desc='Epoch {}/{}'.format(epoch + 1, settings.EPOCHS)) as pbar:# 每个epoch训练settings.STEPS_PER_EPOCH次for step in range(settings.STEPS_PER_EPOCH):_loss = train_one_step()pbar.set_postfix({'loss': '%.4f' % float(_loss)})pbar.update(1)# 每个epoch保存一次图片utils.save_image(noise_image, '{}/{}.jpg'.format(settings.OUTPUT_DIR, epoch + 1))
  • requirements.txt
tensorflow==2.0.0
numpy==1.19.5
tqdm==4.62.3

转载请注明来源

基于Tensorflow 2.0实现的图片风格迁移相关推荐

  1. 使用Tensorflow实现图片风格迁移,圆梦名画

    一,前期基础知识储备 1)Prisma - 图片风格迁移的鼻祖: 照片可以记录生活的瞬间,变成一幅幅的回忆:而 Prisma 则是可以让瞬间的回忆变成永恒的名画!我们平常用手机随意拍出来的照片效果看起 ...

  2. TensorFlow从1到2(十三)图片风格迁移

    风格迁移 <从锅炉工到AI专家(8)>中我们介绍了一个"图片风格迁移"的例子.因为所引用的作品中使用了TensorFlow 1.x的代码,算法也相对复杂,所以文中没有仔 ...

  3. TF之LiR:基于tensorflow实现手写数字图片识别准确率

    TF之LiR:基于tensorflow实现手写数字图片识别准确率 目录 输出结果 代码设计 输出结果 Extracting MNIST_data\train-images-idx3-ubyte.gz ...

  4. YOLO V3基于Tensorflow 2.0的完整实现

    如果对Tensorflow实现最新的Yolo v7算法感兴趣的朋友,可以参见我最新发布的文章,Yolo v7的最简TensorFlow实现_gzroy的博客-CSDN博客 YOLO V3版本是一个强大 ...

  5. 深度学习实战—基于TensorFlow 2.0的人工智能开发应用

    作者:辛大奇 著 出版社:中国水利水电出版社 品牌:智博尚书 出版时间:2020-10-01 深度学习实战-基于TensorFlow 2.0的人工智能开发应用

  6. 基于TensorFlow 2.0的中文深度学习开源书来了!GitHub趋势日榜第一,斩获2K+星

    十三 发自 凹非寺  量子位 报道 | 公众号 QbitAI TensorFlow 2.0 发布已有一个半月之久,你会用了吗? 近日,一个叫做深度学习开源书的项目在火了.GitHub趋势日榜排名全球第 ...

  7. tensorflow contrib模块_OpenCV DNN 模块-风格迁移

    本文主要介绍OpenCV的DNN模块的使用.OpenCV的DNN模块自从contrib仓库开始,就是只支持推理,不支持训练.但是仅仅只是推理方面,也够强大了.现在OpenCV已经支持TensorFlo ...

  8. 第十课.图片风格迁移和GAN

    目录 Neural Style Transfer Neural Style Transfer原理 准备工作 定义模型并加载预训练的模型参数 训练target以及结果可视化 生成对抗网络GAN GAN原 ...

  9. Pytorch入门+实战系列七:图片风格迁移和GAN

    Pytorch官方文档:https://pytorch.org/docs/stable/torch.html? 1. 写在前面 今天开始,兼顾Pytorch学习, 如果刚刚接触深度学习并且想快速搭建神 ...

  10. 用Python实现图片风格迁移,让你的图片更加的高逼格!

    先来看下效果: 上图是老王在甘南合作的米拉日巴佛阁外面拍下的一张照片,采用风格迁移技术后的效果为: 一些其它效果图: 下面进入正题. 如果你依然在编程的世界里迷茫,可以加入我们的Python学习扣qu ...

最新文章

  1. 红帽启动apache服务器_CentOS6.5环境下搭建Apache httpd服务器
  2. 使用noode.js创建一个服务器
  3. 网络营销的探索与爆发
  4. Eigen库基本操作
  5. 透露抖音、腾讯、阿里、美团招开发岗位硬核面试题,轻轻松松收到offer
  6. 直播预告:基于强化学习的关系抽取和文本分类 | PhD Talk #18
  7. intellij出现Initial job has not accepted any resources;
  8. [Linux环境]-centos7下安装jdk1.8.0_141流程.
  9. nginx 访问控制之 document_uri
  10. 怎么看待传菜机器人_比拼食材原料、使用机器人传菜,餐饮业如何把握大消费时代的机遇...
  11. DockOne微信分享( 一零二):基于容器的日志管理实践
  12. oracle数据库查询正在执行的sql,Oracle查询正在执行的SQL语句
  13. linux统计单拷贝基因家族,为什么所有病毒基因都是单拷贝
  14. 【YOLOX 论文+源码解读】YOLOX: Exceeding YOLO Series in 2021
  15. C# + opengl + Tao 环境配置
  16. npm install报错解决fatal: Unable to look up github.com (port 9418) npm ERR! exited with error code: 128
  17. lopa分析_SIS知识贴:一文看懂保护层分析暨LOPA分析
  18. 开传奇大概需要什么条件
  19. 机器学习中Batch Size、Iteration和Epoch的概念
  20. 使用虚拟显存方式操作12864液晶

热门文章

  1. 从TPCC看DM8常见集群架构开销
  2. 从亏损19亿到盈利6亿,恺英网络做对了什么?
  3. 014基于SSH航空订票系统air
  4. win7怎么设置开机密码_主编教您电脑开机密码怎么设置
  5. unb计算机科学,【加拿大新布伦瑞克大学】加拿大UNB_University of New Brunswick - 加拿大大学 - 加拿大留学云...
  6. 头条号小程序制作分享
  7. 路由器——交换机——网络交换机:区别
  8. 噪声概念:白噪声(n)、粉红噪声(1/f)
  9. 利用Google快讯和GoogleReader收集信息
  10. C#应用程序界面开发基础——窗体控制(4)——选择类控件(有部分地方没明白)