What is Style Transfer

Style Transfer, 风格转移,即把图片A变成图片B的风格,但保持图片A的内容不变,举个栗子,假设下图左是你自己的作品,中图是梵高的星空,右图则是风格转移算法的结果。

How to do it?

风格转移的实现方法很多,复杂的算法使用到GANs,其结果会非常的精美,但会需要(十分)强大的算力(和钱)。一些改进的方法能够显著将GANs类模型的计算成本控制在合理范围,例如将一张图片切割成很多小的方块,对每个小块进行风格转移,最后再将它们拼起来。

但实际上存在一种更简单更快速的方法——不需要用到GANs、只需要借助pre-trained image classification CNN,即可完成风格转换。理解其原理之前,必须先简单提一下CNN是如何完成image classification的,我们知道CNN classifier是由很多很多的CNN神经网络组成的,不同的CNN的kernel size不一样,较浅的layers的kernel size都比较小,这样可以抓取一些细微的特征,例如动物的毛发、金属表面的质感等等,较深的layers的kernel size会逐渐增大,这样可以抓取一些更完整的特征,例如眼睛、尾巴、车轮等等。可以发现,刚刚描述的较浅的layers描述的接近于一张图片的styles——油画画笔的texture、色块的分布等等,而较深的layers则描述的更多的是一张图片的内容——眼睛、尾巴、车轮等等。于是一个简单的风格转移模型就是利用CNN classifier不同的layer抓取的内容不一这个特点来实现快速、低成本的风格转移。

具体实现方法

需要利用的工具:VGG19(in Tensorflow), Python
(我们甚至不需要GPU)

Flow

  1. 将input图片feed进一个pre-trained image architecture, like VGG or ResNet.

  2. 计算Loss
    1)Content:把content image的content layer, Fl∈Rm,nF^{l} \in \mathcal{R}^{m ,n}Fl∈Rm,n提取出来,将content layer变平成一个向量fl∈Rm∗n,1\mathbf{f}^{l} \in \mathcal{R}^{m*n,1}fl∈Rm∗n,1;将生成的图片Pl∈Rm,nP^{l} \in \mathcal{R}^{m ,n}Pl∈Rm,n也做同样的变平处理成一个向量pl∈Rm∗n,1\mathbf{p}^{l} \in \mathcal{R}^{m*n,1}pl∈Rm∗n,1,那么content loss就是fl\mathbf{f}^{l}fl和pl\mathbf{p}^{l}pl的Euclidean Norm:

    Lcontent(p,f,l)=12∑i,j(Fi,jl−Pi,jl)2L_{content}(\mathbf{p},\mathbf{f},l)=\frac{1}{2}\sum_{i,j}(F_{i,j}^l-P_{i,j}^l)^2Lcontent​(p,f,l)=21​∑i,j​(Fi,jl​−Pi,jl​)2

    2)Style Loss:两个向量的点乘可以表现这两个向量有多相似(即同方向),当我们把两个flattened feature vector点乘时,这个乘积也代表了某个feature vector在某个方向上是否相似,需要注意的是,由于图形这个张量被flatten成一个向量,故点乘并不能展示spatial信息,而只能描述更加细微的texture。

    Lstyle=Gi,jl=∑kFi,klFj,klL_{style}=G^l_{i,j}=\sum_k F^l_{i,k}F^l_{j,k}Lstyle​=Gi,jl​=∑k​Fi,kl​Fj,kl​
    其中G代表Gram matrix,即两个向量的outer product组成的矩阵

    3)A somewhat intuitive explaination w.r.t. why use difference in content loss and dot product in style loss:The content feature extracted from VGG is like greyscaled sketches of the content image. 即FcontentF_{content}Fcontent​可以想象成黑白的勾勒content的线条,所以当我们想比较生成的图片是否具备FcontentF_{content}Fcontent​所代表的content,我们只需要检查某个pixel上,是否存在一个相似的pixel的值。而style的话是一种local texture,可以想象在一副油画中,笔刷刷出来的质感,或者像梵高的星空这幅画,你会看到大面积的螺旋状的gradient,所以比起是否或高或低的像素值,我更在意这些像素它们变化的方向是否和style image一致,而这种方向可以很好的被dot product给capture。

  3. 计算Gradients w.r.t. input image pixels PPP。注意这个gradients不会被back propagate到VGG的weights上,而是back propagate给input图片,VGG的weights全程保持不变。

Implementation

首先我们load content image and style image,注意这里用的VGG,VGG的input是224X224,所以需要把它们都裁成224X224。

content_image = #load your content image here
style_image = #load your style image here

我们load VGG19 model from Keras

import tensorflow as tf
vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')

