本文给出简单代码实现风格迁移。

1,原理简介

  风格迁移和上篇文章提到的deep dream算法比较接近,都是根据某种优化指标计算梯度来反向优化输入图像的像素。所以在学完deep dream之后趁热打铁又学了这个,但本文仅限于基础版的实现,对该领域后来发展出的诸多进化版不做讨论。
  基于深度学习的风格迁移最早由 Gatys于2015年提出,其核心理论是使用格拉姆矩阵(gram matrix)来提取图像的风格。gram matrix的计算方法是对图像的某中间层特征features(尺寸为CHW),reshape为C*(HW)后,再和自己的转置做矩阵乘法,就可以得到一个CC的矩阵,这个矩阵是原矩阵的偏心协方差矩阵,它去掉了像素级的信息,而表达了各通道之间的相关性。我们从deep dream的分析中已经认识到,中间层特征的各通道表达了图像特征的不同维度,比如有的表示尖顶建筑,有的表示黑色条纹,等等。如果我们把两个通道特征按像素顺序相乘再求总和,就表示这两种特征同时出现或同时不出现的程度。比如尖顶建筑总是和黑色条纹同时出现,那么这就是图像的某种偏好,也就是它的风格。从这个分析中我们可以看出,这样定义的风格是与图像中特征出现的位置无关的,但与两种特征是否同时出现有关,这个定义非常巧妙。但我总觉得这个定义也并不是非常完备,这应该只是一种简单的风格,而更多实际风格应该比这要复杂的多。
  不管怎么说,从数量上提取出了“风格”这个量之后,我们就可以操作它了,可以玩出各种花,最常用的是我们把风格从一张图像迁移到另一张图像上。算法原理如下图(二手中转自知乎):

图1.风格迁移算法原理

  先选择一张风格图片 style image,和一张内容图片 content image,然后还需要给定一张初始种子图片,可以是一张噪声图,也可以就是content image,当然也可以是一张其他图片,效果各不相同。然后先让风格图片和内容图片从网络中过一遍,风格图片则从网络的多个中间层提取出特征图,计算各个层的风格矩阵(gram matrix),因为浅层的风格比较基础,深层的风格比较高级,都有用。(实际上我觉得把浅层和深层也交叉计算一下gram matrix应该也有用,因为有时候浅层和深层特征也会同时出现,这个留作有空做一下试验)。然后再把内容图片从网络中过一下,只提取其中后层的某层特征,因为浅层特征本身就含有更多的风格意味,我们迁移风格的时候不需要保留它们。最后我们再把初始图片从网络中迭代的循环通过,每次正向通过时计算各对应层的风格,和风格图的风格比较得到风格损失,计算对应层的内容,和内容图比较得到内容损失。论文中作者又加入了第三种损失,即下面代码中的TV loss,表示生成图像的光滑程度,防止图像失真。根据综合损失求输入图的梯度,即可以迭代的优化输入图,得到一张风格和风格图一致,内容和内容图一致,又保有种子图片结构(如果种子图不是随机噪声)的新的图片。
  下面还是看完整代码吧。

2,完整代码

