VGG19-图像风格迁移

article/2025/9/18 8:45:37

先导入包

import tensorflow as tf
import IPython.display as display
import matplotlib.pyplot as plt
import numpy as np
import PIL.Image
import time
import functools

迭代了50次(次数过少)的效果

迭代800次

定义一个加载图像的函数,并将其最大尺寸限制为 512 像素。

创建一个简单的函数来显示图像

def tensor_to_image(tensor):tensor = tensor*255tensor = np.array(tensor, dtype=np.uint8)if np.ndim(tensor)>3:assert tensor.shape[0] == 1tensor = tensor[0]return PIL.Image.fromarray(tensor)
def load_img(path_to_img):max_dim = 512img = tf.io.read_file(path_to_img)img = tf.image.decode_image(img, channels=3)img = tf.image.convert_image_dtype(img, tf.float32)shape = tf.cast(tf.shape(img)[:-1], tf.float32)long_dim = max(shape)scale = max_dim / long_dimnew_shape = tf.cast(shape * scale, tf.int32)img = tf.image.resize(img, new_shape)img = img[tf.newaxis, :]return img 
def imshow(image, title=None):if len(image.shape) > 3:image = tf.squeeze(image, axis=0)plt.imshow(image)if title:plt.title(title) content_image = load_img('1.jpg')
style_image = load_img('2.jpg')
plt.subplot(1, 2, 1)
imshow(content_image, 'Content Image')
plt.subplot(1, 2, 2)
imshow(style_image, 'Style Image') 

使用模型的中间层来获取图像的内容风格表示。

从网络的输入层开始,前几个层的激励响应表示边缘和纹理等低级 feature (特征)。

随着层数加深,最后几层代表更高级的 feature (特征)——实体的部分,如轮子眼睛

我们使用的是 VGG19 网络结构,这是一个已经预训练好的图像分类网络。

这些中间层是从图像中定义内容和风格的表示所必需的。

对于一个输入图像,我们尝试匹配这些中间层的相应风格和内容目标的表示。

x = tf.keras.applications.vgg19.preprocess_input(content_image*255)
x = tf.image.resize(x, (224, 224))
vgg = tf.keras.applications.VGG19(include_top=True, weights='imagenet')
prediction_probabilities = vgg(x)
prediction_probabilities.shapepredicted_top_5 = tf.keras.applications.vgg19.decode_predictions(prediction_probabilities.numpy())[0][(class_name, prob) for (number, class_name, prob) in predicted_top_5]# 现在,加载没有分类部分的 VGG19 ,并列出各层的名称:
vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')print()
for layer in vgg.layers:print(layer.name) ------------------------------
input_2
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

从网络中选择中间层的输出以表示图像的风格和内容:

# 内容层将提取出我们的 feature maps (特征图)
content_layers = ['block5_conv2'] # 我们感兴趣的风格层
style_layers = ['block1_conv1','block2_conv1','block3_conv1', 'block4_conv1', 'block5_conv1']num_content_layers = len(content_layers)
num_style_layers = len(style_layers) 

用于表示风格和内容的中间层

那么,为什么我们预训练的图像分类网络中的这些中间层的输出允许我们定义风格和内容的表示?

从高层理解,为了使网络能够实现图像分类(该网络已被训练过),它必须理解图像。

这需要将原始图像作为输入像素并构建内部表示,这个内部表示将原始图像像素转换为对图像中存在的 feature (特征)的复杂理解。

这也是卷积神经网络能够很好地推广的一个原因:它们能够捕获不变性并定义类别(例如猫与狗)之间的 feature (特征),这些 feature (特征)与背景噪声和其他干扰无关。

因此,将原始图像传递到模型输入和分类标签输出之间的某处的这一过程,可以视作复杂的 feature (特征)提取器。通过这些模型的中间层,我们就可以描述输入图像的内容和风格。

建立模型

使用tf.keras.applications中的网络可以让我们非常方便的利用 Keras 的功能接口提取中间层的值。

在使用功能接口定义模型时,我们需要指定输入和输出:

model = Model(inputs, outputs)

以下函数构建了一个 VGG19 模型,该模型返回一个中间层输出的列表:

