图像风格迁移及代码实现

article/2025/9/18 8:56:19

图像风格迁移其实非常好理解,就是将一张图像的“风格”(风格图像)迁移至另外一张图像(内容图像),但是这所谓的另外一张图像只是在“风格”上与之前有所不同,图像的“内容”仍要与之前相同。Luan et al. and Gatys et al. 的工作都是利用VGGNet19作为该项任务的backbone,由于VGGNet19是一种近似“金字塔”型结构,所以随着卷积操作的加深,feature maps的感受野越来越大,提取到的图像特征从局部扩展到了全局。我们为了避免合成的图像过多地保留内容信息,选取VGGNet19中位于金字塔顶部的卷积层作为内容层。整个训练过程为将生成图像初始化为内容图像,每次循环分别抽取生成图像和内容图像的内容特征,计算mse并且使之最小化,同时抽取生成图像和风格图像的样式特征,计算mse并且使之最小化。这里注意损失函数的写法:
在这里插入图片描述
总损失由两部分组成:内容损失和样式损失。内容损失即为生成图像和内容图像对应特征图的均方误差,但是样式损失需要分别计算生成图像和内容图像的格拉姆矩阵再做均方误差。另外, α \alpha α β \beta β分别为内容损失和样式损失的各项权重, Γ \Gamma Γ为样式损失的惩罚系数。我通过实发现 β \beta β Γ \Gamma Γ应该取的值大些,使得样式损失被尽可能地“惩罚”,即“放大”样式损失。

import torch
import numpy as np
from PIL import Image
from torchvision.models import vgg19
from torchvision.transforms import transforms
import torch.nn as nn
import matplotlib.pyplot as plt
from torch.nn.functional import mse_loss
from torch.autograd import Variabledevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 预处理:大小裁剪、转为张量、归一化
def preprocess(img_shape):transform = transforms.Compose([transforms.Resize(img_shape),transforms.ToTensor(),transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))])return transformclass VGGNet19(nn.Module):def __init__(self):super(VGGNet19, self).__init__()self.vggnet19 = vgg19(pretrained=False)self.vggnet19.load_state_dict(torch.load('./vgg19-dcbb9e9d.pth'))self.content_layers = [25]self.style_layers = [0, 5, 10, 19, 28]def forward(self, x):content_features = []style_features = []for name, module in self.vggnet19.features._modules.items():x = module(x)if int(name) in self.content_layers:content_features.append(x)if int(name) in self.style_layers:style_features.append(x)return content_features, style_featuresclass GenerateImage(nn.Module):def __init__(self, img_shape):super(GenerateImage, self).__init__()self.weight = torch.nn.Parameter(torch.rand(*img_shape))def forward(self):return self.weight# 初始化生成图像为内容图像
def generate_inits(content, device, lr):g_img = GenerateImage(content.shape).to(device)g_img.weight.data = content.dataoptimizer = torch.optim.Adam(g_img.parameters(), lr=lr, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)return g_img(), optimizer# 计算格拉姆矩阵
def gramMatrix(x):_, c, h, w = x.shapex = x.view(c, h*w)return torch.matmul(x, x.t()) / (c*h*w)# 计算总损失:内容损失+样式损失
def compute_loss(content_g, content_y, style_g, style_y, content_weight, style_weight, gamma):contentlosses = [mse_loss(g, y)*content_weight for g, y in zip(content_g, content_y)]stylelosses = [mse_loss(gramMatrix(g), gramMatrix(y))*style_weight for g, y in zip(style_g, style_y)]total_loss = sum(contentlosses) + gamma * sum(stylelosses)return contentlosses, stylelosses, total_loss# 用于可视化的后处理
def postprocess(img_tensor):rgb_mean = np.array([0.485, 0.456, 0.406])rgb_std = np.array([0.229, 0.224, 0.225])inv_normalize = transforms.Normalize(mean=-rgb_mean/rgb_std,std=1/rgb_std)to_PIL_image = transforms.ToPILImage()return to_PIL_image(inv_normalize(img_tensor[0].detach().cpu()).clamp(0, 1))def train(lr, epoch_num, c_path, s_path, img_shape):ipt = Image.open(c_path)syl = Image.open(s_path)transform = preprocess(img_shape)content, style = transform(ipt).unsqueeze(0), transform(syl).unsqueeze(0)net = VGGNet19()net.to(device).eval()content = content.type(torch.FloatTensor)style = style.type(torch.FloatTensor)if torch.cuda.is_available():content, style = Variable(content.cuda(), requires_grad=False), Variable(style.cuda(), requires_grad=False)else:content, style = Variable(content, requires_grad=False), Variable(style, requires_grad=False)icontent, istyle = net(content)scontent, sstyle = net(style)input, optimizer = generate_inits(content, device, lr)for epoch in range(epoch_num+1):gcontent, gstyle = net(input)contentlosses, stylelosses, total_loss = compute_loss(gcontent, icontent, gstyle, sstyle, 1, 1e3, 1e2)optimizer.zero_grad()total_loss.backward(retain_graph=True)optimizer.step()print("[epoch: %3d/%3d] content loss: %3f style loss: %3f total loss: %3f" % (epoch, epoch_num, sum(contentlosses).item(), sum(stylelosses).item(), total_loss))if epoch % 100 == 0 and epoch != 0:# plt.imshow(postprocess(input))# plt.axis('off')# plt.show()torch.save(net.state_dict(), "itr_%d_total_loss_%3f.pth" % (epoch, total_loss))if __name__ == "__main__":train(0.01, 10000, './content.jpg', './s.jpg', (500, 700)

