22. 人工智能的味道 - 图像风格迁移

Python是人工智能编程的首选语言,至少当作者在键盘上敲下这行话时,这是事实。作为一本Python基础入门性质的教科书,本书无法就Python在人工智能与深度学习中的应用展开深入讨论。要理解深度学习的内部细节,需要复杂的数学知识。不过作为应用层面的开发者,读者或者不需要理解深度学习复杂的数学细节,简单借助于开源的工具包和模型,也可以享受到人工智能的益处。本章通过图像风格迁移这个示例,让读者尝尝人工智能的味道。

版权声明

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。

本文不可以以纸质出版为目的进行改编、摘抄。

本文节选自作者的《Python编程基础及应用》视频教程。

22.1 图像风格迁移

将上图中左上角图像的“风格”提取出来,应用到左下角的“原图”上,生成右方的图像,就是图像风格迁移的研究内容。 艺术风格是什么,即便在艺术家的视角看,也是见人见智的。显然不太可能从数学上准确地定义并求出图像的风格。到底怎么把一个缺乏明确数学定义的概念变成可以执行的程序,是困扰图像风格迁移的研究者的主要问题。

在将人工神经网络和深度学习应用于图像风格迁移之前,人们的主要思路就是分析某一种风格的图像,比如把毕加索画的全部画作进行统计分析,如小波分析,通过建立统计模型,然后迭代改变要做迁移的图像,让它更好地符合统计模型。这种方法有一个很大的缺点:一个程序只能做某一种风格或场景的图像风格迁移。

2015年,Gatsys等人在论文A Neural Algorithm for Artistic Style中首次将人工神经网络用于图像风格迁移,斯坦福大学的李飞飞和她的学生Justin Johnson等人于2016年进一步改进了算法,提高了图像风格迁移的速度。本章示例程序中图像风格迁移就是以Justin Johnson等人训练的深度神经网络模型为基础的。

22.2 深度神经网络

人工智能是一个研究了很多年的课题。它有众多的学派,其中符号学派将机器学习看作逆向演绎,联结学派对大脑进行逆向分析和模拟,进化学派在计算机上模拟生物进化,贝叶斯学派认为学习是一种概率推理形式,理论根基在于统计学。其中,联结学派的主要工具就是人工神经网络,人工神经网络模拟了大脑神经元和神经元间突触连接的结构和工作方式。而深度神经网络又是人工神经网络的一种。

上图展示了一个深度神经网络(Deep Neural Network) - DNN的示意图。图中的节点称之为神经元,神经元之间的连线模拟了人类大脑中脑细胞之间的突触连接。可以简单地认为,每个神经元具备计算或者是信号处理的功能,它通过突触从别的神经元获取输入,经过计算/处理后再通过突触向其它神经元输出信号。我们已知,大脑细胞之间的每个突触连接,其离子通道的导电能力是有差异的。与之对应,神经网络神经元之间的连接权重参数用来表征该连接的重要程度。

神经元的层次,数量,神经元之间的连接关系可以称为神经网络的结构。突触连接的权重、偏倚等信息则称为神经网络的参数。为满足特定任务的需要,比如在各种图片或者视频中识别出猫,工程师会设计特定的神经网络结构,并将大量经过标注的资料图片(图片中有猫的位置被人工标记)输入神经网络的输入层,通过神经网络的计算,从输出层获得识别结果,然后根据识别错误反向修改神经网络内部的参数,经过多次迭代后,神经网络的内部参数被修正到即便对于标注资料外的图片,神经网络也能识别出猫的程度。这一过程称为神经网络的训练。训练的结果称为模型,它包括了神经网络的结构以及内部参数。

猫长什么模样,在数学上很难精确定义。我们可以认为,上述神经网络的训练过程有点类似于人类学习的过程,经过训练的神经网络模型包括了“何种模式的图像是猫”的知识。这种知识虽然说不清道不明,但在实践中却非常有效。

同理,将梵高的星空等相似风格的画作作为数据集,也可以对深度神经网络进行训练,训练所得的模型包含了这类画作的”风格是什么“以及"如何把另一幅图片变成这种风格"的知识。本章的示例程序就是借助于Justin Johnson等人训练好的图像风格迁移网络模型,对图片进行风格应用的。在调入模型,创建好深度神经网络后,将图片”输入“给图像风格迁移网络,该网络会进行一系列的迭代计算,其输出就是应用了对应风格的被修改过的图片。

22.3 程序解读

本章的示例程序包含在目录C22_StyleTransfer当中,主程序为StyleTransfer.py。子目录images用于存储风格迁移的输入图片,models子目录用于存储风格图片及对应的图像风格迁移网络模型文件。其中,模型文件需要读者自行下载,详情请见Readme.txt。