def vgg_layers(layer_names):""" Creates a vgg model that returns a list of intermediate output values."""# 加载我们的模型。 加载已经在 imagenet 数据上预训练的 VGG vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')vgg.trainable = Falseoutputs = [vgg.get_layer(name).output for name in layer_names]model = tf.keras.Model([vgg.input], outputs)return model 

然后建立模型

style_extractor = vgg_layers(style_layers)
style_outputs = style_extractor(style_image*255)#查看每层输出的统计信息
for name, output in zip(style_layers, style_outputs):print(name)print("  shape: ", output.numpy().shape)print("  min: ", output.numpy().min())print("  max: ", output.numpy().max())print("  mean: ", output.numpy().mean())print()------------------------
block1_conv1shape:  (1, 336, 512, 64)min:  0.0max:  835.5256mean:  33.97525block2_conv1shape:  (1, 168, 256, 128)min:  0.0max:  4625.8857mean:  199.82687block3_conv1shape:  (1, 84, 128, 256)min:  0.0max:  8789.239mean:  230.78099block4_conv1shape:  (1, 42, 64, 512)min:  0.0max:  21566.135mean:  791.24005block5_conv1shape:  (1, 21, 32, 512)min:  0.0max:  3189.2542mean:  59.179478 

风格计算

图像的内容由中间 feature maps (特征图)的值表示。

事实证明,图像的风格可以通过不同 feature maps (特征图)上的平均值和相关性来描述。

通过在每个位置计算 feature (特征)向量的外积,并在所有位置对该外积进行平均,可以计算出包含此信息的 Gram 矩阵。

对于特定层的 Gram 矩阵,具体计算方法如下所示:

这可以使用tf.linalg.einsum函数来实现:

def gram_matrix(input_tensor):result = tf.linalg.einsum('bijc,bijd->bcd', input_tensor, input_tensor)input_shape = tf.shape(input_tensor)num_locations = tf.cast(input_shape[1]*input_shape[2], tf.float32)return result/(num_locations) 

 

提取风格和内容

构建一个返回风格和内容张量的模型。

 

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):"Expects float input in [0,1]"inputs = inputs*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:valuefor style_name, valuein zip(self.style_layers, style_outputs)}return {'content':content_dict, 'style':style_dict} 

在图像上调用此模型,可以返回 style_layers 的 gram 矩阵(风格)和 content_layers 的内容:

extractor = StyleContentModel(style_layers, content_layers)results = extractor(tf.constant(content_image))style_results = results['style']print('Styles:')
for name, output in sorted(results['style'].items()):print("  ", name)print("    shape: ", output.numpy().shape)print("    min: ", output.numpy().min())print("    max: ", output.numpy().max())print("    mean: ", output.numpy().mean())print()print("Contents:")
for name, output in sorted(results['content'].items()):print("  ", name)print("    shape: ", output.numpy().shape)print("    min: ", output.numpy().min())print("    max: ", output.numpy().max())print("    mean: ", output.numpy().mean()) ----------------------------------
Styles:block1_conv1shape:  (1, 64, 64)min:  0.0055228462max:  28014.562mean:  263.79025block2_conv1shape:  (1, 128, 128)min:  0.0max:  61479.49mean:  9100.949block3_conv1shape:  (1, 256, 256)min:  0.0max:  545623.44mean:  7660.976block4_conv1shape:  (1, 512, 512)min:  0.0max:  4320502.0mean:  134288.84block5_conv1shape:  (1, 512, 512)min:  0.0max:  110005.34mean:  1487.0381Contents:block5_conv2shape:  (1, 26, 32, 512)min:  0.0max:  2410.8796mean:  13.764149

梯度下降

使用此风格和内容提取器,我们现在可以实现风格传输算法。我们通过计算每个图像的输出和目标的均方误差来做到这一点,然后取这些损失值的加权和。

设置风格和内容的目标值:

style_targets = extractor(style_image)['style']
content_targets = extractor(content_image)['content'] 

定义一个 tf.Variable 来表示要优化的图像。 为了快速实现这一点,使用内容图像对其进行初始化( tf.Variable 必须与内容图像的形状相同)

image = tf.Variable(content_image) 