可以看一下VGG19里面有哪些layers

print([layer.name for layer in vgg.layers])block1_conv1
block1_conv2
block1_pool
block2_conv1
block2_conv2
block2_pool
block3_conv1
block3_conv2
block3_conv3
block3_conv4
block3_pool
block4_conv1
block4_conv2
block4_conv3
block4_conv4
block4_pool
block5_conv1
block5_conv2
block5_conv3
block5_conv4
block5_pool

虽然看起来很普通,但这一步就是奇迹发生的时刻,我们从VGG19里pick了content layer和style layer。(try picking different layers to represent content and loss, and see what you get)

content_layers = ['block5_conv2']
style_layers = ['block1_conv1','block2_conv1','block3_conv1','block4_conv1','block5_conv1']
num_content_layers = len(conten_layers)
num_style_layers = len(style_layers)

写一个function把Layers给wrap up一下

def vgg_layers(layer_names):"""creates a vgg model that returns a list of intermediate output values"""vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')vgg.trainable = False #锁住VGG的参数不变,因为我们想训练的不是参数,而是inputoutputs = [vgg.get_layer(name).output for layer in layer_names] #vgg.get_layer(name).output 是一个tensor placeholder,下面vgg.input同理,因为VGG19的input必须是224X224,所以vgg.input也是这个size的tensor placeholdermodel = tf.keras.Model([vgg.input],outputs)return model
style_extractor = vgg_layers(style_layers)
style_outputs = style_extractor(style_image*255)

利用Gram matrix计算style loss的function,这个就是前文提到的feature vector的dot product。我们选取了5个CNN block的第一层CNN作为style feature vector,计算这些feature vector和其它feature vectors(包括它们自己)的dot product,并组成一个(num_of_feature_vector *num_of_feature_vector)的矩阵,这个过程可以用gram_matrix来实现:

Gcdl=∑ijFijcl(x)Fijdl(x)IJG^l_{cd}=\frac{\sum_{ij}F^l_{ijc}(x)F^l_{ijd}(x)}{IJ}Gcdl​=IJ∑ij​Fijcl​(x)Fijdl​(x)​

def gram_matrix(input_tensor):#(b,i,j,c)=(batch_size, ith row, jth col, cth color channel)result = tf.linalg.einsum('bijc,bijd->bcd', input_tensor, input_tensor)input_shape = tf.shape(input_tensor)# 第一个dim[0]是batch_size,所以IJ=dim[1]*dim[2]num_locations = tf.cast(input_shape[1]*input_shape[2],tf.float32)return result/num_locations

Wrap loss into the model:

class StyleContentModel(tf.keras.models.Model):def __init__(self, style_layers, content_layers):super(StyleContentModel, self).__init__()self.vgg = vgg_layers(style_layers+content_layers)self.style_layers = style_layersself.content_layers = content_layersself.num_style_layers = len(style_layers)self.vgg.trainable = Falsedef call(self, inputs):"""float input form [0,1]"""inputs = input*255.0preprocessed_input = tf.keras.applications.vgg19.preprocess_input(inputs)outputs = self.vgg(preprocessed_input)style_outputs, content_outputs = (outputs[:self.num_style_layers],outputs[self.num_style_layers:])style_outputs = [gram_matrix(style_output) for style_output in style_outputs]content_dict = {content_name:value for content_name,value in zip(self.content_layers, content_outputs)}style_dict = {style_name:value for style_name,value in zip(self.style_layers, style_outputs)}return {'content':content_dict, 'style':style_dict}extractor = StyleContentModel(style_layers, content_layers)

计算gradient,开始backpropagate:

style_targets = extractor(style_image)['style']
content_targets = extractor(content_image)['content']# initialize a placeholder tensor, with the same dimension of content image
image = tf.Variable(content_image)#输入进模型的数据都*255了,所以这里还原成[0,1]
def clip_0_1(image):return tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0)#定义一个optimizer
opt = tf.optimizers.Adam(learning_rate=0.02, beta_1=0.99, epsilon=1e-1)# 定义style和content的loss在总loss各占多少比重
style_weight, content_weight=1e-2, 1e4
def style_content_loss(outputs):style_outputs = outputs['style']content_outputs = outputs['content']style_loss = tf.add_n([tf.reduce_mean((style_outputs[name]-style_targets[name])**2) for name in style_outputs.keys()])style_loss *= style_weight/num_style_layerscontent_loss = tf.add_n([tf.reduce_mean((content_outputs[name]-content_targets[name])**2) for name in content_outputs.keys()])content_loss *= content_weight/num_content_layersreturn style_loss + content_loss

