图像风格迁移技术


✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭~✨✨

🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。

我是Srlua小谢,在这里我会分享我的知识和经验。🎥

希望在这里,我们能一起探索IT世界的奥妙,提升我们的技能。🔮

记得先点赞👍后阅读哦~ 👏👏

📘📚 所属专栏:传知代码论文复现

欢迎访问我的主页:Srlua小谢 获取更多信息和资源。✨✨🌙🌙

​​

​​

目录

概述

内容 & 风格表示

内容

风格

演示效果

核心逻辑

训练模型

使用方式

部署方式


概述

图像风格迁移是指将一张图像的内容与另一张图像的风格相融合,生成具有新风格的图像。

需要本文的详细复现过程的项目源码、数据和预训练好的模型可从该地址处获取完整版:地址

  • 风格迁移这一想法与纹理生成的想法密切相关,在 2015 年开发出神经风格迁移之前,这一想法就已经在图像处理领域有着悠久的历史。但事实证明,与之前经典的计算机视觉技术实现相比,基于深度学习的风格迁移实现得到的结果是无与伦比的,并且还在计算机视觉的创造性应用中引发了惊人的复兴。

  • 风格迁移其主要应用场景如在艺术创作场景,将不同艺术风格应用于图像,可以创造出独特的艺术效果,使作品具有新的视觉呈现。或者在社交平台上风格化滤镜,图像增强等。

内容 & 风格表示

我们需要让输出的目标图像既符合原始图像和目标风格,就需要内容和风格进行表示。

  • 在深度学习实现上,我们需要确定我们的的目标是什么,就是保存原始图像的内容,同时采用参考图像的风格。那么就有一个适当的损失函数将对其进行最小化,如下: 在这里插入图片描述 这里的 distance 是一个范数函数(用来衡量向量或矩阵的大小或长度),比如 L2 范数;content 是一个函数,输入一张图像,并计算出其内容的表示;style 是一个函数,输入一张图像,并计算出其风格的表示。将这个损失最小化,会使得 style(generated_image) 接近于 style(reference_image)、content(generated_image) 接近于 content(generated_image),从而实现我们定义的风格迁移。

内容

  • 在CNN卷积网络进行对象识别任务中,随着层次的加深对象的信息输出更加的明确,较前的层数特征图输出到一些更加通用的结构,比如猫狗分类中的基础边缘线条,而更深的层可以捕捉到更加全局和抽象的结构,如猫耳,猫眼睛。根据这个模式,我们可以通过不同深度的层特征图重建输入图像以可视化层所包含输入图像的信息,如下图可以看到深层的特征图包含了图像中对象的全局排列信息(高级、抽象),但是像素值信息会丢失。浅层的层特征图重建图像几乎是完整的精确像素值。

在这里插入图片描述 这样一来在内容上的损失函数便是在一个预训练模型(需要在大型数据集下训练好的能有效提取特征的模型)同一深的层激活的范式数,这样至少在深的层看来,二者图像的内容是一致的。

风格

  • 同样的,不同深度的层特征图包含了不同空间尺度的输入图像的信息,基于此Gatys等人在每一层特征图中采用格拉姆矩阵(Gram matrix)(描述特征图之间的相关性),即特征值的内积,以在不同空间尺度下提取不同滤波器的特征图之间的相互关系(纹理),得到输入图像的多尺度的纹理信息(不捕获全局排列信息)。如上图层激活所包含的风格信息(颜色和局部排列信息),abcde图为不同子集的CNN层的风格重建图像的风格,(“conv1 1”(a)、“conv1 1”和“conv2 1”(b)、“conv1 1”、“conv2 1”和“conv3 1”(c)、“conv1 1”、“conv2 1”、“conv3 1”和“conv4 1”(d)、“conv1 1”、“conv2 1”、“conv3 1”、“conv4 1”和“conv5 1”(e)) 这会输出与参考图像风格越来越模式匹配的输出,且会忘记其全局排列信息.

通过使输出图像和风格图像在不同尺度的层激活都有相似的相互关系,即不同尺度的纹理都很相同,便可以实现风格的迁移。

此外,在风格和内容上我们还需要定义贡献强度,这里我们可以通过损失函数的权重来实现即可

通过这两种构建损失函数进行优化,便可以使得模型输出图像符合在深层次中“看到”的图像内容是一致的,不同层中特征图的内部特征相互关系的是相似的以实现目标

演示效果

在这里插入图片描述在这里插入图片描述在这里插入图片描述

核心逻辑

神经风格迁移流程一般为 计算层激活 -> 计算损失函数 -> 梯度下降最优化损失函数

网络层激活:输出目标图像,原始图像,参考图像的层激活输出 计算损失函数: 通过所求的层激活计算对应的内容和风格损失函数 梯度下降优化:通过梯度下降的方式减小损失函数,训练模型参数使得模型能学习到参考图像风格的纹理和保持内容的不变 具体在实现上来说流程如下

