在之前的两篇 GAN 系列文章–[GAN学习系列1]初识GAN以及[GAN学习系列2] GAN的起源中简单介绍了 GAN 的基本思想和原理,这次就介绍利用 GAN 来做一个图片修复的应用,主要采用的也是 GAN 在网络结构上的升级版–DCGAN,最初始的 GAN 采用的还是神经网络,即全连接网络,而 DCGAN 则是换成卷积神经网络(CNNs)了,这可以很好利用 CNN 强大的特征提取能力,更好的生成质量更好的图片。
原文是:
http://bamos.github.io/2016/08/09/deep-completion/
由于原文比较长,所以会分为 3 篇来介绍。
这篇文章的目录如下:
本文会先讲述背景和第一步的工作内容。
设计师和摄像师习惯使用一个非常强有力的工具–内容感知填充,来修复图片中不需要或者缺失的部分。图像修复是指用于修复图像中缺失或者毁坏的部分区域。实现图像的修复有很多种方法。在本文中,介绍的是在 2016年7月26日发表在 arXiv 上的论文“Semantic Image Inpainting with Perceptual and Contextual Losses”,这篇论文介绍如何采用 DCGAN 来实现图像修复。这篇文章会即兼顾非机器学习背景和有机器学习背景的读者,带有 [ML-Heavy] 标签的标题内容表示可以跳过这部分细节内容。我们只考虑有限制的修复带有缺失像素的人脸图片的例子。TensorFlow 实现的源代码可以在下面的 Github 地址上查看:
https://github.com/bamos/dcgan-completion.tensorflow
我们将从以下三个步骤来完成图片修复工作:
下面是两张修复前和修复后的图片例子:
下面是本文将用到的带有缺失区域的人脸例子:
对于上述几张图片例子,假设你正在设计一个系列来填充这些缺失的区域,你会选择如何做?你认为人脑会怎么处理它呢?你需要使用哪些信息来实现这个修复工作呢?
本文会主要关注下面两种信息:
这两种信息都非常重要。没有上下文信息,你怎么知道填充什么信息呢?没有感知信息,对于一个上下文来说会有很多种有效的填充方式。比如一些对于机器学习系统来说看上去是“正常”的填充信息,但对于我们人类来说其实就是非常奇怪的填充内容。
因此,有一个即精确又直观的捕获这两种属性,并且可以解释说明如何一步步实现图像修复的算法是再好不过了。创造出这样的算法可能只会适用于特殊的例子,但通常都没有人知道如何创造这样的算法。现在最佳的做法是使用统计数据和机器学习方法来实现一种近似的技术。
为了解释这个问题,首先介绍一个非常好理解而且能简明表示的概率分布:正态分布。下面是一个正态分布的概率密度函数(probability density function, PDF)的图示。你可以这么理解 PDF,它是水平方向表示输入空间的数值,在垂直方向上表示默写数值发生的概率。
上面这张图的绘制代码如下:
# !/usr/bin/env python3
import numpy as np
from scipy.stats import norm
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
plt.style.use('bmh')
import matplotlib.mlab as mlab
np.random.seed(0)
### 绘制一个正态分布的概率密度函数图###
# 生成数据 X范围是(-3,3),步进为0.001, Y的范围是(0,1)
X = np.arange(-3, 3, 0.001)
Y = norm.pdf(X, 0, 1)
# 绘制
fig = plt.figure()
plt.plot(X, Y)
plt.tight_layout()
plt.savefig("./images/normal-pdf.png")
接着可以从上述分布中采样得到一些样本数据,如下图所示:
绘制代码如下:
### 绘制从正态分布采样的 1D 散点图例子 ###
nSamples = 35
# np.random.normal 是从正态分布中随机采样指定数量的样本,这里指定 35个
X = np.random.normal(0, 1, nSamples)
Y = np.zeros(nSamples)
fig = plt.figure(figsize=(7, 3))
# 绘制散点图
plt.scatter(X, Y, color='k')
plt.xlim((-3, 3))
frame = plt.gca()
frame.axes.get_yaxis().set_visible(False)
plt.savefig("./images/normal-samples.png")
这是 1 维概率分布的例子,因为输入数据就只是一维数据,我们也可以实现二维的例子,如下图所示:
绘制代码如下:
### 绘制从正态分布采样的 2D 散点图例子###
delta = 0.025
# 设置 X,Y 的数值范围和步长值,分别生成 240个数
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-3.0, 3.0, delta)
print('x shape', x.shape)
# 根据坐标向量来生成坐标矩阵
X, Y = np.meshgrid(x, y) # X, Y shape: (240, 240)
print('X shape', X.shape)
print('Y shape', Y.shape)
# Bivariate Gaussian distribution for equal shape *X*, *Y*
# 等形状的双变量高斯分布
Z = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) # Z shape (240, 240)
print('Z shape', Z.shape)
plt.figure()
# 绘制环形图轮廓
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
nSamples = 200
mean = [0, 0]
cov = [[1, 0], [0, 1]]
# 从多元正态分布中采样,得到结果图中的黑点例子
X, Y = np.random.multivariate_normal(mean, cov, nSamples).T
plt.scatter(X, Y, color='k')
plt.savefig("./images/normal-2d.png")
绘制上述三张图的完整代码如下所示,代码地址为:
https://github.com/bamos/dcgan-completion.tensorflow/blob/master/simple-distributions.py
图片和统计学之间的关键关系就是我们可以将图片解释为高维概率分布的样本。概率分布就体现在图片的像素上。假设你正采用你的相机进行拍照,照片的像素数量是有限的,当你用相机拍下一张照片的时候,就相当于从这个复杂的概率分布中进行采样的操作。而这个分布也是我们用来定义一张图片是否正常。和正态分布不同的是,只有图片,我们是不知道真实的概率分布,只是在收集样本而已。
在本文中,我们采用 RGB 颜色模型表示的彩色图片。我们采用的是宽和高都是 64 像素的图片,所以概率分布的维度应该是 64×64×3≈12k。
首先为了更加直观,我们先考虑之前介绍的多元正态分布。给定x=1
时,y
最有可能的取值是什么呢?这可以通过固定x=1
,然后最大化 PDF 的值来找到所有可能的y
的取值。如下图所示:
上图中垂直的黑色直线经过的黑点就是符合要求的y
值。
这个概念可以延伸到我们的图像概率分布中,当我们知道某些数值,然后想填补完成缺失的数值的时候。只需要将它当做寻找所有可能缺失数值的最大问题,那么找到的结果就是最有可能的图片。
从视觉上观察由正态分布采样得到的样本,仅凭它们就找到概率密度函数是一件似乎很合理的事情。我们只需要选择最喜欢的统计模型并将其与数据相适应即可。
然而,我们并不会应用这个方法。虽然从简单分布中恢复概率密度函数是很简单,但这对于图像的复杂分布是非常困难和棘手的事情。其复杂性一定程度上是来自于复杂的条件独立性:图像中的每个像素值之间都是相互依赖的。因此,最大化一个通用的概率密度函数是一个极其困难而且往往难以解决的非凸优化问题。
第一篇主要介绍了图像修复的简单背景,然后就是开始实现的第一步,也是比较偏理论,将我们待处理的图片数据作为一个概率分布的样本,并简单用代码实现了一维和二维的正态分布函数图。
在下一篇将介绍第二步内容,也就是快速生成假数据的工作。
欢迎关注我的微信公众号–机器学习与计算机视觉,或者扫描下方的二维码,在后台留言,和我分享你的建议和看法,指正文章中可能存在的错误,大家一起交流,学习和进步!