手把手教你搭建能够实现 Prisma 风格迁移效果的 iOS 酷炫应用(附代码)

本文为雷锋字幕组编译的技术博客,原标题 Transforming Pictures with Neural Style Transfer in iOS,作者为 Navdeep Singh。 翻译 | 廖明月 吴桐 蔡雨萌 整理 | 凡江

随着 2012 年深度神经网络在 ImageNetchallenge 比赛上以 AlexNet 模型获胜,深度神经网络开创了空前的高潮。AI 工程师已经将深度学习技术应用到越来越多的问题域,包括预训练的深度美国有线电视新闻网模型。还有什么比创造艺术更富有创造力呢?

一种已经提出并实施的想法,称为「神经风格转换」,允许你能够利用预训练的深度神经网络模型,并将某一图像的风格,例如或梵高或莫尼特的任何杰作,迁移到另一个图像,例如你的个人资料图片或你最喜欢的小狗的图片,从而创造了一个混合你的图片内容和名作风格的图像。

实际上有一个 iOS 应用程序称为 Prisma,它赢得了 2016 年度最佳应用程序,就是这样,它在短短几秒钟内,可以将你的图片转换成你所选择的任何风格。

在本文中,您将了解如何训练一个快速的神经风格迁移模型,并在自己的 iOS 上使用,实现 Prisma 可以做到的事情。

快速神经迁移模型的训练

在这一节中,按照以下步骤学习如何利用 TensorFlow 使用快速神经风格转移算法来训练模型:

1. 在你的 Mac 终端或者最好是有着 GPU 驱动的 Ubuntu 上,运行 git clone 命令,复制链接 this Github repo,这是约翰逊快速风格迁移的一个很好的 TensorFlow 实现的分支,修改后经过训练的模型允许在 iOS 或 Android 应用程序中使用。

2. 运行 cd 命令 进入快速风格迁移目录,然后运行 setup.sh 脚本来下载预训练的 VGG-19 模型文件以及 MS COCO 训练数据集。

3. 运行以下命令,使用风格图像 starry_night.jpg 和内容图像 WW1.jPG,通过训练创建检查点文件:

mkdir checkpoints
mkdir test_dir
python style.py --style images/starry_night.jpg --test images/ww1.jpg --test-dir test_dir --content-weight 1.5e1 --checkpoint-dir checkpoints --checkpoint-iterations 1000 --batch-size 10

在图像目录中还有一些其他风格的图像,你可以以此用来创建不同的检查点文件。这里使用的 starry_night.jpg 风格图像是梵高的一幅名画:

使用梵高的画作作为风格图片

在 NVIDIA GTX 1070 GPU 驱动的 Ubuntu 下整个训练需要大约五小时,当然在如果在 CPU 上会花更长的时间。

4. 用文本编辑器打开 evaluate.py 文件,并将以下两行代码取消批注(第 158 和 159 行):

# saver = tf.train.Saver()
# saver.save(sess, "checkpoints_ios/fns.ckpt")

5. 运行以下命令建立一个新的检查点,输入图像命名为 img_placeholder,转移后的图像命名为 preds:

python evaluate.py --checkpoint checkpoints \
--in-path examples/content/dog.jpg \
--out-path examples/content/dog-output.jpg

6. 运行以下命令创建一个 TensorFlow 图文件并载入检查点中的权重参数,这将创建一个约 6.7MB 的大小 .pb 文件:

python freeze.py --model_folder=checkpoints_ios --output_graph fst_frozen.pb

7. 假设你已拥有一个 /ft.file 目录,将生成的 st_frozen.pb 文件复制到 /ft.file 目录下,直接 cd 进入你的 TensorFlow 源代码根目录,如 ~/tensorflow-1.4.0,然后运行以下命令创建为 .pb 文件生成一个量化模型。

bazel-bin/tensorflow/tools/quantization/quantize_graph \
--input=/tf_files/fst_frozen.pb  \
--output_node_names=preds \
--output=/tf_files/fst_frozen_quantized.pb \
--mode=weights

这将把固化图文件的大小从 6.7 MB 缩减到 1.7 MB,它意味着如果你在 App 中为 50 个不同的风格载入了 50 个模型,增加的大小将会是 85 MB。

以上就是利用一张风格图像和输入图像训练和量化一个快速神经风格迁移模型的全部步骤。你可以在 test_dir 目录下检查步骤三中生成的图像,看一看风格迁移的效果。如果需要的话,你还可以试着玩一玩含超参数的模型,看一看那些不同的,很可能更好的风格迁移效果。代码文件提供在 https://github.com/jeffxtang/fast-style-transfer/blob/master/docs.md#style 上。

