自己写个 Prisma

原创 2017年04月26日 11:05:20

Sirajology视频链接

前一段时间特别火的 Prisma 大家都玩了么,看了这篇文章后,你也可以自己写一个 Prisma 迷你版了。

这个 idea 最开始起源于 Google Research Blog
Here’s the initial Google DeepDream blog post:

他们用大量的图片数据来训练深度神经网络,使这个网络可以判断出图片中的事物,然后投入一个新的图片,让图片识别,不仅仅是识别,还要把图片修正为网络学到的东西。

然后另一个团队发表了一篇相似的论文

他们用名画来训练模型,然后投入一个生活中的图片,通过强化一些 feature,将这个图片修正为更像名画风格的图片。

原理就是用一个 Convolutional Neural Network 学习一张图片的 style ,然后把另一张图片转换成这种 style。

用到的工具是 python 和 keras 包,文章后面有作者的源码的地址。

引入需要的包

from scipy.misc import imread, imresize, imsave
from scipy.optimize import fmin_l_bfgs_b
from sklearn.preprocessing import normalize
import numpy as np
import time
import os
import argparse
import h5py

from keras.models import Sequential
from keras.layers.convolutional import Convolution2D, ZeroPadding2D, AveragePooling2D
from keras import backend as K

定义三个图片变量

#Define base image, style image, and result image paths
args = parser.parse_args()
base_image_path = args.base_image_path
style_reference_image_path = args.style_reference_image_path
result_prefix = args.result_prefix

引用事先计算好的 weights vgg16

这是提前训练好的,可以识别生活中的图片,以它作为模型的起点。

#Get the weights file
weights_path = r"vgg16_weights.h5"

定义 booleans 决定是否 reshape 图片

#Init bools to decide whether or not to resize
rescale_image = strToBool(args.rescale_image)
maintain_aspect_ratio = strToBool(args.maintain_aspect_ratio)

然后初始化 style-content weights

什么是style-content weights?

在神经网络学习的过程中,不同的层学到的东西是不一样的,例如识别一个小狗,一层学到的是 edge,下一层学到的是 shape,再下一层是更复杂的 shape,最后学到的是整个的 dog。

在学习艺术风格的网络中发现,低层次学到的是 style,如纹理 颜色框架等,高层次学到的是 content,如太阳等具体的物体,CNN会把 content 和 style 分离开,所以要达到不同的效果,需要不同的权重分配。

# Init variables for style and content weights. 
total_variation_weight = args.tv_weight
style_weight = args.style_weight * args.style_scale
content_weight = args.content_weight

然后设定图片维度,定义tensor代表三个图片 base image,style image,output image。

# Init dimensions of the generated picture.
img_width = img_height = args.img_size
assert img_height == img_width, 'Due to the use of the Gram matrix, width and height must match.'
img_WIDTH = img_HEIGHT = 0
aspect_ratio = 0

# get tensor representations of our images
base_image = K.variable(preprocess_image(base_image_path, True))
style_reference_image = K.variable(preprocess_image(style_reference_image_path))

# this will contain our generated image
combination_image = K.placeholder((1, 3, img_width, img_height))

再组合到一个 tensor 中

# combine the 3 images into a single Keras tensor
input_tensor = K.concatenate([base_image,
                              style_reference_image,
                              combination_image], axis=0)

放在一个 tensor 中,因为更容易被 神经网络 解析,这样一个高维的图片也可以有可以计算的复杂度。

建立 31 层的神经网络

# build the VGG16 network with our 3 images as input
first_layer = ZeroPadding2D((1, 1))
first_layer.set_input(input_tensor, shape=(3, 3, img_width, img_height))

model = Sequential()
model.add(first_layer)
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(64, 3, 3, activation='relu'))
model.add(AveragePooling2D((2, 2), strides=(2, 2)))

。。。

一共有3种:
convolution2D layer:拥有可学习的filters,这些filters有receptive field,用来将神经元连接到下一层的一个局部的区域,而不是连接到每一个神经元

ZeroPadding layer:用来控制 output 的大小

Pooling layer:只用图片的子集来计算,减少参数数量,用来避免 overfitting。

激活函数用的是 ReLU,比sigmoid更快一些。

各个层的参数分别是:

定义完模型后,引入 vgg16 的权重

# load the weights of the VGG16 networks
load_weights(weights_path, model)

定义 Loss Function:计算预测和实际的差别

# get the symbolic outputs of each "key" layer (we gave them unique names).
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers])

# get the loss (we combine style, content, and total variation loss into a single scalar)
loss = get_total_loss(outputs_dict)

得到 gradients

# get the gradients of the generated image wrt the loss
grads = K.gradients(loss, combination_image)

最后用 back propagation 训练模型,此处用到的算法是 limit-memory BFGS,可以最小化 loss function 而且空间效率较高。

#combine loss and gradient
f_outputs = combine_loss_and_gradient(loss, grads)