在这里插入图片描述
在这里插入图片描述
内容图像、风格图像和生成图像(第10000次迭代的可视化)分别如上图所示,并且代码实现是Gatys et al.的工作。


http://chatgpt.dhexx.cn/article/gaP8LhSS.shtml

相关文章

(一)图像风格迁移

图像风格迁移即把图像A的风格和图像B的内容按照一定比例结合,输出具备图像A风格和图像B内容的图像C. [github传送门1]https://github.com/anishathalye/neural-style [github传送门2]https://github.com/Quanfita/Neural-Style/tree/master/examples 系列文章 (二)快速图像风格…

图像风格迁移与快速风格迁移的对比(感知损失)

最近一段时间要写数字图像处理的文献综述,《深度学习在图像风格迁移中的原理与应用综述》。只能感慨自己一时选题不审,导致期末火葬场啊…… 这个问题我纠结了一天,看了N多篇文献(全是英文的…),结果还是没…

图像风格迁移【老版】

深度学习目前为止最有用的东西是图像处理,我们可以用它在极早期判断癌症, 也可以用它在茫茫人海里寻找犯人,但是要我说你能写一个小程序取悦女朋友, 你就不一定能信, 这一招叫艺术风格变换,就是你点击一下&…

图像风格迁移-DSTN

样式传输的目的是从参考图像中再现具有样式的内容图像。现有的通用风格转换方法成功地以艺术或照片逼真的方式将任意风格传递给原始图像。然而,现有作品所定义的“任意风格”的范围由于其结构限制而在特定领域内受到限制。具体而言,根据预定义的目标域来…

学习笔记:图像风格迁移

所谓图像风格迁移,是指利用算法学习著名画作的风格,然后再把这种风格应用到另外一张图片上的技术。著名的国像处理应用Prisma是利用风格迁移技术,将普通用户的照片自动变换为具有艺术家的风格的图片。这篇文章会介绍这项技术背后的原理&#…

图像风格迁移实战

最近看了一些基于深度学习的Style Transfer, 也就是风格迁移相关的paper,感觉挺有意思的。 所谓风格迁移,其实就是提供一幅画(Reference style image),将任意一张照片转化成这个风格,并尽量保留原照的内容(Content)。之前比较火的…

Pytorch实现图像风格迁移(一)

图像风格迁移是图像纹理迁移研究的进一步拓展,可以理解为针对一张风格图像和一张内容图像,通过将风格图像的风格添加到内容图像上,从而对内容图像进行进一步创作,获得具有不同风格的目标图像。基于深度学习网络的图像风格迁移主要有三种类型,分别为固定风格固定内容的风格…

