Image Style Transfer

这次笔者主要对基于tensorflow框架的代码进行理解分析:
《A Neural Algorithm of Artistic Style》
《Perceptual Losses for Real-Time Style Transfer and Super-Resolution》
《Texture Networks: Feed-forward Synthesis of Textures and Stylized Images》

==================读书笔记=======================
风格转移,输出的图片C = A(content) + B(style),就是将图片B的风格转移到图片C中,同时保留图片A的内容。这看起来像是PS就能实现的功能,直接将两个图层叠加融合即可。但其实不然,保留图片A的内容是所有CNN网络都可以实现的,但style是更为抽象的特征(直观上来看),是否可以通过更深层的卷积网络就能得到style的feature,这是一个值得探讨的问题。(当然,作者已经帮我们印证了这个答案,TUT)
所以说,这是一项非常有意思的工作,应该跟之前红极一时的prisma软件的工作原理差不多。

风格转移的两种方法

  1. 基于图片迭代的描述性神经网络
    这一方法会从随机噪声开始,通过反向传播迭代更新(尚未知晓的)风格化图像。图像迭代的目标是最小化总损失,这样风格化后的图像就能同时将内容图像的内容与风格图像的风格匹配起来。
    需要指定输入图片与风格图片,然后进行足够多的迭代才能有比较好的效果,每一次运行都是重新训练一个模型,且不可复用。
    论文中还提及,现有的描述性神经网络主要基于最大均值差(MMD)和基于马尔科夫随机场(MRF)两种方法来做。笔者数学不太好,对这两种方法并不是很了解,但个人感觉应该是用来刻画输出图片与内容图片、风格图片之间分布差异的方法
  2. 基于模型迭代的生成式神经网络
    这种方法更像是为了解决“基于图片迭代的描述性神经网络”在风格转移中效率过低而存在的,也被成为“快速”神经风格迁移,主要解决了前者的计算速度和计算成本问题。
    核心是先训练保存一些用户常用的“风格”参数。用户输入需要转换风格的图片A到网络中,再指定网络调用B风格的参数,输出的图片C其实只是网络最后一层的输出,中间不会涉及过多的参数调整及优化。
    与第一种方法对比,基于模型迭代的生成式神经网络更像是一个网络的“test”部分,其只是输出结果,做部分参数的调整,但优劣性不予保证;基于图片迭代的描述性神经网络就是一个网络的“train”部分,其需要进行多次的权重调整及优化,才能使得初始化的噪声图片有比较好的输出。

两种方法各有优劣,应用场景也是不同的:
第一种方法根据任意风格图片转换任意图片,甚至可以进行多种风格的混合(只要输入多张图片作为风格list);但速度是个硬伤,实验室Tesla K80 的GPU,居然花了40min才完成一张图片1000次的迭代。适用于用户愿意等待,且服务器有很强的计算能力能应付多个用户的并发操作。
第二种方法则是考虑了效率,输出一张效果不错的图片,只花了不到10s;但却失去了灵活性,用户只能从现有的“滤镜”(模型)中转换自己的图片。这更适用于客户端离线转换。


基于图片迭代的源码解析code

整体思路
源码架构.png
  1. 共用一个卷积网络(VGG-19),但上方示意图显示的是对风格图片的“style”特征进行提取,下方示意图显示的是对内容图片的“content”进行提取;这里有一个很有意思的问题,同一个网络,为什么能实现对风格图片的“style”特征提取,又能提取内容图片的“content”特征?
    答案是不同的layer。CNN网络的优势在于不同的layer有不同的含义,使用更高层的layer在理论上可以表达更抽象的特征,具体使用哪一层的特征表达,后面会再进行分析。
  2. 随机初始化一张噪声图片x,与内容图片p和风格图片a三者同时输入网络中训练,定义总的损失函数是关于x的非线性函数。目标是使x在内容上与p相近,在风格上与a相近。怎么实现?
    定义总的损失函数是关于x的,Total_loss = centent_loss + style_loss ;将这个loss最小化,利用梯度下降的方式,就能实现这个目标。
Centent Loss

content loss 计算公式

Fijl:第l层第i个filter上位置j处的激活值
Fl:随机噪声x在第l层的响应值
Pl:内容图片p在第l层的响应值
Lcontent :两者的误差平方和

利用CONTENT_LAYERS('relu4_2','relu5_2')作为“content”的layer表达

