风格迁移算法

最近推导了一些机器学习入门的算法,老是搞那些数学知识,搞的自己都没信心和新区了。今天学着玩点有趣好玩的。

图像的艺术风格迁移算法,算是一个简单有趣,而且一般人都能看得到效果的算法。图像艺术风格迁移,简单的理解,就是找一个照片作为内容,然后把这个照片换成梵高或者毕加索等制定的风格。关于图像艺术风格迁移的一些历史和知识,大家可以看看这篇文章:图像风格迁移(Neural Style)简史

思路

风格迁移的大概思路是:我们需要准备两张图片。一张是我们将要输出的内容图片,另外一张是我们需要模仿的风格图片。我们需要输出一张图片,让输出的这张图片的内容和内容图片相近,让输出图片的风格和风格图片的风格相近。

内容最接近的算法

内容最接近,相对来说比较简单。简单的理解可以对比每个图片的像素,然后计算他们的差距。也可以是计算CNN中间某个卷积层得到的特征值之间的距离。

我经过调试发现,如果内容图层取得太靠前,效果不太好。因为内容特征越靠前,相当于对比的越细腻,而风格迁移要得到的效果是宏观上更像内容图片,细节上用风格表现,这样效果最好。

风格最接近的算法

风格的比较是最难理解的。要理解风格比较的算法,需要了解一个名词叫做格拉姆矩阵。听常博士说这个知识属于矩阵分析里面的内容。我对这方面没系统学习过,不太懂。但是我能理解到的层次是:你给定N个卷积核,然后可以得到N个矩阵。把这N个矩阵拉直了形成N个向量,N个向量两两內积形成的矩阵,就是格莱姆矩阵。而生成图片和风格图片的格莱姆矩阵的距离差,就是风格差。

实现办法

那么最终实现的办法,就是我们生成一张图片,然后得到一个损失函数\(loss=contentloss+styleloss\),然后我们用梯度下降让损失函数最小就可以了。

具体的实现

在看我的代码之前,我参考了tensorflow的官网的算法,这个算法的实现用的是tf最新的API,好处是简单,坏处是封装的太死了,太简单了,很多底层的东西看不到。我想用比较笨的办法。可以顺便学习下VGG19。但是总体思路差不多。

VGG19的实现

首先我下载一个vgg19的模型,并且简单实现了vgg实际加载模型和计算卷积的过程,我把其中全连接层给删除了。因为卷积是共享参数的,所以输入的图表不一定要和VGG19图像一样,但是全连接层会一样。

import tensorflow as tf
import scipy.io
import numpy as np
import cv2

DEFAULT_PATH ='E:\\project\\ChangeStyle\\model\\imagenet-vgg-verydeep-19.mat'
VGG19_LAYERS=('conv1_1','relu1_1','conv1_2','relu1_2','pool1',
              'conv2_1','relu2_1','conv2_2','relu2_2','pool2',
              'conv3_1','relu3_1','conv3_2','relu3_2','conv3_3','relu3_3','conv3_4','relu3_4','pool3',
              'conv4_1','relu4_1','conv4_2','relu4_2','conv4_3','relu4_3','conv4_4','relu4_4','pool4',
              'conv5_1','relu5_1','conv5_2','relu5_2','conv5_3','relu5_3','conv5_4','relu5_4','pool5')
              #,'fc6','relu6','fc7','relu7','fc8','softmax'

VGG19_index_Map = {'conv1_1':0,'conv1_2':2,'conv2_1':5,'conv2_2':7,'conv3_1':10,'conv3_2':12,'conv3_3':14,
                   'conv3_4':16,'conv4_1':19,'conv4_2':21,'conv4_3':23,
            'conv4_4':25,'conv5_1':28,'conv5_2':30,'conv5_3':32,'conv5_4':34,'fc6':37,'fc7':39,'fc8':41}

