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

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

目录
一、选题意义与背景介绍 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
三、具体方法介绍 7
3.1基于卷积神经网络的图片风格迁移 7
3.1.1内容图像表示 8
3.1.2风格图像表示 9
3.1.3风格转移 11
3.2基于 AdaIN 层的实时任意风格迁移 11
3.2.1背景知识介绍 12
3.2.2AdaIN 介绍 13
3.2.3网络结构 13
3.2.4 训练 14
3.2.5 灵活性 15
四、实验结果 16
4.1基于卷积神经网络的图片风格迁移 16
4.1.1相同图片不同比值 16
4.1.2相同比值不同图片 17
4.2基于 AdaIN 层的实时任意风格迁移 18
4.2.1相同图片不同比值 18
4.2.2相同比值不同图片 19
五、总结分析 20
5.1 速度 20
5.2多样性 20
5.3转换效果 21
5.3.1EC 指标 21
5.3.2评估结果 23
六、参考文献 25
二、相关方法介绍

2.1纹理建模
纹理建模主要研究如何表示一种纹理,常用方法可以分为两大类:基于统计分布的 参数化纹理建模方法、基于 MRF 的非参数化纹理建模方法。
基于统计分布的参数化纹理建模方法主要将纹理建模为 N 阶统计量,基于 MRF 的非参数化纹理建模方法主要是用 patch 相似度匹配进行逐点合成。纹理建模解决了图片风格迁移中“如何对风格图中的风格特征进行建模和提取”的问题。
2.2图像重建
图像重建的输入是特征表达,输出是特征表达对应的图像;即把某个特征逆向重建 为原来的图像。通过重建预训练的分类网络中的高层特征,可以发现重建结果中能保留 高层语义信息,而摒弃了低层的颜色等信息。因此,我们将图像重建用于“与内容混合 然后还原成一个相应的风格化结果”。图像重建方法可以分为两类:基于在线图像优化的慢速图像重建方法、基于离线模型优化的快速图像重建方法。
基于在线图像优化的慢速图像重建方法是在图像像素空间做梯度下降来最小化目标函数,可以看作:由随机噪声作为起始图,本文转载自http://www.biyezuopin.vip/onews.asp?id=16766然后不断迭代改变图片的所有像素值来寻找一个目标结果图,这个目标结果图的特征表达和作为重建目标的目标特征表达相似。 但是由于每个重建结果都需要在像素空间进行迭代优化很多次,这个方法十分耗时。
基于离线模型优化的快速图像重建方法,这个方法就是因为第一个方法太慢了,为 了加速重建,大家希望能设计一个前向网络,提前用很多训练数据进行训练,训练过程 将一个特征表达作为输入,重建结果图像作为输出。这个训练好的网络只需要一次前向 就能输出一个结果图。