g = tf.Graph()
with g.as_default(), g.device('/cpu:0'), tf.Session() as sess:
    image = tf.placeholder('float', shape=shape)
    net = vgg.net_preloaded(vgg_weights, image, pooling)
    content_pre = np.array([vgg.preprocess(content, vgg_mean_pixel)])
    for layer in CONTENT_LAYERS:
        content_features[layer] = net[layer].eval(feed_dict={image: content_pre})

content loss 计算

    content_loss = 0
    content_losses = []
    for content_layer in CONTENT_LAYERS:
        content_losses.append(content_layers_weights[content_layer] * content_weight * (2 * tf.nn.l2_loss(
                net[content_layer] - content_features[content_layer]) /
                content_features[content_layer].size))
    content_loss += reduce(tf.add, content_losses)
Style Loss

Gijl:随机噪声x在第l层的响应值
Al:风格图片a在第l层的响应值
El:单层的“style”损失
Lstyle :所有层的“style”损失和

利用STYLE_LAYERS('relu1_1','relu2_1','relu3_1','relu4_1','relu5_1')作为“style”的layer表达。
可以看到,“Style”使用到的layer其实是要比“content”更低的。
或许可以这样解释,正如笔者上方强调的那样,“Style”从直观上来看应该是更为抽象的特征,应该采用更高的layer才能很好刻画出来;但其实“Style”在某种程度上来说其实是纹理信息,采用较为浅的layer才能保留这些有用的细节信息,从而忽略整体信息。(这里还有待考虑,代码体现出来的采用更浅的layer,但"style"应该属于更抽象的特征才对)

for i in range(len(styles)):
    g = tf.Graph()
    with g.as_default(), g.device('/cpu:0'), tf.Session() as sess:
        image = tf.placeholder('float', shape=style_shapes[i])
        net = vgg.net_preloaded(vgg_weights, image, pooling)
        style_pre = np.array([vgg.preprocess(styles[i], vgg_mean_pixel)])
        for layer in STYLE_LAYERS:
            features = net[layer].eval(feed_dict={image: style_pre})
            features = np.reshape(features, (-1, features.shape[3]))
            gram = np.matmul(features.T, features) / features.size
            style_features[i][layer] = gram

style loss 计算

    style_loss = 0
    for i in range(len(styles)):
        style_losses = []
        for style_layer in STYLE_LAYERS:
            layer = net[style_layer]
            _, height, width, number = map(lambda i: i.value, layer.get_shape())
            size = height * width * number
            feats = tf.reshape(layer, (-1, number))
            gram = tf.matmul(tf.transpose(feats), feats) / size
            style_gram = style_features[i][style_layer]
            style_losses.append(style_layers_weights[style_layer] * 2 * tf.nn.l2_loss(gram - style_gram) / style_gram.size)
        style_loss += style_weight * style_blend_weights[i] * reduce(tf.add, style_losses)

基于模型迭代的源码解析code

直接执行代码下的eval.py 文件,指定调用的风格,就可以生成风格转换后的图片,这就没什么要分析的必要了。不过基于模型迭代其实也是可以训练自己的风格网络的,主要在train.py文件中。具体的代码讲解再后续更新,笔者训练自己的风格网络的时候报错了。
不过大致的思想应该是这样的:

基于模型迭代的生成式神经网络示意图
  1. 采用VGG-16的网络,网络架构以及总损失函数与基于图片迭代的描述性神经网络相似
  2. 不再基于噪声生成新图片,而是直接基于输入的内容图片直接转换成特定的纹理风格
  3. loss network 的参数不再更新,只是基于图片做一个前馈计算,作者的解释是“Image Classification的pretrained的卷积模型已经很好的学习了perceptual和semantic information(场景和语义信息)”,有点“迁移学习”的意思

效果图

原图

wave.jpg

Oil painting

后记

相对来说这是篇比较仓促的笔记,简单调试了下代码就开始动笔写了,可能某些细节问题没有注意到,后续有时间会持续更新。不得不说,有这样的idea,并且实现了出来,真的非常了不起。
但通过上面的几张测试图片,我们也可以发现,这样的处理方法确实可以针对图片进行风格转换,但这转化的是“画”,并非图片,这样说的原因是转化过程中出现“局部扭曲”的现象非常明显,没有人会认为这是一张真实的照片,而且关键是没办法做到对特定部位的转换。

References

《Neural Style Transfer: A Review》
神经风格迁移研究概述:从当前研究到未来方向
TensorFlow之深入理解Fast Neural Style

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容