Neural Style Transfer with OpenCV
src: https://www.pyimagesearch.com/2018/08/27/neural-style-transfer-with-opencv/、
source code: https://app.monstercampaigns.com/c/tortsem7qkvyuxc4cyfi
author: Adrian Rosebrock
学完这篇教程你将掌握通过 OpenCV、Python 还有深度学习来对 图片进行神经风格迁移(neural style transfer),到了本文最后你将能够制作自己的风格迁移图片。
最初的神经风格迁移算法是由 Gatys 等人于 2015 年的 A Neural Algorithm of Artistic Style [1] 论文中发布。
到了 2016 年 Johnson 等人发表了 Perceptual Losses for Real-Time Style Transfer and Super-Resolution(实时风格转移和超分辨率的感知损失)[2] 这将神经网络描述为使用感知损失的超分辨率问题。最终的结果是使一个神经风格迁移算法比 Gatys 等人的方法要快上三个数量级(其中也存在一些缺陷,我将在后文说到)。
使用 OpenCV 的神经风格迁移
我今天提到的方法能够在 CPU 上运行,并且还将在 GPU 上获得更好的表现。
我们将从对神经风格迁移的简短讨论开始,包括它是什么以及它的机制。之后我们将应用 OpenCV 和 Python 来实际应用神经风格迁移。
什么是神经风格迁移(neural style transfer)
图像 1:内容图片(左);风格图片(中);风格化输出(右)
神经风格迁移的处理过程如下:
- 获取一幅图像的风格;
- 之后将其应用到另一幅图片的内容上。
图 1 中展示了一个处理的例子,左边是我们的内容图片 —— 原作者本人在德国黑森林山顶喝啤酒眺望巴登。
中间的是我们用于获取风格的图片,Vincent van Gogh [3] 的 Starry Night。
右边的是将梵高的星夜风格应用到原作者本人照片后的输出。请注意我们是如何在应用过星夜的风格后,还能保留住起伏的山丘、森林、原作者甚至是啤酒的,就好像是梵高本人亲手绘出的一样。
问题是,我们该如何定义神经网络进行神经风格迁移?这有可能吗?
神经风格迁移的机制
到此处你可能挠破头皮还在想刚才那句话:“我们该如何定义神经网络进行神经风格迁移?”
有趣的是在最早 2015 年 Gatys 等人的论文中提出了一种根本不需要新结构的神经风格迁移算法,因此我们才能使用一个已预先训练过的网络,并定义一个损失函数,来实现我们风格迁移的最终目标并且能够优化损失函数。
现在的问题已不再是我们应该使用什么神经网络了,而是我们应该使用什么损失函数?
答案是一个三分量(three-component)的损失函数,包括有:
- Content loss
- Style loss
- Total-variation loss
其中每一 component 都被独立计算,并且之后被结合为一个单独的 meta-loss function。通过最小化 meta-loss function 我们也同时将一起优化了 content, style and total-variation loss。
虽然 Gatys 等人的方法可以产生很好的神经风格迁移结果,但问题是这样真是太慢了。
2016 年 Johnson 等人在 Gatys 等人的基础上提出了一种快上前者三个数量级的神经风格迁移算法,Johnson 等人将神经网络描述为了基于感知损失的超分辨率问题。
Johnson 等人的方法确实很快,但其最大的弊端是你不能随意选择你的风格图片。
取而代之的是您首先需要对网络进行训练,使其重现你想要图片的风格。一旦网络训练完成,你就可以将其应用到任何你想要的内容上了。你应该将 Johnson 等人的方法看作是对风格图像的投资,你最好要喜欢你所选的风格图像,因为你将一直训练你的网络来让其更好的在内容图片上重现出来。
Johnson 等人官方的神经风格模型训练文档可以在 GitHub 上找到。[4]
最后还有值得一看的是 Ulyanov 等人在2017 年发表的 Instance Normalization: The Missing Ingredient for Fast Stylization [5],将批标准化(batch normalization)改为实例正则化(instance normalization)将获得更快的实时性能也更具美感。
Johnson 与 Ulyanov 等人的模型都已经包含在了 “下载” 部分,一定要下载下来才能跟进本指南的剩余部分。
你还可以在原作者的书中 Deep Learning for Computer Vision with Python [6] 学到更多有关神经风格迁移的内容。
项目结构
项目中的文件你都可以在 ”下载“ 部分找到,下载之后你就可以使用 tree 命令来探查文件结构:
$ tree --dirsfirst
.
├── images
│ ├── baden_baden.jpg
│ ├── giraffe.jpg
│ ├── jurassic_park.jpg
│ └── messi.jpg
├── models
│ ├── eccv16
│ │ ├── composition_vii.t7
│ │ ├── la_muse.t7
│ │ ├── starry_night.t7
│ │ └── the_wave.t7
│ └── instance_norm
│ ├── candy.t7
│ ├── feathers.t7
│ ├── la_muse.t7
│ ├── mosaic.t7
│ ├── starry_night.t7
│ ├── the_scream.t7
│ └── udnie.t7
├── neural_style_transfer.py
├── neural_style_transfer_examine.py
└── neural_style_transfer_video.py
4 directories, 18 files
有了这些之后你就不用继续寻找别的东西了,我为在 images 目录中你准备了一些内容图片,也在 models 目录中为你准备好了由 Johnson 等人训练过的模型了,还有三个你将会用到的 Python 文件。
实现神经风格迁移
现在就是用 OpenCV 和 Python 来实现神经风格迁移的部分了,打开你的 neural_style_transfer.py 文件并且加入以下代码:
# import the necessary packages
import argparse
import imutils
import time
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", required=True,
help="neural style transfer model")
ap.add_argument("-i", "--image", required=True,
help="input image to apply neural style transfer to")
args = vars(ap.parse_args())
首先我们导入了必要的包并且进行了命令行参数的解析,要注意的有:
- imutils:这是可用包管理器 pip 安装的,并且我们需要 imutils==0.5.1,别忘了进行升级:
pip install --upgrade imutils - OpenCV:你需要 3.4 或者更新。
执行程序时我们还需加上两个命令行参数:
- --model:需要指明神经风格迁移模型的路径,下载部分中我为你准备了十一个;
- --image:需要被作用的内容图片路径,当然也要尝试使用自己的图片。
参数都是在程序运行时被传递与处理的,如果你还不熟悉命令行参数的解析原理,你一定要读读原作者的命令行参数解析一文。[7]
现在有意思的部分来了,我们将加载我们的图片与模型并且来计算出神经风格迁移的结果:
# load the neural style transfer model from disk
print("[INFO] loading style transfer model...")
net = cv2.dnn.readNetFromTorch(args["model"])
# load the input image, resize it to have a width of 600 pixels, and
# then grab the image dimensions
image = cv2.imread(args["image"])
image = imutils.resize(image, width=600)
(h, w) = image.shape[:2]
# construct a blob from the image, set the input, and then perform a
# forward pass of the network
blob = cv2.dnn.blobFromImage(image, 1.0, (w, h),
(103.939, 116.779, 123.680), swapRB=False, crop=False)
net.setInput(blob)
start = time.time()
output = net.forward()
end = time.time()
这个代码块中我们进行了如下的处理:
- 将预先训练过的神经风格迁移模型以变量 net 加载至内存当中;
- 加载图像并且重新天正其大小;
- 通过均值减法(mean subtraction)来构造一个 blob,你可以看原作者之前的文章 [8]来了解 cv2.dnn.blobFromImage 的用法;
- 倒数第二行通过 forward 方法来获得我们期望的输出图像也就是神经风格迁移的结果,在其处理前后我还加入了时间戳。
接下来,我们必须对输出图像进行后期处理:
# reshape the output tensor, add back in the mean subtraction, and
# then swap the channel ordering
output = output.reshape((3, output.shape[2], output.shape[3]))
output[0] += 103.939
output[1] += 116.779
output[2] += 123.680
output /= 255.0
output = output.transpose(1, 2, 0)
举例 NumPy 数组的值为 (1, 3, 452, 600):
- 数字 1 表明我们向网络中传递了某数量的图片;
- OpenCV 以通道优先表示输出图像中有 3 个通道;
- 最后两值即为输出图像的行和列。
上表代码的非注释第一行,我们将图像重塑简化为 (3, H, W) 后继续处理:
- 加上我们减去了的平均值(非注释的第二到四行);
- 缩放处理;
- 转置矩阵使其通道排在最后。
最后一步就是向我们的屏幕输出结果了:
# show information on how long inference took
print("[INFO] neural style transfer took {:.4f} seconds".format(
end - start))
# show the images
cv2.imshow("Input", image)
cv2.imshow("Output", output)
cv2.waitKey(0)
结果
在你都下载齐全后,打开你的终端执行如下命令:
$ python neural_style_transfer.py --image images/giraffe.jpg \
--model models/eccv16/the_wave.t7
[INFO] loading style transfer model...
[INFO] neural style transfer took 0.3152 seconds
这样我们就创造了深度学习的艺术品,终端的输出结果将显示出不同模型的不同处理时间。
Links
[1] https://arxiv.org/abs/1508.06576
[2] https://cs.stanford.edu/people/jcjohns/eccv16/
[3] https://en.wikipedia.org/wiki/Vincent_van_Gogh
[4] https://github.com/jcjohnson/fast-neural-style
[5] https://arxiv.org/abs/1607.08022
[6] https://pyimagesearch.com/deep-learning-computer-vision-python-book/
[7] https://pyimagesearch.com/2018/03/12/python-argparse-command-line-arguments/
[8] https://pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/
来源:oschina
链接:https://my.oschina.net/u/4274358/blog/4334965