#! /usr/bin/env python3
# -*-coding=utf-8-*-import time
from scipy.misc import imsave
from scipy.optimize import fmin_l_bfgs_b
from keras import backend as K
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_array
from keras.models import Model
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import GlobalAveragePooling2D
from keras.layers import Input
from keras.applications.imagenet_utils import preprocess_input
from keras.engine.topology import get_source_inputs
from keras.applications.vgg19 import VGG19
import matplotlib.pyplot as plt#使用tensorflow环境编程
os=K.os
np=K.np#定义目标图像长宽将长宽同时缩小为原来图形的两倍,则矩阵缩小为原来的1/4
img_rows=400
img_columns=300
kl_array=[]
ssim_array=[]
#读入图片文件,以数组形式展开成三阶张量,后用numpy扩展为四阶张量
#最后使用对图片进行预处理:(1)去均值,(2)三基色RGB->BGR(3)调换维度 
def read_img(filename):img=load_img(filename,target_size=(img_columns,img_rows))img=img_to_array(img)img=np.expand_dims(img,axis=0)img=preprocess_input(img)return img#写入/存储图片在results的文件夹中,将输出数组转换为三维张量,量化高度层BGR,并将BGR->RGB
#经灰度大小截断在(0,255)
def write_img(x,name):x=x.reshape((img_columns,img_rows,3))x[:,:,0]+=103.939x[:,:,1]+=116.779x[:,:,2]+=123.68x=x[:,:,::-1]x=np.clip(x,0,255).astype('uint8')result_file=('./result--4/%s' %name)+'.png'if not os.path.exists('./result--4'):os.mkdir('./result--4')imsave(result_file,x)
# =============================================================================
#     start_time=time.time()
#     kl_array.append(calc_kl(base_model,style,result_file,7777))
#     ssim_array.append(calc_ssim(content,result_file))
#     end_time=time.time()
#     print("-Used %ds" %(end_time-start_time))
# =============================================================================print(result_file)#建立vgg19模型,本来卷基层+全连接层+输入层=19层,由于不是用于分类,没有用到全连接层,使用了no top模型
#no top模型权重大小为80.1M远远小于include top的权重574,7M
def vgg19_model(input_tensor):img_input=Input(tensor=input_tensor,shape=(300,400,3))#Blocks 1x=Conv2D(64,(3,3),activation='relu',padding='same',name='block1_conv1')(img_input)x=Conv2D(64,(3,3),activation='relu',padding='same',name='block1_conv2')(x)x=MaxPooling2D((2,2),strides=(2,2),name='block1_pooling')(x)#Block 2x=Conv2D(128,(3,3),activation='relu',padding='same',name='block2_conv1')(x)x=Conv2D(128,(3,3),activation='relu',padding='same',name='block2_conv2')(x)x=MaxPooling2D((2,2),strides=(2,2),name='block2_pooling')(x)#Block3x=Conv2D(256,(3,3),activation='relu',padding='same',name='block3_conv1')(x)x=Conv2D(256,(3,3),activation='relu',padding='same',name='block3_conv2')(x)x=Conv2D(256,(3,3),activation='relu',padding='same',name='block3_conv3')(x)x=Conv2D(256,(3,3),activation='relu',padding='same',name='block3_conv4')(x)x=MaxPooling2D((2,2),strides=(2,2),name='block3_pooling')(x)#Block 4x=Conv2D(512,(3,3),activation='relu',padding='same',name='block4_conv1')(x)x=Conv2D(512,(3,3),activation='relu',padding='same',name='block4_conv2')(x)x=Conv2D(512,(3,3),activation='relu',padding='same',name='block4_conv3')(x)x=Conv2D(512,(3,3),activation='relu',padding='same',name='block4_conv4')(x)x=MaxPooling2D((2,2),strides=(2,2),name='block4_pooling')(x)#Block 5x=Conv2D(512,(3,3),activation='relu',padding='same',name='block5_conv1')(x)x=Conv2D(512,(3,3),activation='relu',padding='same',name='block5_conv2')(x)x=Conv2D(512,(3,3),activation='relu',padding='same',name='block5_conv3')(x)x=Conv2D(512,(3,3),activation='relu',padding='same',name='block5_conv4')(x)x=MaxPooling2D((2,2),strides=(2,2),name='block5_pooling')(x)x=GlobalAveragePooling2D()(x)inputs=get_source_inputs(input_tensor)model=Model(inputs,x,name='vgg19')weights_path='vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5'model.load_weights(weights_path)return model#生成输入的张量,将内容,风格和迁移图像(中间量)一起输入到vgg模型中,返回三合一张量,和中间
#张量输入到VGG模型时要用到input tensor,中间计算要用到迁移图像的tensor,所以只输出这两个值
#待迁移图像初始化为一个待优化图片的占位符,初始输入为随机噪声图像,然后是一直优化的图像
def create_tensor(content_path,style_path):content_tensor=K.variable(read_img(content_path))style_tensor=K.variable(read_img(style_path))transfer_tensor=K.placeholder((1,img_columns,img_rows,3))input_tensor=K.concatenate([content_tensor,style_tensor,transfer_tensor],axis=0)return input_tensor,transfer_tensor#设置Gram_matrix矩阵的计算图,输入为某一层的representation,Gram 矩阵表示向量组的相关性,用于求解
#迁移图像关于风格图像的loss   
def gram_matrix(x):features=K.batch_flatten(K.permute_dimensions(x,(2,0,1)))gram=K.dot(features,K.transpose(features))return gram#计算风格的loss,以风格图像和迁移图像的representation为输入,分别计算gram矩阵,再求解两个Gram矩阵的
#二范数,除以归一化值
def style_loss(style_img_representation,transfer_img_representation):style=style_img_representationtransfer=transfer_img_representationA=gram_matrix(style)G=gram_matrix(transfer)channels=3size=img_rows*img_columnsloss=K.sum(K.square(A-G))/(4.*(channels**2)*(size**2))return loss#计算内容loss,输入为内容和迁移图片的presentation,输出为其reprensentation差的二范数
def content_loss(content_img_representation,transfer_img_representation):content=content_img_representationtransfer=transfer_img_representationloss=K.sum(K.square(transfer-content))return loss		 #变量loss,一段迷一样的表达式×-×,施加全局差正则表达式,全局差正则用于使生成的图片更加平滑自然
def total_variation_loss(x):a=K.square(x[:,:img_columns-1,:img_rows-1,:]-x[:,1:,:img_rows-1,:])b=K.square(x[:,:img_columns-1,:img_rows-1,:]-x[:,:img_columns-1,1:,:])loss=K.sum(K.pow(a+b,1.25))return loss#建立层名称到层输出张量映射的dict,便于取得各层输出feature map,分别求解style loss和content loss
#风格loss和内容loss按照10:1结合为全变量差约束
def total_loss(model,loss_weights,transfer_tensor):loss=K.variable(0.)layer_features_dict=dict([(layer.name,layer.output) for layer in model.layers])layer_features=layer_features_dict['block4_conv2']#layer_features=layer_features_dict['block2_conv2']content_img_features=layer_features[0,:,:,:]transfer_img_features=layer_features[2,:,:,:]loss+=loss_weights['content']*content_loss(content_img_features,transfer_img_features)feature_layers=['block1_conv1','block2_conv1','block3_conv1','block4_conv1','block5_conv1']for layer_name in feature_layers:layer_features=layer_features_dict[layer_name]style_img_features=layer_features[1,:,:,:]transfer_img_features=layer_features[2,:,:,:]#loss+=(loss_weights['style']/len(feature_layers))*(style_loss(style_img_features,transfer_img_features))loss+=loss_weights['style']*0.2*(style_loss(style_img_features,transfer_img_features))loss+=loss_weights['total']*total_variation_loss(transfer_tensor)return loss#通过K.gradient获取反向梯度,同时得到梯度和损失,
def create_outputs(total_loss,transfer_tensor):gradients=K.gradients(total_loss,transfer_tensor)outputs=[total_loss]if isinstance(gradients,(list,tuple)):outputs+=gradientselse:outputs.append(gradients)return outputs#计算输入图像的关于损失函数的梯度值和对应损失值
def eval_loss_and_grads(x):x=x.reshape((1,img_columns,img_rows,3))outs=outputs_func([x])loss_value=outs[0]if len(outs[1:])==1:grads_value=outs[1].flatten().astype('float64')else:grads_value=np.array(outs[1:]).flatten().astype('float64')return loss_value,grads_value#获取评价程序,将获取计算loss和gradients的函数
class Evaluator(object):def __init__(self):self.loss_value=Noneself.grads_value=Nonedef loss(self,x):loss_value,grads_value= eval_loss_and_grads(x)self.loss_value=loss_valueself.grads_value=grads_valuereturn self.loss_valuedef grads(self,x):grads_value=np.copy(self.grads_value)self.loss_value=Noneself.grads_value=Nonereturn grads_value#main函数
if __name__=='__main__':print('Welcom to Our Style-transfer!')#base_model = VGG19(include_top=False,weights='imagenet')#输入图片路径path={'content':'./image/content-img/content-2.jpg','style':'./image/style-img/style-5.jpg'}#path={'content':'./image/content-img/content-1.jpg','style':'./image/style-img/style-2.jpg'}input_tensor,transfer_tensor=create_tensor(path['content'],path['style'])#用来计算总loss的系数#loss_weights={'style':1.0,'content':0.0001,'total':1.0}#loss_weights={'style':1.0,'content':0.001,'total':1.0}#loss_weights={'style':1.0,'content':0.01,'total':1.0}#loss_weights={'style':1.0,'content':0.5,'total':1.0}loss_weights={'style':0.001,'content':0.0005,'total':1e-4}model=vgg19_model(input_tensor)#生成总的反向特征缺失totalloss=total_loss(model,loss_weights,transfer_tensor)#生成正向输出outputs=create_outputs(totalloss,transfer_tensor)#获取计算图(反向输入图)outputs_func=K.function([transfer_tensor],outputs)#生成处理器evaluator=Evaluator()#生成噪声x=np.random.uniform(0,225,(1,img_columns,img_rows,3))-128#x=img_to_array(load_img(path['content'],target_size=(img_columns,img_rows)))#x=img_to_array(load_img(path['style'],target_size=(img_columns,img_rows)))#迭代训练15次for ordering in range(150):print('Start:',ordering+1)start_time=time.time()x,min_val,info=fmin_l_bfgs_b(evaluator.loss,x.flatten(),fprime=evaluator.grads,maxfun=20)print('Current_Loss:',min_val)img=np.copy(x)name=path['content'][-5]+'and'+path['style'][-5]+'-'+str(ordering+1).zfill(2)#write_img(img,name,path['content'],path['style'],base_model)write_img(img,name)end_time=time.time()print("%s" %name,"-Used %ds" %(end_time-start_time))# =============================================================================
#     x=np.array(range(200))
#     y1=np.array(kl_array)
#     y2=np.array(ssim_array)
#     plt.plot(x,y1)
#     plt.plot(x,y2)
#     plt.show()
# =============================================================================

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


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

相关文章

图片风格迁移

##将图片进行风格迁移,将第一幅图片的均值平均差换成第二幅图的均值平方差。第三张是生成的图片 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使用两组明确的结构…

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版本…