首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实时视频上的神经风格迁移(具有完整的可实现代码)

实时视频上的神经风格迁移(具有完整的可实现代码)

作者头像
代码医生工作室
发布2019-06-24 09:47:58
3.7K0
发布2019-06-24 09:47:58
举报
文章被收录于专栏:相约机器人相约机器人

作者 | Sourish Dey

来源 | Medium

编辑 | 代码医生团队

近几年来,经历了计算机视觉在生活中几乎每个角落的应用 - 得益于大量数据和超级动力GPU的可用性,这些GPU已经对卷积神经网络进行了训练和部署(CNN)超级容易。今天在机器学习中最有趣的讨论之一是它如何在未来几十年影响和塑造文化和艺术生产。神经风格迁移是卷积神经网络最具创造性的应用之一。

通过拍摄内容图像和风格图像,神经网络可以重新组合内容和风格图像,以有效地创建艺术(重构)图像。虽然像Prisma这样的应用程序可以为从手机拍摄的照片生成艺术风格,本文的目的是了解这个看似困难的概念背后的科学和艺术。这里共享实时可实现的代码。

背景 - 神经风格迁移

神经风格迁移概念首先由Gatys,Ecker和Bethge(艺术风格的神经算法)引入开创性论文 在2015年)展示了一种将一个图像的艺术风格与另一个图像的内容相结合的方法。基本思想是采用由预训练的深度卷积神经网络(例如VGG 16(通常训练用于图像分类或物体检测))学习的特征表示来获得图像的样式和内容的单独表示。一旦找到这些表示,就会尝试优化生成的图像,以重新组合不同目标图像的内容和样式。因此该概念使纹理,对比度和颜色随机化,同时保留内容图像的形状和语义特征(中心方面)。虽然它有点类似于颜色转换。

问题陈述 - 这不是优化问题吗?

因此这里的问题陈述给出了内容照片X和样式照片Y如何将Y的样式转移到内容X以生成新的照片Z。如何训练CNN来处理和优化差异(X之间的差异)和Y)达到最佳全局(Z)?

优化问题概述

Gatys在原始论文(2015年的艺术风格的神经算法 )中表示,“将一个图像转换为另一个内容图像的样式(纹理)作为优化问题,可以通过训练深度神经网络来解决”。这个难题的组成部分:

  • 内容丢失:它表示样式传输网络的输出图像(样式图像)的内容与输入图像的内容或“内容目标”的相似程度,如果输入图像(X)和样式图像,它往往为零( Z)在内容方面彼此相似,如果它们不同则会增长。为了正确捕获图像的内容,我们需要保留内容图像的空间(形状/边界等)/高级特征。由于像VGG16这样的图像分类卷积神经网络被迫在更深层学习高级特征/抽象表示或图像的“内容”,因此对于内容比较,我们在更深层(L)-1使用激活/特征映射或者在输出(softmax)层之前的2层。可以根据需要选择L(从中提取内容特征的层),选择的层越深,输出图像的外观就越抽象。所以L是网络的超参数。
  • Gram矩阵和样式丢失:虽然稍微复杂一点,原始样式图像(Y)和网络输出图像(Z)之间的样式损失也被计算为从VGG-16的层输出中提取的特征(激活图)之间的距离。这里的主要区别在于,不是直接从VGG-16的层激活矩阵比较特征表示,而是将那些特征表示转换成空间相关矩阵(在激活图内),这通过计算Gram矩阵来完成。克矩阵包含样式图像的各层的每对特征图之间的相关性。因此基本上Gram矩阵捕获了在图像的不同部分共同出现的特征的趋势。它代表了一组向量的内部点积,这捕获了两个向量之间的相似性。
  • 在原始论文中,Gatys建议采用浅层和深层转换层的组合来计算样式表示的样式损失。因此,样式损失是每个转换层激活矩阵的原始样式图像(Y)和生成图像(Z)之间的样式特征的均方差(欧几里德距离)的总和。
  • 总损失:总损失是内容损失和风格损失的加权总和,如下所示。
  • 训练网络以同时最小化内容丢失和样式丢失。α和β是内容丢失和样式丢失的权重,并且再次是整个CNN的超参数。这些值的选择仅取决于生成的图像(Z)中需要保留多少内容或样式。这里从随机(白噪声)图像矩阵开始,并在每次迭代中计算内容图像(内容丢失)和样式图像(样式丢失)之间的特征图距离(总损失)以计算总损失。因此这种损失通过网络反向传播,需要通过适当的优化技术(相当于梯度下降)在迭代(几千)上最小化总损失,以更新随机图像矩阵,尽可能接近内容和样式图像。
  • 因此根据上述典型的NST网络,在较深层计算内容丢失以捕获高级特征(空间),而对于样式图像捕获详细样式特征(纹理,颜色等),通过提取网络输出来计算样式损失(激活图)在每个转换块的浅层(conv-1)。典型的预训练分类CNN如VGG16由几个转换块组成,其具有2或3个卷积(Conv2D)层(conv1,conv2等),然后是汇集(最大/平均)层。所以样式图像网络是多输出模型。在下一节中,将简要讨论该概念在实时视频数据上的实现。详细代码以及所有输入(内容视频和样式图像)和输出(生成的图像帧)可在此处找到。

