风格转换简介

原创 2017年05月26日 00:03:21

风格转换,是把一张图片转化成同内容但包含某风格的新图片。本文将介绍如何让机器学习风格转换,包含两种方法:优化问题求解、转化网络求解。

这里写图片描述

风格转换

风格转换,就是根据现有的风格照片S,把当前照片C转化成带有S风格同时保留C内容的照片T

本文将叙述两种风格转换的思路:

  1. 将风格转换变成优化问题的求解,构建T,C之间的损失Lc以及T,S之间的损失Ls,同时增加图片平滑的损失Lv。通过求解minTiLi的优化问题求解。
  2. 不直接把目标图片T当做求解变量,而是构建一个transform network把内容图片C转化成目标图片T,以类似1中的方法构建损失函数,通过求解transform network的参数求解该问题。

优化问题

综述

首先,陈述问题:假设已知风格照片S、当前照片C,求目标照片T,要求带有S的风格并且保留C的内容。

下面,确定几个损失函数:

  • LsTS风格上的距离
  • LcTC内容上的距离
  • LvT不平滑的度量

最后,便是求解优化问题:

minTαsLs(T,S)+αcLc(T,C)+αvLv(T)

损失函数

优化问题中Ls,Lc是通过预先训练的VGG网络得到。

首先,简单介绍下VGG网络:它是一种固定的网络结构,其结构如下所示,一般采用D或E结构,通常叫VGG-16和VGG-19:

这里写图片描述

那么,为什么Ls,Lc是通过预先训练的VGG网络得到呢?

训练后的VGG网络,每一层都对特征进行了抽象,越深得到的特征越具象。所以每一层的特征也就代表了图片不同粒度的抽象,可以根据特征的距离判断图片内容的相似程度。VGG的卷积层得到了feature map,假设其大小是CHW

假设在层lTfeature mapTlCfeature mapCl,那么Lc的计算如下:

Lc(Tl,Cl)=||TlCl||

假设在层lSfeature mapSlCfeature mapCl,那么Ls的计算如下:

Ls(Tl,Sl)=||G(Tl)G(Sl)||

其中,G代表gram matrixG(Sl)的含义是先将Sl变成C(HW)的二维矩阵,然后计算Sl对自己的协方差SlSlTG(x)其实表示了xfeature map上不同feature的相互作用关系,用其来度量风格。

至于Lv,可以理解成:

Lv(T)=||TT1||+||TT1||

训练

构建好损失函数L后,求解如下优化问题即可:

minTαsLs(T,S)+αcLc(T,C)+αvLv(T)

这里优化问题的求解方法采用L-BFGS(一种伪牛顿法),这样做的目的是得到比gradient descent更快的收敛速度。

例子

本人是詹姆斯的铁杆球迷,对詹姆斯的照片采用不同风格转换后的效果图如下所示。需要说明的是:第二列第一张是未加平滑损失Lv的效果图,可以看到存在很多噪点,第二列第二张是加入平滑损失Lv的效果图,照片清晰了很多。

这里写图片描述

代码

以下代码参考了Siraj Raval on YouTube

# Load library
from __future__ import print_function

import time
from PIL import Image
import numpy as np

from keras import backend
from keras.models import Model
from keras.applications.vgg16 import VGG16

from scipy.optimize import fmin_l_bfgs_b
from scipy.misc import imsave

# Load and preprocess the content and style images
height = 512
width = 512

content_image_path = 'images/hugo.jpg'
content_image = Image.open(content_image_path)
content_image = content_image.resize((height, width))
content_image

style_image_path = 'images/styles/wave.jpg'
style_image = Image.open(style_image_path)
style_image = style_image.resize((height, width))
style_image

content_array = np.asarray(content_image, dtype='float32')
content_array = np.expand_dims(content_array, axis=0)
print(content_array.shape)

style_array = np.asarray(style_image, dtype='float32')
style_array = np.expand_dims(style_array, axis=0)
print(style_array.shape)

content_array[:, :, :, 0] -= 103.939
content_array[:, :, :, 1] -= 116.779
content_array[:, :, :, 2] -= 123.68
content_array = content_array[:, :, :, ::-1]

