制作混合图像(2014)
摘要
一篇2014年的博文,讲解如何制作混合图像——即随观看距离变化而改变的图像,结合了2006年 Oliva/Torralba/Schyns 的技术、直观的傅里叶分析以及代码。
<p><a href="https://lobste.rs/s/16tsvr/making_hybrid_images_2014">评论</a></p>
查看缓存全文
缓存时间: 2026/10/18 13:49
# 制作混合图像
来源:https://www.jeremykun.com/2014/09/29/hybrid-images/
这篇文章从我以前的 WordPress 博客移植而来,原地址:(https://jeremykun.wordpress.com/2014/09/29/hybrid-images/)。如果你发现渲染或排版有任何问题,请[给我发邮件](mailto:[email protected])。
[](https://www.jeremykun.com/img/2014/monalisa.png)#### 蒙娜丽莎
列奥纳多·达·芬奇的《蒙娜丽莎》是有史以来最著名的画作之一。关于她那神秘的微笑,人们一直有着诸多讨论。达·芬奇运用了一种名为*晕涂法*(sfumato)的标志性文艺复兴技法,这种方法涉及许多混合了微妙颜料的薄薄釉层。令人惊叹的效果是,当你直视蒙娜丽莎的微笑时,它似乎消失了;但当你看向背景时,你的周边视觉却能看到一张微笑的脸。
人们可以花数十年时间从各种角度研究这些大师的作品,但如果我们想专注于那抹微笑的消失特性,数学可以提供宝贵的洞察。的确,虽然萨尔瓦多·达利可能并不知道自己的作品与达·芬奇之间的联系,但在数百年后,达利做了一件艺术家版的"数学隔离问题"的工作,那就是他的画作《凝视地中海的加拉》。
[](https://www.jeremykun.com/img/2014/gala-dali.jpg)#### 凝视地中海的加拉,从20米外看则变成了亚伯拉罕·林肯的肖像(向罗斯科致敬) - 萨尔瓦多·达利,1974
在这里你看到前景中有一个女人,但当你离画很远的地方望去,就有一个(或多或少)清晰的亚伯拉罕·林肯的图像。这里,注视方向的问题成了这件作品最醒目的焦点。当然,达利和达·芬奇并没有在写下方程式和计算积分;他们的艺术表达远没有那么精确。但我们这些艺术水平有限的人也有自己的工具:数学、科学和编程。
2006年,Aude Oliva、Antonio Torralba 和 Philippe G. Schyns 使用这些工具,将达利的距离感和达·芬奇的渐隐微笑融合为一个统一的概念。在他们的2006年论文 (http://web.archive.org/web/20191231032911/http://cvcl.mit.edu:80/publications/OlivaTorralb_Hybrid_Siggraph06.pdf) 中,他们提出了"混合图像"的概念,如下所示。
[](https://www.jeremykun.com/img/2014/monalisas1.png)#### 科学的蒙娜丽莎
如果你仔细观察,会看到三个女人,每一个看起来都有那么一点点奇怪,好像在努力忍住笑意,但她们实际上都没有在笑。眯起眼睛或者后退几米,她们看起来明显是在微笑。这个效果相当戏剧化。虽然说得可能有些夸张,但这三个女人简直就是蒙娜丽莎的现代版本——"科学的蒙娜丽莎"。
他们的技术还有另一个也许更著名的版本,因为被更广泛地报道,那就是"玛丽莲·爱因斯坦"——近看是阿尔伯特·爱因斯坦,远看是玛丽莲·梦露。
[](https://www.jeremykun.com/img/2014/marilyn-einstein.png)#### 玛丽莲·爱因斯坦
这张图触及了核心问题:眼睛在近距离和远距离分别看到什么。事实证明,你可以用一些基本的傅里叶分析来解决这个问题(并创造出像上面那样出色的艺术作品)。
## 直观的傅里叶分析(及参考文献)
傅里叶分析的基本思想是:光滑的函数很难理解,而如果我们能把它们分解成更简单的部分该有多好。将复杂事物分解为简单部分是整个数学中的主要工具之一,而傅里叶分析是其应用最清晰的例子之一。
具体来说,我们关心的是具有某些特定性质(如"光滑性"和"有限性")的函数 $f(x)$,这里我不再详细说明。构建模块是复指数函数
$$\displaystyle e^{2 \pi i kx}$$
其中 $k$ 可以是任意整数。如果你学过一些线性代数(没学过的话可以跳过),我可以简洁地概括这个概念:复指数构成了平方可积函数向量空间的一组标准正交基。
回到通俗语言,傅里叶定理说的是:*任何*我们关心的函数都可以被分解成(也许是无限多个)这种形式的片段,称为*傅里叶系数*(我在这里滥用了"系数"这个词)。分解的方式也令人愉快地简单:它是一个线性组合。非正式地说,这意味着你只是把所有的复指数加在一起,每个带有特定的权重。从数学上讲,从函数到其傅里叶系数的转换被称为*傅里叶变换*,所有傅里叶系数的集合被称为*傅里叶频谱*。所以如果你想了解你的函数 $f$,或者更重要的是以某种方式*修改*它,你可以检查和修改它的频谱。之所以有用,是因为傅里叶系数在声音和图像中有非常自然的物理解释,我们将在图像中看到这一点。
我们把 $f(x)$ 和复指数写成了*一个*实变量的函数,但你也可以对两个变量(或一百个变量!)做同样的事情。而且,如果你愿意做一些简化,忽略复数的复数性质,你可以把"两个变量的复指数"可视化为条纹图像,其方向和厚度对应两个参数(即偏移方程中的 $k$ 变成了两个系数)。下面的视频展示了这样的复指数如何用来构建一个细节惊人的图像。左帧显示当前正在添加哪个复指数,右帧显示所有叠加在一起的层次。我认为结果相当美丽。
这恰恰说明了达·芬奇的精细分层思想有多么强大:它已经强大到了极致,因为它可以创造出*任何图像*!
对于上面那样的数字图像,一切都是有限的。所以你得到的不是一个无限精确的函数和相应的无限傅里叶系数集合,而是一组有限的采样值(像素)和一个相应的傅里叶系数网格。但重要而美妙的定理是——我想强调这是多么具有突破性的重要意义:
如果你给我一张图像(或任何函数!),我可以非常高效地*计算*出它的分解。
而同一个定理让你可以反向操作:如果你给我分解结果,我可以相当容易地计算出原始函数的采样值。实现这一点的算法被称为快速傅里叶变换(Fast Fourier Transform),如果说*任何*数学或计算机科学成果有资格被称为改变世界,那就是快速傅里叶变换了。很难指出具体的应用 (http://dsp.stackexchange.com/questions/69/why-is-the-fourier-transform-so-important),因为这个变换在科学和工程中无处不在,但如果没有傅里叶变换和快速计算它的能力,我们绝对不可能拥有手机、卫星、互联网,或像现在这样小巧的电子产品。
构建混合图像是操纵两张图像的傅里叶频谱并将它们组合回单张图像的一个特别好的例子。这就是我们现在要做的。
顺便说一句,由于篇幅简短,上面的讨论对涉及的数学内容是个很大的简化。我以数学家会反对的方式进行了概括和简化。如果你想看到对这个材料更好的讲解,这个博客有一个长系列文章从头开始讲解傅里叶变换及其离散形式。参见我们的四篇入门文章 (https://www.jeremykun.com/primers/),它们引导进入主要内容文章,在那里我们用 Python 实现快速傅里叶变换 (https://www.jeremykun.com/2012/07/18/the-fast-fourier-transform/) 并将其用于对图像添加数字水印 (https://www.jeremykun.com/2013/12/30/the-two-dimensional-fourier-transform-and-digital-watermarking/)。注意在那些文章中,和本文一样,所有使用的材料和代码都发布在这个博客的 Github 页面 (https://github.com/j2kun/) 上。
## 高频和低频
对于图像来说,解释傅里叶系数的范围是很容易的。你可以想象系数位于平面上的一个网格中,如下所示:
[](https://www.jeremykun.com/img/2013/sherlock-spectrum.png)#### 傅里叶频谱的一个例子
网格中的每个点对应于傅里叶系数的"强度"。也就是说,它是相应复指数的(复)系数的模长。靠近原点的点直观上对应于图像中宽泛、平滑的变化,这些被称为"低频"系数。而远离原点的点对应于尖锐的变化和边缘,同样被称为"高频"分量。所以如果你想要"混合"两张图像,你会挑选在这两个区域中强度互补的图像。这就是为什么爱因斯坦(满头凌乱的头发和皱纹)和梦露(平滑的面容)是如此好的候选对象的原因。这也是为什么在前面的视频中我们逐一叠加傅里叶分量时,先出现模糊的形状然后才出现精细的细节。
此外,我们可以通过简单地去除低频分量来"提取"高频傅里叶分量。这比说起来要复杂一些,因为你想让从"有"到"无"的过渡在某种意义上是平滑的。对此的恰当讨论会涉及到采样和奈奎斯特频率(Nyquist frequency),但这超出了本文的范围。相反,我们只会在不做太多解释的情况下定义一族"滤波函数",并观察它们效果良好。
**定义:** 方差为 $\sigma$、中心为 $(a, b)$ 的*高斯滤波函数*是函数
$$\displaystyle g(x,y) = e^{-\frac{(x – a)^2 + (y – b)^2}{2 \sigma^2}}$$
它看起来像这样
[](https://www.jeremykun.com/img/2014/gaussian2d.png)#### 一个高斯函数,图片来自维基百科
特别地,在零点处函数值为1,并随着距离增大逐渐下降到零。参数 $\sigma$ 控制着它消失的速率,在上面的图片中中心设置为 $(0,0)$。
现在我们要做的是:取我们的图像,计算其频谱,然后与某个高斯函数逐坐标相乘。如果我们想要去除高频分量(称为"低通滤波器",因为它让低频通过),我们可以直接将傅里叶系数乘以滤波值 $g(x,y)$;如果我们做的是"高通滤波",则乘以 $1 – g(x,y)$。
在进入代码之前,这里有一个低通滤波的例子。首先,取这张玛丽莲·梦露的照片
[](https://www.jeremykun.com/img/2014/marilyn1.png)#### 玛丽莲·梦露
现在计算它的傅里叶变换
[](https://www.jeremykun.com/img/2014/dft.png)#### 玛丽莲的离散傅里叶变换
应用低通滤波器
[](https://www.jeremykun.com/img/2014/filtered-dft1.png)#### 玛丽莲的滤波后频谱
然后进行傅里叶逆变换得到图像
[](https://www.jeremykun.com/img/2014/low-passed-marilyn1.png)#### 低通滤波后的玛丽莲
事实上,这是 Photoshop 等程序中模糊图像的常见操作(由于显而易见的原因,它被称为*高斯模糊*)。以下是执行此操作的 Python 代码。你可以在博客的 Github 页面 (https://github.com/j2kun/) 上[下载它](https://github.com/j2kun/hybrid-images)以及制作本文所用的其他所有资源。
```
import numpy
from numpy.fft import fft2, ifft2, fftshift, ifftshift
from scipy import misc
from scipy import ndimage
import math
def makeGaussianFilter(numRows, numCols, sigma, highPass=True):
centerI = int(numRows/2) + 1 if numRows % 2 == 1 else int(numRows/2)
centerJ = int(numCols/2) + 1 if numCols % 2 == 1 else int(numCols/2)
def gaussian(i,j):
coefficient = math.exp(-1.0 * ((i - centerI)**2 + (j - centerJ)**2) / (2 * sigma**2))
return 1 - coefficient if highPass else coefficient
return numpy.array([[gaussian(i,j) for j in range(numCols)] for i in range(numRows)])
def filterDFT(imageMatrix, filterMatrix):
shiftedDFT = fftshift(fft2(imageMatrix))
filteredDFT = shiftedDFT * filterMatrix
return ifft2(ifftshift(filteredDFT))
def lowPass(imageMatrix, sigma):
n,m = imageMatrix.shape
return filterDFT(imageMatrix, makeGaussianFilter(n, m, sigma, highPass=False))
def highPass(imageMatrix, sigma):
n,m = imageMatrix.shape
return filterDFT(imageMatrix, makeGaussianFilter(n, m, sigma, highPass=True))
if __name__ == "__main__":
marilyn = ndimage.imread("marilyn.png", flatten=True)
lowPassedMarilyn = lowPass(marilyn, 20)
misc.imsave("low-passed-marilyn.png", numpy.real(lowPassedMarilyn))
```
第一个函数从指定参数的高斯函数中采样值,将函数离散化并将值存储在矩阵中。然后 `filterDFT` 函数通过逐坐标相乘来应用滤波器(注意这些都是 numpy 数组)。我们可以用高通滤波器做同样的事情,产生下面这张棱角分明的图像
[](https://www.jeremykun.com/img/2014/high-passed-marilyn1.png)#### 高通滤波后的玛丽莲
如果我们将这两张图像取平均,基本上就能回到原始图像。
[](https://www.jeremykun.com/img/2014/sum-of-marilyns1.png)#### 两张玛丽莲之和
所以这和混合图像之间唯一的区别就是:你取一张图像的低通部分和另一张图像的高通部分。然后艺术就在于平衡参数,使平均后的图像看起来正确。确实,用下面的爱因斯坦照片和上面的梦露照片,我们可以相当好地重现 Oliva-Torralba-Schyns 的作品。我觉得如果再做一些调整会更好(我对原始图像几乎没做任何居中/对齐/缩放处理)。
[](https://www.jeremykun.com/img/2014/marilyn-einstein-hybrid-collage.png)#### 阿尔伯特·爱因斯坦、玛丽莲·梦露,以及它们的混合。
以下是实现它的代码
```
def hybridImage(highFreqImg, lowFreqImg, sigmaHigh, sigmaLow):
highPassed = highPass(highFreqImg, sigmaHigh)
lowPassed = lowPass(lowFreqImg, sigmaLow)
return highPassed + lowPassed
```
有趣的是,反向操作并没有产生同样令人满意的结果,但在技术上仍然可行。所以高通图像确实应该包含大量高频分量,低通则反之,这一点特别重要。
[](https://www.jeremykun.com/img/2014/backwards.png)#### 反向高低通
你可以在他们的网络画廊 (https://web.archive.org/web/20180311064128/http://cvcl.mit.edu/hybrid_gallery/gallery.html) 看到 Oliva 等人构造的其他一些混合图像。
## 下一步
我们如何进一步推进这个想法?我能想到几条路径。最明显的是看看这如何扩展到视频。能否找到通用的参数,使得当两段视频(逐帧使用此技术)被混合时,在近距离只能清楚地看到其中一个?或者,能否对视频应用三维变换并以某种有原则的方式对其进行修改?我认为不太可能发现什么惊人的结果,但谁知道呢?
第二条是看看我们可利用的[许多其他变换](http://en.wikipedia.org/wiki/List_of_transforms)。操纵这些变换的频谱如何影响原始图像?你能否制作出在其他意义上进行混合的图像?
最后,我们能否将这个思想降低维度,应用到一维信号上?具体来说……
相似文章
我的图片是如何进行抖动处理的
作者解释了使用 Imagemagick 进行图片抖动处理的方法,模拟调幅和调频打印模式,以减小文件大小并获得特定美学效果,灵感来源于 Low Tech Magazine 和粉色单色设计。
通过同质-异质分割的合成图像生成后策展
本文提出了一种与生成器无关的生成后策展方法,通过将真实类别拆分为规范的同质子集和无冗余的异质子集,并基于保真度-多样性准则对合成图像进行评分,从而选取信息丰富的合成图像子集。该方法持续优于现有的数据选择基线,并且在合成样本数量减少多达40%的情况下,仍能达到与真实数据相当的性能。
Chimera:混合视觉扩散Transformer的设计与Chinchilla式缩放
本文介绍了Chimera,一种具有原则性缩放方案的混合视觉扩散骨干网络,结合了Kimi Delta Attention、多头潜在注意力和稀疏混合专家,以高效处理长上下文图像和视频生成。它还提出了HeteroP,一种模块级超参数迁移方案,以及Chinchilla式缩放定律,用于训练一个具有20亿激活参数的110亿参数模型。
抖动二维码
本文介绍了一个抖动二维码生成器,它允许你在保持二维码可扫描的同时嵌入照片,并解释了相应的底层技术,如功能图案、拜耳抖动和弗洛伊德-斯坦伯格误差扩散。
面向插花的四维数字档案设计
本文提出了一种四维数字观看系统,用于存档插花创作过程。该系统采用同步摄像头、眼动追踪和3D高斯泼溅(3D Gaussian Splatting)技术,支持从任意角度交互式观看。