使图像尺寸大小相似(如果差异很大,由于模型中尺度变换会使得风格迁移很麻烦)

读取图像, 加载预训练模型,得到层激活

根据层激活构建需要最小化的最终损失(需要注意的是在重建图像需要添加总变差损失平滑图像)

在图像重建或图像去噪任务中,通常将总变差损失与其他损失函数(如均方误差损失)相结合,通过权衡平滑性和重建准确性。

设置梯度下降优化算法(论文所实现的方案为L-BFGS 算法,)

训练模型

以下为核心代码

<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">from</span> <span style="color:#000000">keras</span> <span style="color:#770088">import</span> <span style="color:#000000">backend</span> <span style="color:#770088">as</span> <span style="color:#000000">K</span>
​
<span style="color:#aa5500"># 获取原始图像和参考风格图像 不变设置为常量</span>
<span style="color:#000000">target_image</span> <span style="color:#981a1a">=</span> <span style="color:#000000">K</span>.<span style="color:#000000">constant</span>(<span style="color:#000000">preprocess_image</span>(<span style="color:#000000">target_image_path</span>))
<span style="color:#000000">style_reference_image</span> <span style="color:#981a1a">=</span> <span style="color:#000000">K</span>.<span style="color:#000000">constant</span>(<span style="color:#000000">preprocess_image</span>(<span style="color:#000000">style_reference_image_path</span>))
​
<span style="color:#aa5500"># 目标生成图像占位符(可变)默认为float32 </span>
<span style="color:#000000">combination_image</span> <span style="color:#981a1a">=</span> <span style="color:#000000">K</span>.<span style="color:#000000">placeholder</span>((<span style="color:#116644">1</span>, <span style="color:#000000">img_height</span>, <span style="color:#000000">img_width</span>, <span style="color:#116644">3</span>))
​
<span style="color:#aa5500"># 合并为一个批量(这里是为了符合VGG19的批次(3,width,height,3)</span>
<span style="color:#000000">input_tensor</span> <span style="color:#981a1a">=</span> <span style="color:#000000">K</span>.<span style="color:#000000">concatenate</span>([<span style="color:#000000">target_image</span>,
                              <span style="color:#000000">style_reference_image</span>,
                              <span style="color:#000000">combination_image</span>], <span style="color:#000000">axis</span><span style="color:#981a1a">=</span><span style="color:#116644">0</span>)
​
<span style="color:#aa5500"># 官方文档 https://keras.io/api/applications/vgg/#vgg19-function </span>
<span style="color:#000000">model</span> <span style="color:#981a1a">=</span> <span style="color:#000000">vgg19</span>.<span style="color:#000000">VGG19</span>(<span style="color:#000000">input_tensor</span><span style="color:#981a1a">=</span><span style="color:#000000">input_tensor</span>,
                    <span style="color:#000000">weights</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'imagenet'</span>,
                    <span style="color:#000000">include_top</span><span style="color:#981a1a">=</span><span style="color:#770088">False</span>)
<span style="color:#3300aa">print</span>(<span style="color:#aa1111">'Model loaded.'</span>)
<span style="color:#3300aa">print</span>(<span style="color:#000000">model</span>.<span style="color:#000000">summary</span>())
​
<span style="color:#000000">outputs_dict</span> <span style="color:#981a1a">=</span> <span style="color:#3300aa">dict</span>([(<span style="color:#000000">layer</span>.<span style="color:#000000">name</span>, <span style="color:#000000">layer</span>.<span style="color:#000000">output</span>) <span style="color:#770088">for</span> <span style="color:#000000">layer</span> <span style="color:#770088">in</span> <span style="color:#000000">model</span>.<span style="color:#000000">layers</span>])
​
<span style="color:#aa5500"># 内容格式的层激活</span>
<span style="color:#000000">content_layer</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">'block5_conv4'</span>
<span style="color:#aa5500"># 风格的CNN层激活</span>
<span style="color:#000000">style_layers</span> <span style="color:#981a1a">=</span> [<span style="color:#aa1111">'block1_conv1'</span>,
                <span style="color:#aa1111">'block2_conv1'</span>,
                <span style="color:#aa1111">'block3_conv1'</span>,
                <span style="color:#aa1111">'block4_conv1'</span>,
                <span style="color:#aa1111">'block5_conv1'</span>]