style_array[:, :, :, 0] -= 103.939
style_array[:, :, :, 1] -= 116.779
style_array[:, :, :, 2] -= 123.68
style_array = style_array[:, :, :, ::-1]

content_image = backend.variable(content_array)
style_image = backend.variable(style_array)
combination_image = backend.placeholder((1, height, width, 3))

input_tensor = backend.concatenate([content_image,
                                    style_image,
                                    combination_image], axis=0)

# Reuse a model pre-trained for image classification to define loss functions
model = VGG16(input_tensor=input_tensor, weights='imagenet',
              include_top=False)
layers = dict([(layer.name, layer.output) for layer in model.layers])

content_weight = 0.025
style_weight = 5.0
total_variation_weight = 1.0

# Loss
loss = backend.variable(0.)
# The content loss
def content_loss(content, combination):
    return backend.sum(backend.square(combination - content))

layer_features = layers['block2_conv2']
content_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]

loss += content_weight * content_loss(content_image_features,
                                      combination_features)
# The style loss
def gram_matrix(x):
    features = backend.batch_flatten(backend.permute_dimensions(x, (2, 0, 1)))
    gram = backend.dot(features, backend.transpose(features))
    return gram
def style_loss(style, combination):
    S = gram_matrix(style)
    C = gram_matrix(combination)
    channels = 3
    size = height * width
    return backend.sum(backend.square(S - C)) / (4. * (channels ** 2) * (size ** 2))

feature_layers = ['block1_conv2', 'block2_conv2',
                  'block3_conv3', 'block4_conv3',
                  'block5_conv3']
for layer_name in feature_layers:
    layer_features = layers[layer_name]
    style_features = layer_features[1, :, :, :]
    combination_features = layer_features[2, :, :, :]
    sl = style_loss(style_features, combination_features)
    loss += (style_weight / len(feature_layers)) * sl
# The total variation loss
def total_variation_loss(x):
    a = backend.square(x[:, :height-1, :width-1, :] - x[:, 1:, :width-1, :])
    b = backend.square(x[:, :height-1, :width-1, :] - x[:, :height-1, 1:, :])
    return backend.sum(backend.pow(a + b, 1.25))

loss += total_variation_weight * total_variation_loss(combination_image)

# Define needed gradients and solve the optimisation problem
grads = backend.gradients(loss, combination_image)
outputs = [loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)

def eval_loss_and_grads(x):
    x = x.reshape((1, height, width, 3))
    outs = f_outputs([x])
    loss_value = outs[0]
    grad_values = outs[1].flatten().astype('float64')
    return loss_value, grad_values

class Evaluator(object):

    def __init__(self):
        self.loss_value = None
        self.grads_values = None

    def loss(self, x):
        assert self.loss_value is None
        loss_value, grad_values = eval_loss_and_grads(x)
        self.loss_value = loss_value
        self.grad_values = grad_values
        return self.loss_value

    def grads(self, x):
        assert self.loss_value is not None
        grad_values = np.copy(self.grad_values)
        self.loss_value = None
        self.grad_values = None
        return grad_values

evaluator = Evaluator()

# Train
x = np.random.uniform(0, 255, (1, height, width, 3)) - 128.

iterations = 10

for i in range(iterations):
    print('Start of iteration', i)
    start_time = time.time()
    x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
                                     fprime=evaluator.grads, maxfun=20)
    print('Current loss value:', min_val)
    end_time = time.time()
    print('Iteration %d completed in %ds' % (i, end_time - start_time))

# Evaluation
x = x.reshape((height, width, 3))
x = x[:, :, ::-1]
x[:, :, 0] += 103.939
x[:, :, 1] += 116.779
x[:, :, 2] += 123.68
x = np.clip(x, 0, 255).astype('uint8')

Image.fromarray(x)

网络转换

结构

将风格转换当成优化问题求解存在如下问题:

  • 每来一张新图片,都需要重新求解优化问题。如果需要将大量图片转换成同一风格的话效率会很低