由于这是一个浮点图像,因此我们定义一个函数来保持像素值在 0 和 1 之间:

def clip_0_1(image):return tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0) 

创建一个 optimizer 。 本教程推荐 LBFGS,但 Adam 也可以正常工作:

opt = tf.optimizers.Adam(learning_rate=0.02, beta_1=0.99, epsilon=1e-1) 

为了优化它,我们使用两个损失的加权组合来获得总损失:

style_weight=1e-2
content_weight=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_layersloss = style_loss + content_lossreturn loss 

使用 tf.GradientTape 来更新图像。

@tf.function()
def train_step(image):with tf.GradientTape() as tape:outputs = extractor(image)loss = style_content_loss(outputs)grad = tape.gradient(loss, image)opt.apply_gradients([(grad, image)])image.assign(clip_0_1(image)) 

现在,我们运行几个步来测试一下:

train_step(image)
train_step(image)
train_step(image)
tensor_to_image(image) 

 结果如下:

运行正常,我们来执行一个更长的优化:

import time
start = time.time()epochs = 10
steps_per_epoch = 100step = 0
for n in range(epochs):for m in range(steps_per_epoch):step += 1train_step(image)print(".", end='')display.clear_output(wait=True)display.display(tensor_to_image(image))print("Train step: {}".format(step))end = time.time()
print("Total time: {:.1f}".format(end-start)) 


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

相关文章

图像风格迁移试玩

风格迁移 图像风格迁移原理内容损失函数风格损失函数 现成工具:tensorflow hub手工实现风格迁移我们对风格有失恭敬 神经风格转换是深度学习领域中一个很有趣的技术。它可以改变图像的风格。 如下图所示,根据一张内容图片和一张风格图片,生成…

迁移网络的应用-图像风格迁移

图片风格迁移指的是将一个图片的风格转换到另一个图片中,如图所示: 原图片经过一系列的特征变换,具有了新的纹理特征,这就叫做风格迁移。 VGG网络 在实现风格迁移之前,需要先简单了解一下VGG网络(由于VGG…

图像风格迁移算法学习总结

目录 一、简要说明 二、具体实施步骤 2.1综述 2.2基本思路 2.3核心思路 2.4基本问题处理 三、代码的简要描述 四、成果展示 一、简要说明 本次学习的图像风格迁移算法是基于一个2015年由Gatys等人发表的文章A Neural Algorithm of Artistic Style_的一个代码复…

【数字图像处理】图像风格迁移

代码和实验报告下载:http://download.csdn.net/detail/jsgaobiao/9523313 【作业要求】 设计自己的算法完成一个附图所示的图像风格迁移算法(基于matlab的快速实现)(很可能用到,并且鼓励使用)基于频率域…

图像风格迁移 CycleGAN原理

CycleGAN是一种很方便使用的用于进行图像风格转换的模型。它的一大优势就在于不需要成对的数据集就可以进行训练。比如我们只需要随便一大堆真人图像和随便另一大堆动漫图像,就可以训练出这两类风格互相转换的模型。 CycleGAN进行风格转换的原理是这样的&#xff1a…

Python实现基于深度学习的图像风格迁移

目录 一、选题意义与背景介绍 3 1.1背景介绍 3 1.2选题意义 3 二、相关方法介绍 4 2.1纹理建模 4 2.2图像重建 4 2.3图像风格迁移 4 2.3.1基于在线图像优化的慢速图像风格化迁移算法 4 2.3.2基于离线模型优化的快速图像风格化迁移算法 5 2.4图像风格迁移效果评估 6 三、具体方法…

图片风格迁移

##将图片进行风格迁移,将第一幅图片的均值平均差换成第二幅图的均值平方差。第三张是生成的图片 from numpy.lib.type_check import _imag_dispatcher from builtins import print from os import pread import sys from PIL import Image,ImageStat import numpy …

图像风格迁移及代码实现

图像风格迁移其实非常好理解,就是将一张图像的“风格”(风格图像)迁移至另外一张图像(内容图像),但是这所谓的另外一张图像只是在“风格”上与之前有所不同,图像的“内容”仍要与之前相同。Luan…

(一)图像风格迁移

图像风格迁移即把图像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使用两组明确的结构…