# Run scipy-based optimization (L-BFGS) over the pixels of the generated image to minimize the neural style loss
# 5 Step process
x, num_iter = prepare_image()
for i in range(num_iter):

    #Step 1 - Record iterations
    print('Start of iteration', (i+1))
    start_time = time.time()

    #Step 2 - Perform l_bfgs optimization function using loss and gradient
    x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
                                     fprime=evaluator.grads, maxfun=20)
    print('Current loss value:', min_val)

    #Step 3 - Get the generated image
    img = deprocess_image(x.reshape((3, img_width, img_height)))

    #Step 4 - Maintain aspect ratio
    if (maintain_aspect_ratio) & (not rescale_image):
        img_ht = int(img_width * aspect_ratio)
        print("Rescaling Image to (%d, %d)" % (img_width, img_ht))
        img = imresize(img, (img_width, img_ht), interp=args.rescale_method)
    if rescale_image:
        print("Rescaling Image to (%d, %d)" % (img_WIDTH, img_HEIGHT))
        img = imresize(img, (img_WIDTH, img_HEIGHT), interp=args.rescale_method)


最后,rescale 并且保存图片

    #Step 5 - Save the generated image
    fname = result_prefix + '_at_iteration_%d.png' % (i+1)
    imsave(fname, img)
    end_time = time.time()
    print('Image saved as', fname)
    print('Iteration %d completed in %ds' % (i+1, end_time - start_time))

这个算法也可以用到视频中。

另外还找到一篇《我是如何用TensorFlow 做出属于自己的Prisma的?》

感兴趣就动手写一下吧。

The code for this video is here:
Here’s the initial Google DeepDream blog post:
A Deepdream web app:
The Neural Style Paper:


推荐阅读
历史技术博文链接汇总
也许可以找到你想要的

版权声明:本文为博主原创文章,未经博主允许不得转载。

自己写个 Prisma

Sirajology的视频链接 前一段时间特别火的 Prisma 大家都玩了么,看了这篇文章后,你也可以自己写一个 Prisma 迷你版了。 这个 idea 最开始起源于 Go...
  • u014365862
  • u014365862
  • 2017年02月06日 09:03
  • 836

prisma风格总结篇

最近很火的一款软件,prisma,可以实现将自己的照片和世界名画进行融合,生成名画风格的照片,闹腾的是,火火火火火,可以说,一半人在玩小精灵,另一半人则是玩prisma。 这款软件就是根据Image ...
  • qq_14845119
  • qq_14845119
  • 2016年12月12日 16:13
  • 1022

Prisma 算法实现

**Prisma 算法实现** 最近有一款流行的APP, 叫(Prisma),用户选择一张需要处理的图片,再选择一张艺术风格照, 就可以在线生成一张美图啦! 效果如下: 该功能的实现算法:斯坦福大学...
  • luojun2007
  • luojun2007
  • 2016年07月20日 16:59
  • 1973

编写自己的sniffer(一)

http://www.cnblogs.com/Seiyagoo/archive/2012/06/30/2571297.html#2454757 一、系统环境   1、系统及类库要求: 类U...
  • chengfangang
  • chengfangang
  • 2013年01月08日 10:51
  • 1553

编写一个汇编语言数据编译器小记

    最近有一堆数据要处理,把数据按源文件要求链接成二进制数据,原理就像是汇编器一样。由于找不到适合的工具,于是花一天时间写了一个。要处理的数据源文件如下:DATA_TAB    DB       ...
  • zhongyh
  • zhongyh
  • 2006年04月21日 14:36
  • 1449

2.1 :java能不能自己写一个类叫java.lang.System/String正确答案

最近学习了下Java类加载相关的知识。然后看到网上有一道面试题是 能不能自己写个类叫java.lang.System? 网上提供的答案:通常不可以,但可以采取另类方法达到这个需求。所谓的另类方法指自...
  • xiaoliuliu2050
  • xiaoliuliu2050
  • 2016年11月11日 13:57
  • 369

java能不能自己写一个类叫java.lang.System/String正确答案

最近学习了下java类加载相关的知识。然后看到网上有一道面试题是 能不能自己写个类叫java.lang.System? 网上提供的答案:通常不可以,但可以采取另类方法达到这个需求。所谓的另类方法指...
  • tang9140
  • tang9140
  • 2015年01月15日 11:03
  • 4047

自己手动写个线程池

前言:   要自定义一个线程池,首先要有存固定的线程数目的阻塞队列,还要有个存放任务的阻塞队列。   如果任务放入时有线程为空闲直接交付于它执行否则就放入任务队列中   在内部有个线程会不断的扫...
  • yinbucheng
  • yinbucheng
  • 2016年07月26日 15:03
  • 561

局域网通信学习

用c#写的一个局域网聊天客户端 类似小飞鸽 最后在公司实习,新人不给活干,就自己随便看看,了解一些DevExpress控件啊,编码规范啊之类的,自己就寻思着写一点点小东西练习练习 ...
  • luthreestone
  • luthreestone
  • 2017年07月25日 10:27
  • 124

如何定义自己的迭代器

基本上iterator必须做两件事情1)必须指向某物2)他必须能够遍历任何一个有效区间。一旦定义了operator* operator++通常剩下的行为就比较简单了。必须确定iterator被正确的定...
  • hxxiaopei
  • hxxiaopei
  • 2006年09月24日 15:12
  • 3828
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自己写个 Prisma
举报原因:
原因补充:

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