该程序需要用到opencv-python以及matplotlib库。opencv是著名的C++语言编写的计算机视觉及图像处理工具包,本程序中,主要用它来读取、转换图片以及进行图像风格迁移网络的计算。matplotlib在本程序中用于交互和图片显示。

22.3.1 程序的使用

先下载并将模型文件(扩展名为.t7)存入models子目录。在Visual Source Code中打开StyleTransfer目录,打开StyleTransfer.py并运行即可。按上下方向键可以切换图片,按左右方向键则可以切换风格模型。如本章开始的样图所示,左上角是风格图片,左下角是原图,右边则是风格迁移的结果图片。由于风格迁移网络的计算量很大,所以切换图片或者风格时反应较慢。

22.3.2 数据结构

class App:def __init__(self):self.idxImage = 0   #当前被风格迁移的图片在images列表中的下标self.idxModel = 0   #当前使用的模型文件在models列表中的下标self.images = glob.glob("images/*.jpg")  self.paintings,self.models = [],[] t = glob.glob("models/*.jpg")for x in t:m = x[:-3] + "t7"       #模型文件的扩展名为.t7, 基本名与对应的风格图片相同if os.path.exists(m):   #仅在风格图片及模型文件同时存在时,将二者加入列表self.paintings.append(x)self.models.append(m)

为了避免过多的全部变量污染名字空间,作者把相关信息组织在App类型中。

数据成员
- images: 包含images子目录内全部jpg图片文件路径的列表,形如[‘images\1.jpg’, ‘images\2.jpg’, ‘images\3.jpg’]。
- paintings: 包含models子目录风格图片路径的列表,形如[‘models\candy.jpg’, ‘models\composition_vii.jpg’,…]。
- models: 包含models子目录内模型文件路径的列表,形如[‘models\candy.t7’, ‘models\composition_vii.t7’,…]。 paintings与models列表内的风格图片与模型是一一对应的,且文件基本名相同,仅扩展名不同。
- idxImage: 当前被风络迁移的内容图片在images列表中的下标。
- idxModel: 当前使用的模型文件在models列表中的下标。