import torch
import torchvision.models as models
import torch.nn.functional as F
import torch.nn as nn
import numpy as np
import numbers
import math
import cv2
from PIL import Image
from torchvision.transforms import Compose, ToTensor, Normalize, Resize, ToPILImage
import time
t0 = time.time()model = models.vgg19(pretrained=True).cuda()
batch_size = 1for params in model.parameters():params.requires_grad = False
model.eval()mu = torch.Tensor([0.485, 0.456, 0.406]).unsqueeze(-1).unsqueeze(-1).cuda()
std = torch.Tensor([0.229, 0.224, 0.225]).unsqueeze(-1).unsqueeze(-1).cuda()
unnormalize = lambda x: x*std + mu
normalize = lambda x: (x-mu)/std
transform_test = Compose([Resize((512,512)),ToTensor(),
])content_img = Image.open('./data/tubingen.jpg')
image_size = content_img.size
content_img = transform_test(content_img).unsqueeze(0).cuda()style_img = Image.open('./data/starry_night.jpg')
style_img = transform_test(style_img).unsqueeze(0).cuda()var_img = content_img.clone()
#var_img = torch.rand_like(content_img)
var_img.requires_grad=Trueclass ShuntModel(nn.Module):def __init__(self, model):super().__init__()self.module = model.features.cuda().eval()self.con_layers = [22]self.sty_layers = [1,6,11,20,29]for name, layer in self.module.named_children():if isinstance(layer, nn.MaxPool2d):self.module[int(name)] = nn.AvgPool2d(kernel_size = 2, stride = 2)def forward(self, tensor: torch.Tensor) -> dict:sty_feat_maps = []; con_feat_maps = [];x = normalize(tensor)for name, layer in self.module.named_children():x = layer(x);if int(name) in self.con_layers: con_feat_maps.append(x)if int(name) in self.sty_layers: sty_feat_maps.append(x)return {"Con_features": con_feat_maps, "Sty_features": sty_feat_maps}model = ShuntModel(model)
sty_target = model(style_img)["Sty_features"]
con_target = model(content_img)["Con_features"]
gram_target = []
for i in range(len(sty_target)):b, c, h, w  = sty_target[i].size()tensor_ = sty_target[i].view(b * c, h * w)gram_i = torch.mm(tensor_, tensor_.t()).div(b*c*h*w)gram_target.append(gram_i)optimizer = torch.optim.Adam([var_img], lr = 0.01, betas = (0.9,0.999), eps = 1e-8)
lam1 = 1e-3; lam2 = 1e7; lam3 = 5e-3
for itera in range(20001):optimizer.zero_grad()output = model(var_img)sty_output = output["Sty_features"]con_output = output["Con_features"]con_loss = torch.tensor([0]).cuda().float()for i in range(len(con_output)):con_loss = con_loss + F.mse_loss(con_output[i], con_target[i])sty_loss = torch.tensor([0]).cuda().float()for i in range(len(sty_output)):b, c, h, w  = sty_output[i].size()tensor_ = sty_output[i].view(b * c, h * w)gram_i = torch.mm(tensor_, tensor_.t()).div(b*c*h*w)sty_loss = sty_loss + F.mse_loss(gram_i, gram_target[i])b, c, h, w  = style_img.size()TV_loss = (torch.sum(torch.abs(style_img[:, :, :, :-1] - style_img[:, :, :, 1:])) +torch.sum(torch.abs(style_img[:, :, :-1, :] - style_img[:, :, 1:, :])))/(b*c*h*w)loss = con_loss * lam1 + sty_loss * lam2 + TV_loss * lam3loss.backward()var_img.data.clamp_(0, 1)optimizer.step()if itera%100==0:print('itera: %d, con_loss: %.4f, sty_loss: %.4f, TV_loss: %.4f'%(itera,con_loss.item()*lam1,sty_loss.item()*lam2,TV_loss.item()*lam3),'\n\t total loss:',loss.item())print('var_img mean:%.4f, std:%.4f'%(var_img.mean().item(),var_img.std().item()))print('time: %.2f seconds'%(time.time()-t0))if itera%1000==0:    save_img = var_img.clone()save_img = torch.clamp(save_img,0,1)save_img = save_img[0].permute(1,2,0).data.cpu().numpy()*255save_img = save_img[...,::-1].astype('uint8')  #注意cv2使用BGR顺序save_img = cv2.resize(save_img,image_size)cv2.imwrite('./data/output1/transfer%d.jpg'%itera,save_img)

3,效果

图2.效果图   通常还是用原图做种子效果好一些。

  该方法的速度较慢,通常要好几分钟才能生成一幅图片。后续的改进论文中有快速生成算法,也有按照区域迁移(例如避免把天空中的风格迁移到建筑上)使效果更好等等诸多改进,暂时先不打算学了。(这两年AI坑挖的太快,知识量太多,只能先粗学一遍)
  这个技术还多少有点用处,即使用本文这个基础版代码,在网上随便找一些风格图片简单处理一下,也可以生成一些还不错的图片,下面是我宝贝女儿的照片进行处理后的一些结果。

图3.效果图


4,补充(2021.4.13)

  上面提到可以把多层的特征合并后统一计算格拉姆矩阵,这个实现并不难,所以今天顺手就试了一下,发现有效。

图4.使用统一格拉姆矩阵提取风格

  和图3最右图对比,可以发现,使用统一格拉姆矩阵时转移了风格图上更多更细节的风格,比如:鼻尖上出现黄点,眼睛、眉毛的形状更像风格图,脸上涂色更像风格图,等。但是这种方法更加占用显存(内存),计算速度也慢很多,根据具体需要选择吧。