毕设 深度学习图像风格迁移 - opencv python

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这…

图像风格迁移

文章目录 前言一、传统的图像风格迁移(Traditional style transfer)1.1计算机图形学领域和计算机视觉领域(Computer Graphics&Computer Vision)1.2非真实感图形学(Non-photorealistic graphics)和纹理迁…

ARM SMMU的原理与IOMMU

首先放一个社区iommupatch的网址:https://lore.kernel.org/linux-iommu/ 1: arm smmu的原理 1.1: smmu 基本知识 如上图所示,smmu 的作用和mmu 类似,mmu作用是替cpu翻译页表将进程的虚拟地址转换成cpu可以识别的物理地址。同理,sm…

ARM SMMU学习笔记

1. 什么是SMMU? SMMU(system mmu),是I/O device与总线之间的地址转换桥。 它在系统的位置如下图: 它与mmu的功能类似,可以实现地址转换,内存属性转换,权限检查等功能。 2. 为什么需要SMMU? 了解…

SMMU架构手册之数据结构和转换流程(1)

SMMU使用内存中一组数据结构来放置转换数据。寄存器指向初始根结构STE的基地址。STE包含stage2转换表基地址指针,同时也指向stage1的配置结构,该配置结构包含转换表基指针。CD表示stage1转换,STE表示stage2转换。 因此SMMU使用两组明确的结构…

SMMU架构手册之数据结构和转换流程(2)

STE包含每个stream的配置: 是否使能来之设备的流量;是否是stage1转换;是否是stage2转换哪个数据结构定位到stage1的转换表 若使用stage1,STE使用域STE.S1ContextPtr来指示内存中的一个或多个CD的地址。 CD关联起streamID和stage1…

ARM_SMMU_上

本篇参考 arm 官网公开材料 和 小崔的linux 专栏 https://zhuanlan.zhihu.com/p/105005488 宋宝华老师MMU介绍 armv8-armv9 MMU深度学习 MMU简介 MMU是Memory Management Unit的缩写,中文名是内存管理单元。它是一种负责处理中央处理器(CPU)…

SMMU架构手册之数据结构和转换流程(3)

3. 配置和转换的查找 图中 描述了配置查找和转换查找所涉及的相关概念。 正如3.3.2中所描述的,传入的事务首先受配置查找的约束,而SMMU决定怎样开始进行事务的转换。这涉及到找到合适的STE,如果有必要也需要CD。 配置的查找不依赖于输入的地址…

简谈MMU与SMMU

在服务器的处理器firmware开发中,经常会遇到大家讨论MMU和SMMU的概念,而且会进一步讨论的相关的TLB和huge TLB概念。实不相瞒,一开始我是非常懵逼的,只是写code而已,最多是有用到MMIO来写device的寄存器,MM…

arm smmu 学习(1)

文章linux 4.14 代码分析smmu 流程 linux 在驱动找到对应的设备后会执行driver_probe_device 函数,具体dev和drv 匹配参考文章链接 driver_probe_device->really_probe->dma_configure->of_dma_configure int of_dma_configure(struct device *dev, str…

linux内核笔记之SMMU代码分析

2020/06/10: first version, 主要介绍smmu驱动的初始化流程 在前一篇博文ARM SMMU学习笔记中, 介绍了SMMU的一些基本概念以及SMMU地址转换的基本流程,本文主要分析linux kernel中SMMUv3的代码(drivers/iommu/arm-smmu-v3.c) linux kernel版本…

smmu实现分析

1.概述 1.1 SMMU产生背景 了解SMMU产生背景之前,首先要了解DMA工作原理。因为SMMU的产生主要是为了解决虚拟化平台下的DMA重映射问题。 DMA,外设和内存的连接件,用于解放CPU。外设可以通过DMA,将搜集的数据批量传输到内存&…

ARM SMMU介绍

SMMU:system Memory Management Unit系统存储管理单元 类似于CPU流水线中的MMU,SMMU负责翻译IO主设备对内存等从设备的读写请求地址,例如DMA,主设备读写请求也会发送到CPU物理地址总线上。IO设备上的DMA会对CPU物理地址总线上的内…