<span style="color:#000000">outputs_dict</span>
<span style="color:#770088">from</span> <span style="color:#000000">scipy</span>.<span style="color:#000000">optimize</span> <span style="color:#770088">import</span> <span style="color:#000000">fmin_l_bfgs_b</span>
<span style="color:#aa5500"># from scipy.misc import imsavefrom  import </span>
<span style="color:#770088">import</span> <span style="color:#000000">time</span>
​
<span style="color:#000000">result_prefix</span> <span style="color:#981a1a">=</span> <span style="color:#aa1111">'style_transfer_result'</span>
<span style="color:#000000">iterations</span> <span style="color:#981a1a">=</span> <span style="color:#116644">10</span> <span style="color:#aa5500"># 更深的轮次 效果越深(风格迁移越强)</span>
​
<span style="color:#aa5500"># Run scipy-based optimization (L-BFGS) over the pixels of the generated image</span>
<span style="color:#aa5500"># so as to minimize the neural style loss.</span>
<span style="color:#aa5500"># This is our initial state: the target image.</span>
<span style="color:#aa5500"># Note that `scipy.optimize.fmin_l_bfgs_b` can only process flat vectors.</span>
<span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#000000">preprocess_image</span>(<span style="color:#000000">target_image_path</span>)
<span style="color:#000000">x</span> <span style="color:#981a1a">=</span> <span style="color:#000000">x</span>.<span style="color:#000000">flatten</span>()
<span style="color:#770088">for</span> <span style="color:#000000">i</span> <span style="color:#770088">in</span> <span style="color:#3300aa">range</span>(<span style="color:#000000">iterations</span>):
    <span style="color:#3300aa">print</span>(<span style="color:#aa1111">'Start of iteration'</span>, <span style="color:#000000">i</span>)
    <span style="color:#000000">start_time</span> <span style="color:#981a1a">=</span> <span style="color:#000000">time</span>.<span style="color:#000000">time</span>()
    <span style="color:#000000">x</span>, <span style="color:#000000">min_val</span>, <span style="color:#000000">info</span> <span style="color:#981a1a">=</span> <span style="color:#000000">fmin_l_bfgs_b</span>(<span style="color:#000000">evaluator</span>.<span style="color:#000000">loss</span>, <span style="color:#000000">x</span>,
                                     <span style="color:#000000">fprime</span><span style="color:#981a1a">=</span><span style="color:#000000">evaluator</span>.<span style="color:#000000">grads</span>, <span style="color:#000000">maxfun</span><span style="color:#981a1a">=</span><span style="color:#116644">20</span>)
    <span style="color:#3300aa">print</span>(<span style="color:#aa1111">'Current loss value:'</span>, <span style="color:#000000">min_val</span>)
    <span style="color:#aa5500"># Save current generated image</span>
    <span style="color:#000000">img</span> <span style="color:#981a1a">=</span> <span style="color:#000000">x</span>.<span style="color:#000000">copy</span>().<span style="color:#000000">reshape</span>((<span style="color:#000000">img_height</span>, <span style="color:#000000">img_width</span>, <span style="color:#116644">3</span>))
    <span style="color:#000000">img</span> <span style="color:#981a1a">=</span> <span style="color:#000000">deprocess_image</span>(<span style="color:#000000">img</span>)
    <span style="color:#000000">fname</span> <span style="color:#981a1a">=</span> <span style="color:#000000">result_prefix</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">'_at_iteration_%d.png'</span> <span style="color:#981a1a">%</span> <span style="color:#000000">i</span>
    <span style="color:#000000">keras</span>.<span style="color:#000000">preprocessing</span>.<span style="color:#000000">image</span>.<span style="color:#000000">save_img</span>(<span style="color:#000000">fname</span>, <span style="color:#000000">img</span>)
    <span style="color:#000000">end_time</span> <span style="color:#981a1a">=</span> <span style="color:#000000">time</span>.<span style="color:#000000">time</span>()
    <span style="color:#3300aa">print</span>(<span style="color:#aa1111">'Image saved as'</span>, <span style="color:#000000">fname</span>)
    <span style="color:#3300aa">print</span>(<span style="color:#aa1111">'Iteration %d completed in %ds'</span> <span style="color:#981a1a">%</span> (<span style="color:#000000">i</span>, <span style="color:#000000">end_time</span> <span style="color:#981a1a">-</span> <span style="color:#000000">start_time</span>))</span></span>

使用方式

在线Jupyter notebook代码运行

修改上传图片路径(内容图像和参考风格图像路径) 修改损失权重大小比例可实现对应的迁移强度(可选) 保存迁移后的图片(可选) 需要注意的是这个算法速度较慢,RTX1650几百K数据也要半小时左右,建议电脑空闲时间运行,后续小伙伴们感兴趣推出更高效的算法, Tips: 我们可以通过花费大量的时间参考某一个固定风格图像批量生成许多输入 - 输出样例图像,并训练一个小型前馈卷积神经网络学习这种变换,就可以非常快速进行这种变换(秒级)

部署方式

  • 版本:Python 3 , Tensorflow 2

  • 参考文献: Gatys, L. A., Ecker, A. S., & Bethge, M. (2016). A neural algorithm of artistic style. arXiv preprint arXiv:1508.06576.

  • 需要本文的详细复现过程的项目源码、数据和预训练好的模型可从该地址处获取完整版:地址

 ​​

希望对你有帮助!加油!

若您认为本文内容有益,请不吝赐予赞同并订阅,以便持续接收有价值的信息。衷心感谢您的关注和支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值