基于tensorflow 的图像风格迁移原理与实现
⼀、什么是风格迁移?
苹果APP 有⼀个图⽚编辑器的软件叫Prisma ,可以把我们拍的平时的照⽚转换成像漫画⼀样的样式,这个APP 中给我们提供了各式各样的艺术图⽚, 我们可以任意选择⾃⼰的拍的图⽚,选择喜欢的漫画图、或者说是不同风格的图,进⾏转化。这样⾃⼰拍的图⽚就风格化了 ,就像是⼀幅画⼀样了。 下⾯就先⽤Prisma 对风格迁移做⼀个简单的描述:如下图,我选择了不同的风格,⽣成的图⽚效果也就不⼀样。现在我们应该就都知道了,风格迁移 简单来说,就是把⼀幅图像的风格“迁移”到现有图像上,现有图像的内容还是原来的内容 ,就想我的图⽚上,⽔杯还是⽔杯根本没变化,只是整个 图⽚的“画风”或者说风格发⽣了变化,可以说成了⼀个漫画杯⼦了。
![
⼆、风格迁移是如何实现的呢?
风格迁移是先由Gatys在2016年CVPR论⽂中提出来的,论⽂的题⽬是“ Image Style Transfer Using Convolutional Neural Networks”,咱们可以顺着⼤佬的论⽂拜读⼀下,⾃然就明⽩风格迁移是怎么实现的了
⾸先作者先把两张图⽚都是输⼊到了VGG-19 ⽹络⾥⾯,然后把每个卷积层出来的结果都列出来了,然后作者得出了⼀个结论:神经⽹络的浅层多提取出来的是图像的像素信息,⽽深层更多提取的是物体的位置信息、布局等。
接下来我们看⼀下Gatys在风格迁移中做了哪些⼯作:
⾸先先把⼀张内容图P输⼊到VGG19 ⽹络,想要提取这幅图像的P的内容信息。因为之前已经得出了结论“神经⽹络的浅层多提取出来的是图像的像素信息,⽽深层更多提取的是物体的位置信息、布局等”,也就是说在提取content特征时,不同层的表达效果是不⼀样的,⽂章在提取内容图像的特征时采⽤⾼层特征,如下图的右侧可以看出,取的是第四层,⾼层会很⼤程度上保留原图的内容特征。然后把⼀个⽩噪声图⽚x也输⼊到了VGG-19⽹络⾥⾯,就是下图那个灰⿊⾊的⼩图⽚,然后同样也在第四个卷积层取出,p和x在第四层卷积层所有的feature map求⼀个均⽅差,作为内容的损失值。为什么要这样做呢?这是两幅图⽚相当于内容上的“差”,我们是想通过后期迭代优化,使其X⽩噪声图像中的内容与p越来越⼀样。这是内容损失值的计算公式Lcontent
再来看看怎么样能得到⼀幅图像的风格呢?
提到图像的风格,我们就得说⼀下什么是Gram矩阵?
Gram矩阵
格拉姆矩阵可以看做feature之间的偏⼼协⽅差矩阵(即没有减去均值的协⽅差矩阵),在feature map
中,每个数字都来⾃于⼀个特定滤波器在特定位置的卷积,因此每个数字代表⼀个特征的强度,⽽Gram计算的实际上是两两特征之间的相关性,哪两个特征是同时出现的,哪两个是此消彼长的等等,同时,Gram的对⾓线元素,还体现了每个特征在图像中出现的量,因此,Gram有助于把握整个图像的⼤体风格。有了表⽰风格的Gram Matrix,要度量两个图像风格的差异,只需⽐较他们Gram Matrix的差异即可。
⼀般来说浅层⽹络提取的是局部的细节纹理特征,深层⽹络提取的是更抽象的轮廓、⼤⼩等信息。这些特征总的结合起来表现出来的感觉就是图像的风格,由这些特征向量计算出来的的Gram矩阵,就可以把图像特征之间隐藏的联系提取出来,也就是各个特征之间的相关性⾼低。为了获得所有这些通道的相互关系,我们需要计算⼀些称为 gram矩阵的东西,我们将使⽤ gram 矩阵来测量通道之间的相关程度,这些通道随后将作为风格本⾝的度量。如果两个图像的特征向量的Gram矩阵的差异较⼩,就可以认定这两个图像风格是相近的。格拉姆矩阵⽤于度量各个维度⾃⼰的特性以及各个维度之间的关系。内积之后得到的多尺度矩阵中,对⾓线元素提供了不同特征图各⾃的信息,其余元素提供了不同特征图之间的相关信息。这样⼀个矩阵,既能体现出有哪些特征,⼜能体现出不同特征间的紧密程度。
Gatys把风格图像a也输⼊到了VGG-19⽹络⾥⾯,把每⼀层得出的feature map 求出它们的Gram矩阵,⼀共是5层卷积,求得5个Gram 矩阵,X也做同样的操作,然后把它们两个Gram矩阵求均⽅差,
得到的值当做风格损失。x的每⼀层的Gram矩阵都会和a风格图像的每⼀层的Gran矩阵⼀起计算均⽅差EL,然后由这个EL根据权重w计算得到 _style,权重w⽤来表达各层特征的重要性,这个损失就是⽤来描述风格的差异。最后,迭代更新x,其实就是对总的loss求导,然后乘以步长,得到的就是更新的⼤⼩。因此x就不断在⽹络中循环更新,直到达到好的效果。
三、tensorflow 代码实现
from  PIL import  Image
import  numpy as  np
import  scipy .io
import  tensorflow as  tf
from  functools import  reduce
import  scipy .misc
#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#*******************LOAD PICTURE*********************
def  loadpic ():
imgcon = Image .open ("111.jpg")
imgsty = Image .open ("time1.jpg")
datacon = imgcon .getdata ()
datacon = data()
datasty = data()
datacon = np.reshape(datacon,(300,300,3))
eval是做什么的
datasty = np.reshape(datasty,(300,300,3))
datacon = np.array(datacon)
datasty = np.array(datasty)
'''
datacon = scipy.misc.imread("111.jpg").astype(np.float)
datasty = scipy.misc.imread("time1.jpg").astype(np.float)
'''
return datacon, datasty
#********************CONSTRUCT VGG***********************
def vggnet(input_image):
predata = scipy.io.loadmat('imagenet-vgg-verydeep-19.mat')
weights = predata['layers'][0]
def conv_layer(bottom, num):
kernels, bias = weights[num][0][0][0][0]
kernels = np.transpose(kernels,(1,0,2,3))
bias = shape(-1)
cvnt = v2d(bottom, tf.constant(kernels), strides =(1,1,1,1), padding ='SAME')  ret = tf.nn.bias_add(cvnt, bias)
return ret
def pool_layer(bottom):
ret = tf.nn.avg_pool(bottom, ksize =(1,2,2,1), strides =(1,2,2,1), padding ='SAME') return ret
def relu_layer(bottom):
ret = lu(bottom)
return ret
vgg ={}
vgg['conv1_1']= conv_layer(input_image,0)
vgg['relu1_1']= relu_layer(vgg['conv1_1'])
vgg['conv1_2']= conv_layer(vgg['relu1_1'],2)
vgg['relu1_2']= relu_layer(vgg['conv1_2'])
vgg['pool1']= pool_layer(vgg['relu1_2'])
vgg['conv2_1']= conv_layer(vgg['pool1'],5)
vgg['relu2_1']= relu_layer(vgg['conv2_1'])
vgg['conv2_2']= conv_layer(vgg['relu2_1'],7)
vgg['relu2_2']= relu_layer(vgg['conv2_2'])
vgg['pool2']= pool_layer(vgg['relu2_2'])
vgg['conv3_1']= conv_layer(vgg['pool2'],10)
vgg['relu3_1']= relu_layer(vgg['conv3_1'])
vgg['conv3_2']= conv_layer(vgg['relu3_1'],12)
vgg['relu3_2']= relu_layer(vgg['conv3_2'])
vgg['conv3_3']= conv_layer(vgg['relu3_2'],14)
vgg['relu3_3']= relu_layer(vgg['conv3_3'])
vgg['conv3_4']= conv_layer(vgg['relu3_3'],16)
vgg['relu3_4']= relu_layer(vgg['conv3_4'])
vgg['pool3']= pool_layer(vgg['relu3_4'])
vgg['pool3']= pool_layer(vgg['relu3_4'])
vgg['conv4_1']= conv_layer(vgg['pool3'],19)
vgg['relu4_1']= relu_layer(vgg['conv4_1'])
vgg['conv4_2']= conv_layer(vgg['relu4_1'],21)
vgg['relu4_2']= relu_layer(vgg['conv4_2'])
vgg['conv4_3']= conv_layer(vgg['relu4_2'],23)
vgg['relu4_3']= relu_layer(vgg['conv4_3'])
vgg['conv4_4']= conv_layer(vgg['relu4_3'],25)
vgg['relu4_4']= relu_layer(vgg['conv4_4'])
vgg['pool4']= pool_layer(vgg['relu4_4'])
vgg['conv5_1']= conv_layer(vgg['pool4'],28)
vgg['relu5_1']= relu_layer(vgg['conv5_1'])
vgg['conv5_2']= conv_layer(vgg['relu5_1'],30)
vgg['relu5_2']= relu_layer(vgg['conv5_2'])
vgg['conv5_3']= conv_layer(vgg['relu5_2'],32)
vgg['relu5_3']= relu_layer(vgg['conv5_3'])
vgg['conv5_4']= conv_layer(vgg['relu5_3'],34)
vgg['relu5_4']= relu_layer(vgg['conv5_4'])
return vgg
#***********************STYLIZE IMAGE************************
def main():
infodata = scipy.io.loadmat('imagenet-vgg-verydeep-19.mat')#导⼊VGG-19 mat⽂件
mean = infodata['normalization'][0][0][0]
mean = np.mean(mean, axis=(0,1))
datacon, datasty = loadpic()#把内容图和风格图都读进来
shape =(1,)+ datacon.shape
infocon ={}
infosty ={}
compute_content ='relu4_2'#内容图去的是卷积层的第四层
compute_style =('relu1_1','relu2_1','relu3_1','relu4_1','relu5_1')#风格图去的是所有卷积层
weightcon =5
weightsty =0.2
iterations =1000
g = tf.Graph()
with g.as_default(), tf.Session()as sess:
tempimage = tf.placeholder('float', shape = shape)
tempvgg = vggnet(tempimage)#内容图喂给VGG-19 ⽹络
qqq = np.array([datacon - mean])
infocon[compute_content]= tempvgg[compute_content].eval(feed_dict ={tempimage: qqq})
g = tf.Graph()
with g.as_default(), tf.Session()as sess:
tempimage = tf.placeholder('float', shape = shape)
tempvgg = vggnet(tempimage)#风格图像喂给VGG-19 ⽹络
ppp = np.array([datasty - mean])
for layer in compute_style:
temp_style = tempvgg[layer].eval(feed_dict ={tempimage: ppp})
temp_style = np.reshape(temp_style,(-1, temp_style.shape[3]))
infosty[layer]= np.matmul(temp_style.T, temp_style)/ temp_style.size#实现求每层的Gram矩阵
g = tf.Graph()
with g.as_default(), tf.Session()as sess:
#initial = al(size = shape, scale = 0.2)
ini = tf.random_normal(shape)*0.1#随机⽣成⽩噪声图像
via = tf.Variable(ini)
center_vgg = vggnet(via)#⽩噪声图像输⼊到VGG-19⽹络⾥⾯去

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。