到这里就是全部的setup了,后续就是一个tf.session开始训练,感兴趣的可以去链接2继续看看,这里就不继续copy&paste了(不然都没办法tag成原创了hhh),所以就到此为止了,感谢阅读。

参考:
https://arxiv.org/abs/1508.06576
https://www.tensorflow.org/tutorials/generative/style_transfer

简单快速的Style Transfer(利用VGG19)-笔记相关推荐

  1. Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization论文阅读笔记

    Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization论文阅读笔记 目录 Arbitrary Style ...

  2. 学习笔记:Controllable Artistic Text Style Transfer via Shape-Matching GAN 基于形状匹配生成对抗网络的可控艺术文本风格迁移

    [ICCV-2019] Controllable Artistic Text Style Transfer via Shape-Matching GAN 基于形状匹配生成对抗网络的可控艺术文本风格迁移 ...

  3. 吴恩达老师深度学习视频课笔记:神经风格迁移(neural style transfer)

            什么是神经风格迁移(neural style transfer):如下图,Content为原始拍摄的图像,Style为一种风格图像.如果用Style来重新创造Content照片,神经风 ...

  4. AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder Loss笔记

    文章目录 网络结构 说话人编码器 内容编码器 解码器 声码器 实验 论文: AUTOVC: Zero-Shot Voice Style Transfer with Only Autoencoder L ...

  5. 论文笔记-Real-Time MDE using Synthetic Data with Domain Adaptation via Image Style Transfer

    论文信息 标题: Real-Time Monocular Depth Estimation using Synthetic Data with Domain Adaptation via Image ...

  6. 李宏毅nlp学习笔记06:Text Style Transfer

    1.Text Style Transfer 可以把消极的消息变成积极的消息: 进行的应该是无监督的学习. 以把消极的句子转为积极的句子为例. G:模型是消极的模型转化为积极的模型 D:则应该能够判断转 ...

  7. 【论文笔记】An Unsupervised Style Transfer Framework for Profanity Redaction

    Towards A Friendly Online Community: An Unsupervised Style Transfer Framework for Profanity Redactio ...

  8. CVPR-Drafting and Revision: Laplacian Pyramid Network for Fast High-Quality Artistic Style Transfer

    [CVPR-2021] Drafting and Revision: Laplacian Pyramid Network for Fast High-Quality Artistic Style Tr ...

  9. [Style Transfer]——Joint Bilateral Learning for Real-time Universal Photorealistic Style Transfer

    Joint Bilateral Learning for Real-time Universal Photorealistic Style Transfer 基于联合双边学习的通用写实风格转换
 fr ...

最新文章

  1. 树形dp ---- C. Karen and Supermarket(子树选择类型dp + 注意dp更新顺序)
  2. 使用JSARToolKit5 开发AR应用 (2) Marker
  3. 合成孔径成像算法与实现_西安光机所光学成像研究取得进展
  4. 《JavaScript 高级程序设计》 7.5 常用模式
  5. yum php 降级 5.3,CentOS 5.3 通过yum升级php的方法
  6. BEC translation exercise 4
  7. tinymce移动端使用_关于在移动端避免使用100vh的原因及解决方案
  8. webpack使用七
  9. python鼠标移动_python自动化之鼠标移动
  10. python3爬虫实战一: 爬取豆瓣最新上映电影及画出词云分布
  11. ctfshow-Misc入门 图片篇(1-49)
  12. 卡内基梅隆大学计算机专业系,卡内基梅隆大学
  13. matlab在常微分方程的应用,MATLAB在求解常微分方程中的应用
  14. 路由器ACL(访问控制列表)详解
  15. ESXI 6.7 虚拟机配置端口聚合
  16. VMware提示【该虚拟机似乎正在使用中】的解决方案
  17. C#windows竞赛管理系统
  18. mysql COMMENT字段注释,表注释使用(转)
  19. Makefile教程(超级详细)
  20. java基于springboot足球联赛管理系统

热门文章

  1. JAVA SpringBoot从零到商用化校园商铺平台的开发实战(毕设)
  2. Mask Scoring RCNN
  3. TCA-JDA 公式推导学习记录
  4. Direct3D 实例
  5. 单出口双防火墙双核心冗余_大型自动化物流系统之冗余设计
  6. PDF怎么修改,如何调整PDF页面大小
  7. 记录nubia z17miniS 刷机
  8. Google VR Views实战(VR全景图播放器)
  9. 【Arduino】一个按键开关控制RGB音乐彩灯(蜂鸣器播放音乐+按键控制播放暂停+RGB彩灯随音乐节奏跳动)
  10. [机缘参悟-44]:鬼谷子-第八摩篇- 摩者,顺而抚之也, 摩得其情,则顺而抚之,以成其事.