https://github.com/nitsourish/Neural-Style-Transfer-on-video-data

实时视频的神经风格迁移:

将解释图像的步骤,因为视频只是一组图像的集合。这些图像被称为帧,可以组合起来获得原始视频。因此可以遍历所有单独帧的步骤,重新组合并生成风格化视频。

第1步:加载预先训练的VGG-16 CNN模型

为NST应用从头开始构建(训练)CNN需要大量的时间和强大的计算基础设施,而这些基础设施并不是个人可用的。

因此将加载预先训练的CNN -VGG-16的权重(从着名的' ImageNet。'挑战图像训练)来实现神经样式迁移。将使用Keras应用程序加载具有预训练重量的VGG-16。对于NST,VGG-16可能不是最佳(期望的复杂性)CNN架构。对于此应用程序,有更复杂(更深入的高级架构)网络,如InceptionV4,VGG-19,Resnet-101等,这将花费更多时间来加载和运行。然而,作为实验选择了VGG-16(具有高分类精度和对特征的良好内在理解)。

from keras.applications.vgg16 import VGG16
shape = (224,224)
vgg = VGG16(input_shape=shape,weights='imagenet',include_top=False)

这里的形状很重要,因为VGG-16网络采用224 x 224 x 3形状的输入图像。

vgg.summary()
_________________________________________________________________
Layer (type) Output Shape Param # 
=================================================================
input_21 (InputLayer) (None, 224, 224, 3) 0 
_________________________________________________________________
block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 
_________________________________________________________________
block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 
_________________________________________________________________
average_pooling2d_101 (Avera (None, 112, 112, 64) 0 
_________________________________________________________________
block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 
_________________________________________________________________
block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 
_________________________________________________________________
average_pooling2d_102 (Avera (None, 56, 56, 128) 0 
_________________________________________________________________
block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 
_________________________________________________________________
block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 
_________________________________________________________________
block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 
_________________________________________________________________
average_pooling2d_103 (Avera (None, 28, 28, 256) 0 
_________________________________________________________________
block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 
_________________________________________________________________
block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 
_________________________________________________________________
block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 
_________________________________________________________________
average_pooling2d_104 (Avera (None, 14, 14, 512) 0 
_________________________________________________________________
block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 
_________________________________________________________________
block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 
_________________________________________________________________
block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 
_________________________________________________________________
average_pooling2d_105 (Avera (None, 7, 7, 512) 0 
=================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0

VGG-16架构

第2步:定义内容模型和成本函数

对于高级内容功能,希望考虑整个图像中的功能。因此将使用平均池替换max-pool(可能会丢弃一些信息)。然后将从总共13个卷中选择任何更深层作为“输出”并将模型定义到该层。然后将在n / w中提供我们的预处理内容图像(X),以在输出层计算(预测)特征/激活图,该模型和模型输出与定义形状的任何随机(白噪声)矩阵相对应(224 x 224 x 3)。计算内容图像网络的MSE损失和梯度。这将有助于将输入图像(随机图像)更新为梯度的相反方向,并允许内容丢失值减小,以便生成的图像将与输入的图像匹配图片。详细的实施代码保存在GitHub存储库。

https://github.com/nitsourish/Neural-Style-Transfer-on-video-data

content_model = vgg_cutoff(shape, 13) #Can be experimented with other deep layers
# make the target
target = K.variable(content_model.predict(x))
# try to match the input image
# define loss in keras
loss = K.mean(K.square(target - content_model.output))
# gradients which are needed by the optimizer
grads = K.gradients(loss, content_model.input)

第3步:定义样式模型和样式损失函数

两个图像的特征图在给定层产生相同的Gram矩阵,希望两个图像具有相同的样式(但不一定是相同的内容)。因此网络中早期层中的激活图将捕获一些更精细的纹理(低级特征),而激活贴图更深的层将捕获更高级别的图像样式元素。为了获得最佳结果,将结合浅层和深层作为输出来比较图像的样式表示和相应地定义了多输出模型。

首先,计算每层的Gram矩阵,并计算样式网络的总样式损失。对不同的层采用不同的权重来计算加权损失。然后基于样式损失(样式分量的差异)和渐变,更新输入图像(随机图像)并减少样式损失值,使得生成的图像(Z)纹理看起来类似于样式图像(Y)。

#Define multi-output model
symb_conv_outputs = [layer.get_output_at(1) for layer in vgg.layers\
if layer.name.endswith('conv1')]
multi_output_model = Model(vgg.input, symb_conv_outputs)
#Style feature map(outputs) of style image
symb_layer_out = [K.variable(y) for y in multi_output_model.predect(x)]
#Defining Style loss
def gram_matrix(img):
 X = K.batch_flatten(K.permute_dimensions(img,(2,0,1)))
 gram_mat = K.dot(X,K.transpose(X))/img.get_shape().num_elements()
 return gram_mat
def style_loss(y,t):
 return K.mean(K.square(gram_matrix(y)-gram_matrix(t)))
#Style loss calculation through out the network
#Defining layer weights for layers 
weights = [0.2,0.4,0.3,0.5,0.2]
loss=0
for symb,actual,w in zip(symb_conv_outputs,symb_layer_out,weights):
 loss += w * style_loss(symb[0],actual[0])
grad = K.gradients(loss,multi_output_model.input)
get_loss_grad = K.Function(inputs=[multi_output_model.input], outputs=[loss] + grad)

第4步:定义总成本(总损失):

现在可以将内容和样式损失结合起来以获得网络的整体损失。需要使用合适的优化算法在迭代中最小化该数量。

#Content Loss
loss=K.mean(K.square(content_model.output-content_target)) * Wc #Wc is content loss weight(hyperparameter)
#Defining layer weights of layers for style loss 
weights = [0.2,0.4,0.3,0.5,0.2]
#Total loss and gradient
for symb,actual,w in zip(symb_conv_outputs,symb_layer_out,weights):
 loss += Ws * w * style_loss(symb[0],actual[0]) #Wc is content loss weight(hyperparameter)
 
grad = K.gradients(loss,vgg.input)
get_loss_grad = K.Function(inputs=[vgg.input], outputs=[loss] + grad)

第5步:解决优化问题和损失最小化函数

在定义整个符号计算图之后,优化算法是主要组件,其将能够迭代地最小化整体网络成本。这里不使用keras标准优化器函数(例如optimizers.Adam,optimizers.sgd等),这可能需要更多时间,将使用有限内存BFGS(Broyden-Fletcher-Goldfarb-Shanno),这是一个近似的数值使用有限数量的计算机内存的优化算法。由于其产生的线性存储器要求,该方法非常适用于涉及大量无变量(参数)的优化问题。与普通BFGS一样,它是一种标准的准牛顿方法,通过最大化正则化对数似然来优化平滑函数。

Scipy的最小化函数(fmin_l_bfgs_b)允许传回函数值f(x)及其渐变f'(x),在前面的步骤中计算过。但是需要将输入展开为1-D数组格式的最小化函数,并且丢失和渐变都必须是np.float64。

#Wrapper Function to feed loss and gradient with proper format to L-BFGS 
def get_loss_grad_wrapper(x_vec):
 l,g = get_loss_grad([x_vec.reshape(*batch_shape)])
 return l.astype(np.float64), g.flatten().astype(np.float64)
#Function to minimize loss and iteratively generate the image
def min_loss(fn,epochs,batch_shape):
 t0 = datetime.now()
 losses = []
 x = np.random.randn(np.prod(batch_shape))
 for i in range(epochs):
 x, l, _ = scipy.optimize.fmin_l_bfgs_b(func=fn,x0=x,maxfun=20)
# bounds=[[-127, 127]]*len(x.flatten())
#x = np.clip(x, -127, 127)
# print("min:", x.min(), "max:", x.max())
 print("iter=%s, loss=%s" % (i, l))
 losses.append(l)
 print("duration:", datetime.now() - t0)
 plt.plot(losses)
 plt.show()
 newimg = x.reshape(*batch_shape)
 final_img = unpreprocess(newimg)
 return final_img[0]

第6步:在输入内容和样式图像上运行优化器功能:

在输入内容框架和样式图像上运行优化器,并根据定义的符号计算图形,网络完成其最小化总体损失的预期工作,并生成看起来与内容和样式图像一样接近的图像。

输出图像仍然很嘈杂,因为只运行网络30次迭代。理想的NST网络应针对数千次迭代进行优化,以达到最小损耗阈值,从而生成清晰的混合输出。

第7步:对所有图像帧重复上述步骤:

在从短视频中提取帧之后对每个帧执行网络推断,为每个帧生成样式化图像并重新组合/缝合样式化图像帧。

#Vedio Reading and extracting frames
cap = cv2.VideoCapture(path)
while(True):
 ret, frame = cap.read()
 frame = cv2.resize(frame,(224,224))
 X = preprocess_img(frame)
#Running the above optimization as per defined comutation graph and generate styled image frame# 
 final_img = min_loss(fn=get_loss_grad_wrapper,epochs=30,batch_shape=batch_shape) 
 plt.imshow(scale(final_img))
 plt.show()
 cv2.imwrite(filename, final_img)
#Recombine styled image frames to form the video
video = cv2.VideoWriter(video_name, 0, 1, (width,height))
for image in images:
 video.write(cv2.imread(os.path.join(image_folder, image)))
cv2.destroyAllWindows()
video.release()

也可以使用设备相机尝试使用视频,并尝试在线模式(实时视频)中的样式传输,只需调整VideoCapture模式即可。

cap = cv2.VideoCapture(0)
cap.release()

商业应用

除了个人和艺术使用这种看似奇特的技术外,神经风格迁移还有可能改变人类创造力传统上占主导地位的任何行业,例如美术,时尚,建筑或新潮流的汽车纹理设计等。例如,时尚产业需要深刻理解时尚机制:趋势的起因和传播,循环重复的原则和演变模式,以发展明天的时尚。

然而,神经网络或NST可以通过为不同类型的服装自动分配形状,元素和创意纹理(样式)来帮助设计新设计,并进一步将它们结合起来,为明天创造时尚的时尚。通过自动化,NST的重要部分有可能大大减少服装设计过程。

进一步改进和实验:

以下是一些提高生成图像质量的策略:

1)更多迭代:更明显的是,运行网络进行更多迭代(大约1000次)将减少整体损失并创建更清晰的混合图像。

2)先进的CNN架构:对于NST应用,通常具有非常先进的连接的更深入的神经网络可以更准确地捕获高水平(空间)和详细的纹理特征。所以值得尝试其他优秀的预训练网络,如InceptionV4,GoogLeNet,Resnet-101等。然而,这些网络的运行时间非常高,NST应用程序需要数千次迭代,并且需要昂贵的计算基础设施,如强大的GPU堆栈。

3)调整内容和样式损失权重:作为一个实验,分别尝试使用4和0.03作为内容和样式损失权重,主要是尽可能专注于捕获内容(因为我只运行几次迭代网络)。但是,这可能不合适,找到最佳权重的理想方法是通过网格搜索。

4)调整样式损失的图层权重:为了最大化样式特征捕获,需要在相应的转换层中调整权重以控制样式损失计算,以优化纹理的提取(早期层的更精细纹理和更深层的更高级别特征)。这些都是超参数,网格搜索是理想的选择。

此外,可以使用图层(L)来提取内容特征。L也是网络的超参数。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像识别
腾讯云图像识别基于深度学习等人工智能技术,提供车辆,物体及场景等检测和识别服务, 已上线产品子功能包含车辆识别,商品识别,宠物识别,文件封识别等,更多功能接口敬请期待。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档