考虑能否构建一个transformer,将图片C转化成目标图片T。训练的时候只需要学习transformer的参数。训练完成得到transformer后,当新的图片来到时,直接输入transformer即可得到新的图片,大大提高了效率。

本节中的风格转换即采用上述构建transformer的方法,利用预训练的VGG得到特征进而得到损失函数,通过调节transformer的参数最小化损失函数。图示如下:

这里写图片描述

训练

损失函数的定义与优化问题部分相同,这里求解的优化问题是:

ŷ minw=fW(x)αsLs(ŷ ,ys)+αcLc(ŷ ,yc)+αvLv(ŷ )

参考

  1. A Neural Algorithm of Artistic Style
  2. Perceptual Losses for Real-Time Style Transfer
    and Super-Resolution

第1周:机器学习简介-人工智能工程师直通车

-
  • 1970年01月01日 08:00

基于深度学习的图像风格转换

距离上次写博客已经好久好久好久了,真是懈怠的生活节奏,整天混吃等死玩游戏,前些日子做毕业设计时总算又学了点新东西。学了一点深度学习和卷积神经网络的知识,附带着详细学习了一下前段时间我觉得比较有意思的图...
  • u013805360
  • u013805360
  • 2017-06-21 13:35:56
  • 3910

图像处理(九)人物肖像风格转换-Siggraph 2014

对于人物头像风格转换,2014年siggraph上面出了一篇比较不错的paper:《Style Transfer for Headshot Portraits》 ,这篇文献涉及到的算法非常多,可以说,...
  • hjimce
  • hjimce
  • 2015-05-06 12:25:26
  • 5583

图片风格转换(附TensorFlow代码)

源论文:A NeuralAlgorithm of Artistic Style. 2015 NIPS 思路:采用ImageNet数据集预训练一个VGG19网络出来,得到网络结构如下图:  其中紫色框...
  • hwj_wayne
  • hwj_wayne
  • 2017-10-18 22:39:48
  • 1205

基于tensorflow的图片风格转换

这个东东算是学python和深度学习的一个demo,核心的算法和实现不是本人写的,参照fast-neural-style-tensorflow,只是用flask东拼西凑写了一个基于web方式的调用。 ...
  • alpa
  • alpa
  • 2017-03-05 15:29:07
  • 1703

图像风格转换(Image style transfer)

图像风格转换是最近新兴起的一种基于深度学习的技术,它的出现一方面是占了卷积神经网络的天时,卷积神经网络所带来的对图像特征的高层特征的抽取使得风格和内容的分离成为了可能。另一方面则可能是作者的灵感,内容...
  • xinzhangyanxiang
  • xinzhangyanxiang
  • 2016-12-20 23:39:41
  • 22614

使用CNN做图像风格转化+代码实现

论文来源:Image Style Transfer Using Convolutional Neural Networks  图像风格转化,即将我们的原始图片,转换成我们想要的特定风格的图片。这是一...
  • k87974
  • k87974
  • 2018-01-22 17:27:52
  • 246

深度学习之风格转换——Style Transfer

最近在学习CS20课程——一门讲述tensorflow应用的实践性课程,正好Assignment2讲到了Style Transfer这个东西,这里把我的理解总结一下(代码基于Tensorflow...
  • g11d111
  • g11d111
  • 2018-03-19 20:33:16
  • 93

基于tensorflow实现图像风格的变换

Leon A. Gatys, Alexander S. Ecker, 和 Matthias Bethge 等人的论文“A Neural Algorithm of Artistic Style”开...
  • sparkexpert
  • sparkexpert
  • 2017-04-15 20:53:56
  • 1913

tensorflow学习笔记(七):TensorFLow实战之style_transfer(风格转换)

从这部分开始利用TensorFlow进行实际应用,将会慢慢的把最新的东西一边学习一边整理一边实现,计划是初期的代码都利用tensorflow的基本API完成,后期建立大的网络结构的时候引入Keras等...
  • woaidapaopao
  • woaidapaopao
  • 2017-06-18 20:04:49
  • 1826
收藏助手
不良信息举报
您举报文章:风格转换简介
举报原因:
原因补充:

(最多只允许输入30个字)