class VGG19:

    def __init__(self, model_path = None):
        layers = []
        if model_path == None:
            layers = scipy.io.loadmat(DEFAULT_PATH)
        else:
            layers = scipy.io.loadmat(model_path)
        assert layers != None
        self.vgg_layers = layers['layers'][0]

    def _compute_(self, layer_name, input):
        output = []

        w = []
        b = []
        if VGG19_index_Map.__contains__(layer_name):
            i = VGG19_index_Map[layer_name]
            w = self.vgg_layers[i][0][0][0][0][0]
            b = self.vgg_layers[i][0][0][0][0][1]

        type = layer_name[:3]
        if type == 'con':
            output = tf.nn.conv2d(input,w,strides=[1,1,1,1],padding='SAME')
            output = tf.add(output, b)
        elif type == 'rel':
            output = tf.nn.relu(input)
        elif type == 'poo':
            output = tf.nn.max_pool(input,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
        elif type == 'fc6':
            b = np.reshape(b,-1)
            input = tf.reshape(input,[input.shape[0],-1])
            w = tf.reshape(w,(-1,w.shape[-1]))
            output = tf.nn.bias_add(tf.matmul(input,w),b)
        elif type == 'fc7':
            w = np.reshape(w,(-1,w.shape[-1]))
            output = tf.add(tf.matmul(input,w),b)
        elif type == 'fc8':
            w = np.reshape(w,(-1,w.shape[-1]))
            output = tf.add(tf.matmul(input,w),b)
        else:
            output = tf.nn.softmax(input)
        return output

    def build_model(self, image):

        net={}
        net['input'] = image

        input = image
        for layer in VGG19_LAYERS:
            input = self._compute_(layer,input)
            net[layer] = input

        return net

上述代码基本上比较简单,我个人感觉不需要怎么解释。

我们有了vgg19的代码架构以后,我们需要的是可以实现图像的可以通过vgg19以后得到的卷积后的数值,同时可以计算他的数值

IMAGE_SIZE = 512
feature_layers_w = [0.1,0.1,0.4,0.3,0.1]
STYLE_LAYERS =['conv1_1','conv2_1','conv3_1','conv4_1','conv5_1']
CONTENT_LAYERS =['conv5_2']
import tensorflow as tf
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

def get_content_loss(p, x):

    loss = tf.reduce_mean(tf.pow(p - x,2))
    return loss

def gram_matrix(input_tensor):
    channels = int(input_tensor.shape[-1])
    a = tf.reshape(input_tensor, [-1, channels])
    n = tf.shape(a)[0]
    gram = tf.matmul(a, a, transpose_a=True) / tf.to_float(n)
    return gram

def get_style_loss(base_style, gram_target, index):
    gram_style = gram_matrix(base_style)
    gram_target = gram_matrix(gram_target)

    return feature_layers_w[index] * tf.reduce_mean(tf.pow(gram_style - gram_target,2))

def get_compute_loss(genarate,content, style):

    c_loss = 0
    s_loss = 0

    for i,s_name in enumerate(STYLE_LAYERS):
        g_data = genarate[s_name]
        s_data = style[s_name]

        g_data = tf.reshape(g_data,(-1,g_data.shape[3]))
        s_data = tf.reshape(s_data,(-1,s_data.shape[3]))

        s_loss = s_loss + get_style_loss(s_data,g_data,i)

    for c_name in CONTENT_LAYERS:
        g_data = genarate[c_name]
        c_data = content[c_name]

        g_data = tf.reshape(g_data,(-1,g_data.shape[3]))
        c_data = tf.reshape(c_data,(-1,c_data.shape[3]))

        c_loss = c_loss + get_content_loss(c_data, g_data)

    return 1e-2 * s_loss/ tf.to_float(tf.size(STYLE_LAYERS)) + 1e3 *c_loss
import tensorflow as tf
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

def deprocess_img(processed_img):
    x = processed_img.copy()
    if len(x.shape) == 4:
        x = np.squeeze(x, 0)
    assert len(x.shape) == 3, ("Input to deprocess image must be an image of "
                             "dimension [1, height, width, channel] or [height, width, channel]")
    if len(x.shape) != 3:
        raise ValueError("Invalid input to deprocessing image")

    #perform the inverse of the preprocessiing step
    x[:, :, 0] += 103.939
    x[:, :, 1] += 116.779
    x[:, :, 2] += 123.68
    x = x[:, :, ::-1]
    x = np.clip(x, 0, 255).astype('uint8')
    return x

def load_img(img_path):
    img = Image.open(img_path)
    img = img.resize((IMAGE_SIZE,IMAGE_SIZE))
    img = img.tobytes()
    img = tf.decode_raw(img,tf.uint8)
    img = tf.cast(img,tf.float32)
    img = tf.reshape(img,(1,IMAGE_SIZE,IMAGE_SIZE,3))
    img = tf.keras.applications.vgg19.preprocess_input(img)
    return img

content_img = load_img('E:\\project\\ChangeStyle\\img\\nst\\Tuebingen_Neckarfront.jpg')
style_img = load_img('E:\\project\\ChangeStyle\\img\\nst\\1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg')
generate_img = tf.Variable(content_img, dtype=tf.float32)

with tf.Session() as sess:
    vgg = VGG19()

    g_model = vgg.build_model(generate_img)
    c_model = sess.run(vgg.build_model(content_img))
    s_model = sess.run(vgg.build_model(style_img))

    loss = get_compute_loss(g_model,c_model,s_model)
    optimizer = tf.train.AdamOptimizer(learning_rate=5, beta1=0.99, epsilon=1e-1)
    op = optimizer.minimize(loss,global_step= tf.train.get_global_step())
    sess.run(tf.global_variables_initializer())
    for i in range(1000):
        _,l = sess.run((op,loss))

    img = sess.run(generate_img)

    img = deprocess_img(img)
    plt.imshow(img)
    plt.show()

这个代码有几个问题:

  1. 生成任何一张图片,需要消耗太多的时间。
  2. 我测试通过上述参数,绘制油画效果就不错,但是绘制素描效果就比较差,当然可以通过调整参数获得相对较好的效果。

原文地址:https://www.cnblogs.com/bbird/p/11571352.html

时间: 2024-11-09 01:51:06

风格迁移算法的相关文章

Keras实现风格迁移

风格迁移 风格迁移算法经历多次定义和更新,现在应用在许多智能手机APP上. 风格迁移在保留目标图片内容的基础上,将图片风格引用在目标图片上. 风格本质上是指在各种空间尺度上图像中的纹理,颜色和视觉图案;内容是图像的高级宏观结构. 实现风格迁移背后的关键概念与所有深度学习算法的核心相同:定义了一个损失函数来指定想要实现的目标,并最大限度地减少这种损失. 知道自己想要实现的目标:在采用参考图像的样式的同时保留原始图像的内容.如果我们能够在数学上定义内容和样式,那么最小化的适当损失函数将是以下内容:

风格迁移学习三

论文: Instance Normalization: The Missing Ingredient for Fast Stylization 提出背景: 在论文<Image style transfer using convolutional neural networks>中提出了风格迁移算法,将一个图像的风格转移到另一个图像中,这个被风格化的图像同时匹配风格图和内容图,风格统计和内容统计都是从预训练好的用于图像分类的深度神经网络中提取出来的.风格统计是从浅层提取的,并且在空间位置上是均匀

Distill详述「可微图像参数化」:神经网络可视化和风格迁移利器!

近日,期刊平台 Distill 发布了谷歌研究人员的一篇文章,介绍一个适用于神经网络可视化和风格迁移的强大工具:可微图像参数化.这篇文章从多个方面介绍了该工具. 图像分类神经网络拥有卓越的图像生成能力.DeepDream [1].风格迁移 [2] 和特征可视化 [3] 等技术利用这种能力作为探索神经网络内部原理的强大工具,并基于神经网络把艺术创作推进了一小步. 所有这些技术基本上以相同的方式工作.计算机视觉领域使用的神经网络拥有图像的丰富内部表征.我们可以使用该表征描述我们希望图像具备的特性(如

图像风格迁移原理

所谓图像风格迁移,是指利用算法学习著名画作的风格,然后再把这种风格应用到另外一张图片上的技术.著名的图像处理应用Prisma是利用风格迁移技术,普通用户的照片自动变换为具有艺术家风格的图片. 一.图像风格迁移的原理 1.原始图像风格迁移的原理 在学习原始的图像风格迁移之前,可以在先看看ImageNet图像识别模型VGGNet(微调(Fine-tune)原理).事实上,可以这样理解VGGNet的结构:前面的卷积层是从图像中提取“特征”,而后面的全连接层把图片的“特征”转换为类别概率.其中,VGGN

风格迁移(2)-Fast Style Transfer

X为输入图片 fw 风格迁移的网络 yc就是X ys是风格后的图片 y帽为输入图片X经过fw 风格迁移的网络生成的图片 y帽在内容上与yc相类似,在风格上与ys相类似. Fast Style Transfer的训练步骤如下: 1 输入一张图片x到fw中得到结果y帽 2 将y帽与yc输入到loss network(VGG-16)中,计算它的relu3_3的输出,并计算它们的均方误差作为content loss 3 将y帽与ys输入到loss network(VGG-16)中,计算它的relu1_2

实战| 一行命令对你的图像视频进行风格迁移

1.项目介绍 今天我们要做的是一个快速图像风格迁移的程序. 那么,什么是图像风格迁移?图像风格迁移就是把一种图像风格转变为另一种图像风格.例如,原图为:加上不同风格的图像可以得到如下不同的结果: 2.使用训练好的模型来生成图像 2.1环境 PythonTensorflow 2.2模型下载 训练好的模型有7个,表示7种类型的风格,模型文件的百度云:模型的百度云地址密码:35pg 2.3使用训练好的模型 在项目根目录下执行: python eval.py --model_file <your pat

机器学习:利用卷积神经网络实现图像风格迁移 (一)

相信很多人都对之前大名鼎鼎的 Prisma 早有耳闻,Prisma 能够将一张普通的图像转换成各种艺术风格的图像,今天,我们将要介绍一下Prisma 这款软件背后的算法原理.就是发表于 2016 CVPR 一篇文章, " Image Style Transfer Using Convolutional Neural Networks" 算法的流程图主要如下: 总得来说,就是利用一个训练好的卷积神经网络 VGG-19,这个网络在ImageNet 上已经训练过了. 给定一张风格图像 a 和

人像美妆---妆容迁移算法研究(Makeup transfer)

对于人像美妆算法,现在的美妆相机.玩美彩妆之类的app已经做的比较成熟了,但是具体算法,基本网络上是杳无可查,今天本人介绍一种自动的人像美妆算法----(Makeup Transfer)妆容迁移 妆容迁移相关的论文不多,有如下几篇: 1.Example-Based cosmetic transfer 2.Makeup Transfer using Multi-example 3.A new digtial face makeup method 4.An automatic framework f

颜色迁移之五——自适应迁移算法

颜色空间为一个三维的线性空间,通常使用红色.绿色和蓝色(RGB)作为颜色空间的基,但这三原色不能直观地度量色调.饱和度和亮度(HSV),为了体现颜色空间中的不同特性,人们总结了很多颜色空间.由Smith等提出的LMS颜色空间的三个分量分别表示长.中.短激发光谱.而人的视网膜中锥状细胞的光感器对光的波长最敏感.在这个意义上,我们把计算机里的RGB图像表示转换成基于人眼更为敏感波长的LMS表示.实际上不同颜色空间是同构的,因此它们之间必然存在变换矩阵,事实上在Reinhard等给出了RGB和LMS之