glob.glob(“images/*.jpg”) 遍历images子目录,找出其中的全部jpg文件,生成一个包含全部jpg文件路径字符串的列表。上述图像风格迁移网络的模型文件的扩展名为.t7,这是PyTorch的数据保存格式。而PyTorch是Facebook的深度学习开源工具包,具体一点可以把PyTorch看做加入了GPU 支持的numpy,同时也可以看成一个拥有自动求导功能的强大的深度神经网络。

22.3.3 风格迁移

import cv2 as cv    #导入opencv库
class App:...def styleTransfer(self):net = cv.dnn.readNetFromTorch(self.models[self.idxModel]) #cv2.dnn_Netnet.setPreferableBackend(cv.dnn.DNN_BACKEND_OPENCV) #设置计算后台类型inImg = cv.imread(self.images[self.idxImage]) #读取输入的内容图片inp = cv.dnn.blobFromImage(inImg, 1.0, (inImg.shape[1],inImg.shape[0]),(103.939, 116.779, 123.68), swapRB=False, crop=False)net.setInput(inp)outImg = net.forward()outImg = outImg.reshape(3, outImg.shape[2], outImg.shape[3])outImg[0] += 103.939outImg[1] += 116.779outImg[2] += 123.68outImg /= 255outImg = outImg.transpose(1, 2, 0)return inImg, outImg

类App的styleTransfer()函数应用当前的图像风格迁移网络模型将当前内容图片风格迁移成输出图片。函数返回的inImg即为输入的内容图片,outImg则为输出图片,两者都是numpy三维数组。

读者可能会感到诧异,如此复杂的功能居然只有这么少的代码。是的,当你站在前人的肩膀上,只是调库(调用别人开发好的库)时,就是这么简单。

代码说明
- net = cv.dnn.readNetFromTorch() - 加载Torch格式的配置网络及其参数。执行后,net的类型为cv2.dnn_net,是一个深度神经网络。
- cv.imread()函数用于读取图像文件,其返回一个numpy的三维数组,其维度依次为图像的像素高,图像的像素宽,像素颜色通道数(通常为3)。
- 接下来,blobFromImage将inImg进行转换,变成深度神经网络所接受的数据格式;net.setupInput(inp)则把转换后的数据设定为神经网络的输入; net.forward()则应用神经网络进行计算,并在其输出层得到输出图像outImg;outImg也是一个numpy多维数组,其维度与数据格式与常规的图像有所不同;所以后续代码对其进行了一些格式变换。
- 最后,函数返回inImg-输入图像, outImg-输出图像两个用于表示图像的多维数组。

22.3.4 图像显示与刷新

class App:...def refresh(self,fig,axStyle,axIn,axOut):print("Style tranfering...")self.idxImage = self.idxImage % len(self.images)self.idxModel = self.idxModel % len(self.models)inImg, outImg = self.styleTransfer()print("Rendering...")styleImg = cv.imread(self.paintings[self.idxModel])axStyle.imshow(cv.cvtColor(styleImg,cv.COLOR_BGR2RGB))axIn.imshow(cv.cvtColor(inImg, cv.COLOR_BGR2RGB))axOut.imshow(cv.cvtColor(outImg, cv.COLOR_BGR2RGB))fig.canvas.draw()

函数refresh()负责对当前内容图像进行风格迁移运算,并将风格风像,内容图像,迁移后的输出图像显示在对应的子图(Axes)中。其中,axStyle用于显示风格图像,axIn显示内容图像,axOut用于显示输出图像。

self.idxImage和self.idxModel分别对len(self.images)和len(self.models)进行求模,目的是防止列表的越界访问(后续代码中,切换图片和风格时没有进行越界限制)。

axStyle/axIn/axOut.imshow()负责将图像显示在子图中。请注意,在显示之前,还应用cv.cvtColor()函数进行了一次图像格式转换,这是因为OpenCV中的图像其颜色通道顺序为BGR,即三维数组第三维中下标0对应蓝色,下标1对应绿色,下标2对应红色;而Matplotlib中,图像的颜色通道顺序为RGB,为了能正确显示,交换一下颜色通道顺序。

...
fig = plt.figure(figsize=(12,6))
axStyle = plt.subplot(231)
axIn = plt.subplot(234)
axOut = plt.subplot2grid((2,3),(0,1),rowspan=2,colspan=2)
plt.subplots_adjust(0,0,1,1,0,0)    #设置子图间的间距为0
for ax in (axStyle,axIn,axOut):ax.set_axis_off()                #子图不显示坐标轴
app.refresh(fig,axStyle,axIn,axOut) #刷新显示
fig.canvas.mpl_connect('key_release_event',on_key_release)  #连接键盘事件响应函数
plt.show()

我们在matplotlib中创建1个图和3个子图。plt.subplot(231)将图的可视区域等分成2行3列,然后在第1个区域创建一个子图。同理,plt.subplot(234)将图的可视区域等分成2行3列,然后在第4个区域(即第2行的第1个区域)创建子图。上述第1个区域,第4个区域是从1开始计数的。

plt.subplot2grid()也是将图的可视区域分成2行3列,然后在第0行的第1列开始创建子图,子图横向占2列,纵向占两行。这里的第0行,第1列从0开始计数,显然,这里的计数方式跟subplot()函数不同。读者不必为此感到困惑,这没有为什么。作者将此差异视作matplotlib设计者犯下的一个小错误,如果全部保持相同的计数规则,对于使用者而言,可能可容易理解一些。

fig.canvas.connect()函数连接键盘松开事件至on_key_release()函数, 当读者松开(release)键盘按键时,on_key_release()函数将被调用执行。

22.3.5 交互响应

def on_key_release(event):if event.key == 'up':app.idxImage-=1elif event.key == 'down':app.idxImage+=1elif event.key == 'left':app.idxModel-=1elif event.key == 'right':app.idxModel+=1else:returnapp.refresh(fig,axStyle,axIn,axOut)

on_key_release()函数响应按键松开事件。如果是上-up, 下-down键,修改idxImage切换内容图片;如果是左(left),右(right)键,修改idxModel切换图像风格迁移网络模型。最后,调用app.refresh()函数进行风格迁移和界面刷新。

22.4 小结

下图是重庆大学虎溪校区图书馆及其在云湖上的倒影经过风格迁移后的效果。

现实中很多类似的计算问题,人类无法对相关概念进行准确的数学定义。比如:什么是图像的风格? 何种形式的图像模式是一只猫? 在乳腺X片里,什么样的图像可能是恶性的结节? 在这种情况下,使用经过标注的数据集来训练神经网络,经过训练的神经网络从数据集中学习了相关的知识。这些知识包含在神经网络的内部参数中。即便这种知识的表达和真正的数学含义有些说不清,道不明,但我们仍然可以应用这些经过训练的神经网络来解决各种现实问题。

本文节选自作者的《Python编程基础及应用》视频教程及同名教科书。想完整零基础学习Python程序设计,欢迎使用此免费视频教程。

人工智能的味道 - 图像风格迁移 by Python相关推荐

  1. 毕设 深度学习图像风格迁移 - opencv python

    文章目录 0 前言 1 VGG网络 2 风格迁移 3 内容损失 4 风格损失 5 主代码实现 6 迁移模型实现 7 效果展示 8 最后 0 前言

  2. java图像风格迁移_Python+OpenCV 图像风格迁移(模仿名画)

    现在很多人都喜欢拍照(自拍).有限的滤镜和装饰玩多了也会腻,所以就有 APP 提供了模仿名画风格的功能,比如 prisma.versa 等,可以把你的照片变成 梵高.毕加索.蒙克 等大师的风格. 这种 ...

  3. 【人工智能】python图像风格迁移,来欣赏梵高风格的石原里美吧!

    图像的风格迁移,心心念念好久了啊! 简单几个步骤,就可以转换图片风格啦. 1. 在github下载模型,模型后缀名是 t7,新建 model 文件夹用来存放模型 图像风格迁移模型链接 2. 新建 fe ...

  4. 图像迁移风格保存模型_图像风格迁移也有框架了:使用Python编写,与PyTorch完美兼容,外行也能用...

    原标题:图像风格迁移也有框架了:使用Python编写,与PyTorch完美兼容,外行也能用 选自Medium 作者:Philip Meier 机器之心编译 编辑:陈萍 易于使用的神经风格迁移框架 py ...

  5. python图片风格迁移毕设_Python简单实现图像风格迁移

    下载W3Cschool手机App,0基础随时随地学编程导语 T_T之前似乎发过类似的文章,那时候是用Keras实现的,现在用的PyTorch,而且那时候发的内容感觉有些水,于是我决定... 好吧我确实 ...

  6. 【人工智能专题】基于 GAN 的艺术风格化——图像风格迁移

    原文:https://mp.weixin.qq.com/s?__biz=MzAxMzEwMDM2Mg==&mid=2652847175&idx=3&sn=51dcb41bc5c ...

  7. 图像风格迁移_图像风格迁移—谷歌大脑团队任意图像风格化迁移论文详解

    点击蓝字关注我们 AI研习图书馆,发现不一样的世界 风格迁移 图像风格化迁移是一个很有意思的研究领域,它可以将一张图的风格迁移到另外一张图像上,由此还诞生了Prisma和Ostagram这样的商业化产 ...

  8. CVPR 2021 | 澳洲国立大学提出基于模型的图像风格迁移

    ©作者|侯云钟 学校|澳洲国立大学博士生 研究方向|计算机视觉 本文从另外一个角度解读,澳洲国立大学郑良老师实验室 CVPR 2021 新工作.一般而言,我们需要同时利用两张图片完成图像的风格迁移(s ...

  9. TensorFlow练手项目三:使用VGG19迁移学习实现图像风格迁移

    使用VGG19迁移学习实现图像风格迁移 2020.3.15 更新: 使用Python 3.7 + TensorFlow 2.0的实现: 有趣的深度学习--使用TensorFlow 2.0实现图片神经风 ...

  10. 图像迁移风格保存模型_一种图像风格迁移方法与流程

    本发明涉及图像处理技术领域,更为具体地,涉及一种图像风格迁移方法. 背景技术: 近年来,由深度学习所引领的人工智能技术浪潮,开始越来越广泛地应用到社会各个领域,尤其是在在计算机视觉领域,图像风格迁移作 ...

最新文章

  1. 我的理解:box-sizing
  2. oracle 10g
  3. Unity Editor开发
  4. 神作!3万程序员在学,这本深度学习宝典刷爆IT圈!
  5. c语言里有js的预编译环节吗,C语言第十一讲,预处理命令.
  6. IntelliJ IDEA 2021连接MySql数据库的操作
  7. uboot kernel 博客
  8. C++_数据类型_算术运算符_取模运算_递增递减运算_赋值运算符_比较运算符---C++语言工作笔记014
  9. The LAO将于4月2日启动Neptune DAO,旨在为其他区块链项目提供流动性
  10. 【SpringBoot】解决拦截器注入 Service 为空问题
  11. Go 语言初级教程之一[变量声明]
  12. 看看ConcurrentLinkedQueue源码 in Java 9
  13. YIi2 Pjax简单使用
  14. Kotlin中定义编译时常量
  15. 导出DMP文件实现数据库备份、数据迁移流程
  16. uhs3内存卡有哪些_三分钟教你看懂存储卡标识
  17. python批量读取图片并复制入word_提取出 Word 文档里的图片  并利用 python 批量转换格式...
  18. const的作用和用法
  19. revit二次开发--Reference
  20. 在php中 var什么意思,php关键字”var”的作用是什么?

热门文章

  1. Win 10 下 android studio显示 Intel haxm无法安装,以及VT-X和hyper-x的冲突问题
  2. 联邦学习:加密算法Paillier,Affine,IterativeAffine
  3. Typora设置图片上传服务
  4. 计算机基础——4.1 数字通信入门
  5. 微信公众号开发模式几点介绍
  6. java打印指定宽度_如何设置图片打印尺寸,长与宽指定大小(CM)?
  7. python获取图片长宽高,Python获取图片的大小/尺寸
  8. 常见音频编码格式总结
  9. 超参数(Hyperparameter)
  10. Unity3D 游戏贴图(法线贴图,漫反射贴图,高光贴图)