pytorch实现风格迁移 style transfer相关推荐

  1. 风格迁移 Style transfer

    本文个人博客访问地址: 点击查看 一.介绍 将一张图片的艺术风格应用在另外一张图片上 使用深度卷积网络CNN提取一张图片的内容和提取一张图片的风格, 然后将两者结合起来得到最后的结果 二. 方法 - ...

  2. 通过PyTorch实现风格迁移

    一.环境及数据集准备 pytorch 1.1.0 torchvision 0.3.0 cuda 9.0 数据集用的是COCO2014的train2014训练集,使用ImageNet也可以 需要用到在I ...

  3. 使用pytorch进行风格迁移,以艺术绘画的风格来生成一些好玩的图片

    pytorch官网 https://pytorch.org/tutorials/advanced/neural_style_tutorial.html 原理 风格迁移的原理基于卷积神经网络的特征提取能 ...

  4. 使用 PyTorch 进行 风格迁移(Neural-Transfer)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 1.简介 本教程主要讲解如何实现由 Leon A. Gatys,A ...

  5. MM2018/风格迁移-Style Separation and Synthesis via Generative Adversarial Networks通过生成性对抗网络进行风格分离和合成

    Style Separation and Synthesis via Generative Adversarial Networks通过生成性对抗网络进行风格分离和合成 0.摘要 1.概述 2.相关工 ...

  6. CV Code | 本周新出计算机视觉开源代码汇总(含自动驾驶目标检测、医学图像分割、风格迁移、语义分割、目标跟踪等)...

    点击我爱计算机视觉标星,更快获取CVML新技术 刚刚过去的一周含五一假期,工作日第一天,CV君汇总了过去一周计算机视觉领域新出的开源代码,涉及到自动驾驶目标检测.医学图像分割.风格迁移.神经架构搜索. ...

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

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

  8. 让你的图片更有逼格,用Python实现图片风格迁移

    点击上方 Python知识圈,选择置顶或星标 第一时间关注 Python 技术干货! 文章转自R语言和Python学堂,禁二次转载 阅读文本大概需要 3 分钟. 先来看下效果: 上图是小编在甘南合作的 ...

  9. 用Python深度学习来快速实现图片的风格迁移

    先来看下效果: 上图是小编在甘南合作的米拉日巴佛阁外面拍下的一张照片,采用风格迁移技术后的效果为: 一些其它效果图: 下面进入正题. 近年来,由深度学习所引领的人工智能(AI)技术浪潮,开始越来越广泛 ...

最新文章

  1. Windows server 2012 搭建×××图文教程(一)安装×××相关服务
  2. linux内核提供的内存操作函数
  3. 那个傻子的网站突然打不开虚拟光驱
  4. 一下代码不符合php,各位帮我看看哪里错了一直提示错误,谢谢大家。可把代码复制一下...
  5. ORA-12541 TNS:无监听程序问题解决
  6. cp、mv、rm、find、mkdir
  7. Django框架(二)---- 常用命令
  8. 一文搞懂Spring Cloud Zuul
  9. 鸿蒙系统 Hi3861 实现手机APP配网功能
  10. 截图工具-Excel加载宏(图片大小可调)
  11. 路由器中的DMZ功能是什么?功能及设置方法详解
  12. web(html运行)设置字体大小、样式及字体名称
  13. 帝国CMS7.5基于es(Elasticsearch)7.x的全文搜索插件
  14. 消费者怎么看待 then, catch, finally
  15. 获取京东cookie最简单暴力获取京东CK
  16. 小米node2红外_关于小米人体传感器2的抢先体验报告
  17. 环境问题:fatal error LNK1318: 非意外的 PDB 错误
  18. 开源办公OA开发:新版考勤管理介绍
  19. 【智能零售】在逛了100家店之后,我从Stitch Fix上买到了我最喜爱的衣服...
  20. 使用server酱进行服务器端口异常报警

热门文章

  1. Word文档使用Mathtype如何实现公式自动居中并右对齐编号?
  2. java 全角_java字符全角半角转换
  3. cc1101初始化c语言程序,STC89C52单片机驱动CC1101无线模块的接收C语言程序
  4. 莫纳什大学计算机专业排名,2019上海软科世界一流学科排名计算机科学与工程专业排名莫纳什大学排名第151-200...
  5. C#重载函数与可选参数简单实例
  6. 智慧安全3.0的融合之道
  7. marvin java_java-与MarvinFramework比较图像
  8. 高薪程序员面试题精讲系列28之你熟悉哪些设计模式?
  9. ARP Cache Poisoning Attack Lab(SEED实验)
  10. GIF录制编辑工具(GifCam)