一个重要提示:当你在你的 iOS 或者 Android app 上使用这些模型之前,需要记录下输入图像的精确宽度和高度值作为步骤五中 --in-path 的参数,iOS 或 Android 的代码将会调用图像的宽度和高度值(你很快就会看到是如何调用的),否则当你在 App 上运行这些模型的时候将会得到 Conv2DCustomBackpropInput: Size of out_backprop doesn't match computed 的错误提示。

在 iOS 系统上添加和测试神经风格迁移模型

第一件事是手动建立 TensorFlow 库,如果你此前尚未安装过这个库。然后执行以下步骤就可以在你的 iOS App 上获取 TensorFlow 支持和并添加神经风格迁移模型,并试运行你的 App。

  1. 如果你已经拥有了一个添加了 TensorFlow 手册库的 iOS app,可以跳过下面这一步。否则,创建一个新的基于 Objective-C 语言的 iOS app,比如可以命名它为 NeuralStyleTransfer,或者在已经存在的 app 中,在 PROJECT 下的 Build Settings 配置中创建一个新的自定义设置,名称是 TENSORFLOW_ROOT,值为 $HOME/tensorflow-1.4.0,假定上面是你 TensorFlow 1.4.0 的安装地址。然后在 TARGET 下的 Build Settings 配置中将 Other Linker Flags 设置如下:
-force_load $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/lib/libtensorflow-core.a $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/protobuf_ios/lib/libprotobuf.a $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/protobuf_ios/lib/libprotobuf-lite.a $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/downloads/nsync/builds/lipo.ios.c++11/nsync.a

然后将 Header Search Paths 设置如下:

$(TENSORFLOW_ROOT) $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/downloads/protobuf/src $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/downloads $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/downloads/eigen $(TENSORFLOW_ROOT)/tensorflow/contrib/makefile/gen/proto

2. 将 fst_frozen_quantized.pb 文件和几个测试所用图片拖放到你的工程文件夹中,在 https://github.com/PacktPublishing/Intelligent-Mobile-Projects-with-TensorFlow/tree/master/ch4/ios/NeuralStyleTransfer 中的 NeuralStyleTransfer app 文件夹中找到相同的 ios_image_load.mm 和 .h 文件,把它们复制到工程文件夹下。

3. 将 ViewController.m 文件重命名为 ViewController.mm,把它和原 ViewController.h 文件替换为从上面的 GitHub 网址链接获取中的 ViewController.h 和 .mm 文件。

4. 在 iOS 模拟器中或者你的 iOS 设备中运行 App,你会看到一个狗图片:

5. 点击选择 Fast Style Transfer 选项,过几秒钟,你会看到一个带有 starry night 风格的新图片。

你可以很简单的通过选择你最喜欢的图片作为风格图片然后跟随之前的步骤编译带有不同风格的其他模型。然后可以按照这段的步骤在你的 iOS App 中使用模型。这里有使用 iOS 的详细代码。

使用快速神经迁移模型回顾 iOS 代码

在 ViewController.mm 中包含许多重要的代码片断它在输入图片的预处理和转移图片的后处理中是独特的。

1. 两个常量,wanted_width,wanted_height,作为图片的高度和宽度定义为相同的值,这里的图片就是步骤 5 中的 dog.jpg:

const int wanted_width = 300;
const int wanted_height = 400;

2. iOS 的 dispatch 队列是用来在 non-UI 线程加载和运行你的快速神经迁移模型并且在风格迁移后图片生成了,以下为将图片发送到 UI 线程的代码:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    UIImage *img = imageStyleTransfer(@"fst_frozen_quantized");
    dispatch_async(dispatch_get_main_queue(), ^{
        _lbl.text = @"Tap Anywhere";
        _iv.image = img;
    });
});

3. 定义浮点型 3 维张量用于转换输入图片:

tensorflow::Tensor image_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({wanted_height, wanted_width, wanted_channels}));
auto image_tensor_mapped = image_tensor.tensor<float, 3>();

4. 发送到 tensorflow Sess->Run 方法中的输入节点名和输出节点名与训练模型的时候是相同的 :

std::string input_layer = "img_placeholder";
std::string output_layer = "preds";
std::vector<tensorflow::Tensor> outputs;
tensorflow::Status run_status = session->Run({{input_layer, image_tensor}} {output_layer}, {}, &outputs);

5. 当模型运行完成并且返回输出张量 (包含 0 到 255 的 RGB 值) 时,你需要调用 tensorToUIImage 通用函数把张量数据转换为 RGB buffer:

UIImage *imgScaled = tensorToUIImage(model, output->flat<float>(), image_width, image_height);
static UIImage* tensorToUIImage(NSString *model, const Eigen::TensorMap<Eigen::Tensor<float, 1, Eigen::RowMajor>, Eigen::Aligned>& outputTensor, int image_width, int image_height) {
    const int count = outputTensor.size();
    unsigned char* buffer = (unsigned char*)malloc(count);
    for (int i = 0; i < count; ++i) {
        const float value = outputTensor(i);
        int n;
        if (value < 0) n = 0;
        else if (value > 255) n = 255;
        else n = (int)value;
        buffer[i] = n;
}

6. 现在,你可以把 buffer 转化成 UIImage 实例在调整它的大小之前,以下为代码:

UIImage *img = [ViewController convertRGBBufferToUIImage:buffer withWidth:wanted_width withHeight:wanted_height];
UIImage *imgScaled = [img scaleToSize:CGSizeMake(image_width, image_height)];
return imgScaled;

如果你对这个主题感兴趣,你可以使用 Jeff Tange 的书来研究更多的深度学习和强化学习 app。这本书包括超过 10 种 iOS,android 和 raspberry pi app 使用 tensorflow 来运行,使用 scratch 进行编译,离线运行所有绚丽的 tensorflow 模型:从计算机视觉,语音合成到生成对抗网络和类似于 Alphazero 的深度强化学习模型。

原文链接:https://medium.com/@navdeepsingh_2336/transforming-pictures-with-neural-style-transfer-in-ios-6988b79b34ee

原文发布于微信公众号 - AI研习社(okweiwu)

原文发表时间:2018-07-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PingCAP的专栏

TiDB 源码阅读系列文章(十四)统计信息(下)

在 统计信息(上) 中,我们介绍了统计信息基本概念、TiDB 的统计信息收集/更新机制以及如何用统计信息来估计算子代价,本篇将会结合原理介绍 TiDB 的源码实...

1163
来自专栏大数据智能实战

LargeVis可视化技术学习

大图可视化一直是大数据可视化领域的一个关键技术,当前有各种办法,但是今年出来了一个LargeVis的技术,因此对这个技术进行复现和学习一下。 前面有很多基础理论...

4257
来自专栏新智元

10 亿图片仅需 17.7微秒:Facebook AI 实验室开源图像搜索工具Faiss

【新智元导读】Facebook的 FAIR 最新开源了一个用于有效的相似性搜索和稠密矢量聚类的库,名为 Faiss,在10亿图像数据集上的一次查询仅需17.7 ...

4215
来自专栏数据小魔方

不等宽柱形图

今天要跟大家分享的图表是不等宽柱形图! ▽▼▽ 基础等柱形图一般只能展示一个维度的数据,但是如果想要在柱形图中同时展示两个维度的数据(柱高一个维度、柱宽另一个维...

3105
来自专栏ATYUN订阅号

【实践操作】 在iOS11中使用Core ML 和TensorFlow对手势进行智能识别

在计算机科学中,手势识别是通过数学算法来识别人类手势的一个议题。用户可以使用简单的手势来控制或与设备交互,让计算机理解人类的行为。 这篇文章将带领你实现在你自己...

5316
来自专栏和蔼的张星的图像处理专栏

LCT代码跑起来先文章思路总结

论文才刚开始看,但是代码先跑了一下看结果,有一点小坑,记录下: 首先去论文的github上去下载代码:点这里 readme里其实写了怎么搞:

5623
来自专栏机器之心

教程 | 如何用百度深度学习框架PaddlePaddle做数据预处理

3466
来自专栏专知

【干货】主题模型如何帮助法律部门提取PDF摘要及可视化(附代码)

【导读】本文是Oguejiofor Chibueze于1月25日发布的一篇实用向博文,详细介绍了如何将主题模型应用于法律部门。文章中,作者分析了律师在浏览大量的...

3887
来自专栏机器学习实践二三事

caffe introduction & classification

caffe 介绍 caffe是Berkely的深度学习框架,在流行的deep learning framework里属于使用人数很多的,github上的统计显示...

2345
来自专栏Unity Shader

Shader初学笔记:一种三维等值线的算法

把反距离加权算法转换到三维空间,是不是很方便的算出三维空间下的等值线了呢,再结合Shatter Toolkit插件进行剖切,修改修改代码。应该可以解决地层模型任...

51418

扫码关注云+社区

领取腾讯云代金券