ImageMagick 示例 -
傅里叶变换

索引
ImageMagick 示例 前言与索引
简介
傅里叶变换
ImageMagick 中的 FFT/IFT
傅里叶变换的性质
实用应用
高级应用
 
FFT 乘法与除法 (低级示例 - 子页面)

简介

图像处理中最难理解的概念之一就是傅里叶变换。造成这种情况的原因有两个。首先,它在数学上很复杂,其次,得到的图像不像是原始图像,很难解释。然而,利用傅里叶变换可以提供新的方法来进行熟悉的处理,例如增强亮度和对比度、模糊、锐化和去除噪声。但它还可以提供在普通图像域中无法实现的新功能。这些功能包括对典型相机失真(例如运动模糊和镜头散焦)进行反卷积(也称为去模糊),以及使用归一化互相关进行图像匹配。本页面的目标是尝试解释傅里叶变换的背景和简化数学,并给出使用傅里叶变换可以进行的处理的示例。如果你觉得这太难了,你可以跳过它,只关注属性和示例,从 ImageMagick 中的 FFT/IFT 开始。对于有兴趣的人来说,另一个不错的简单讨论,包括与光学的类比,可以在 直观的傅里叶理论解释 中找到。范德堡大学工程学院的讲义对于那些对数学更感兴趣的人来说也非常有参考价值: 一维和二维傅里叶变换频域滤波。其他数学参考包括维基百科关于 傅里叶变换离散傅里叶变换快速傅里叶变换 以及 复数 的页面。感谢 Sean Burke 对原始演示的编码,以及 ImageMagick 的创建者将其集成到 ImageMagick 中。两者都是英勇的努力。 许多示例使用 HDRI 版本的 ImageMagick,这对于保持变换图像的精度是必要的。建议你编译一个个人 HDRI 版本,如果你想充分利用这些技术。

傅里叶变换

图像通常由一个“像素”数组组成,每个像素由一组值定义:红色、绿色、蓝色以及有时还会包含透明度。但在这里,为了我们的目的,我们将忽略透明度。因此,每个红色、绿色和蓝色“通道”都包含一组“强度”或“灰度”值。这被称为栅格图像的“空间域”。这只是一个花哨的说法,即图像由每个“位置”或“空间位置”的“强度值”定义。但图像也可以用另一种方式表示,被称为图像的“频率域”。在这个域中,每个图像通道都以正弦波的形式表示。在这样的“频率域”中,每个通道都具有“幅度”值,这些值存储在基于 X,Y “空间”坐标而不是 X,Y “频率”坐标的位置。由于这是一个数字表示,因此频率是“最小”或单位频率的倍数,像素坐标表示这些单位频率的索引或整数倍数。这是由“任何行为良好的函数都可以用正弦波的叠加(组合或求和)来表示”这一原理得出的。换句话说,“频率域”表示只是存储和再现“空间域”图像的另一种方式。但是,图像如何表示为“波”呢?

图像即波

好吧,如果我们从任何图像中取一行或一列像素,然后将其绘制出来(使用“gnuplot”并使用脚本“im_profile”生成),你就会发现它看起来很像一个波形。

  magick holocaust_tn.gif -colorspace gray miff:- |\
    im_profile -s - image_profile.gif
[IM Output] ==> [IM Output]
如果波动在间距和幅度上更加规律,你将得到更类似于波形图案的东西,例如...

  magick -size 20x150 gradient: -rotate 90 \
          -function sinusoid 3.5,0,.4   wave.gif
  im_profile -s wave.gif wave_profile.gif
[IM Output] ==> [IM Output]
然而,虽然这种规则的波形图案与上面显示的图像轮廓有点相似,但它过于规则。但是,如果你要将更多的波形加在一起,你可以得到一个更接近图像中轮廓的图案。

  magick -size 1x150 gradient: -rotate 90 \
          -function sinusoid 3.5,0,.25,.25     wave_1.png
  magick -size 1x150 gradient: -rotate 90 \
          -function sinusoid 1.5,-90,.13,.15   wave_2.png
  magick -size 1x150 gradient: -rotate 90 \
          -function sinusoid 0.6,-90,.07,.1    wave_3.png

  magick wave_1.png wave_2.png wave_3.png \
          -evaluate-sequence add added_waves.png
[IM Output]  + [IM Output]  + [IM Output] ==> [IM Output]
另见 添加偏差梯度,以获取上述内容的另一种示例。这种“波形叠加”(波形叠加)更接近,但仍然与原始图像的轮廓不完全匹配。但是,你可以继续以这种方式,添加更多波形并对其进行调整,这样产生的复合波形越来越接近原始图像的实际轮廓。最终,通过添加足够的波形,你可以完全再现原始图像的轮廓。这是数学家 Joseph Fourier 发现的。它的现代解释表明,“任何行为良好的函数都可以用正弦波的叠加来表示”。换句话说,通过将足够数量的具有适当频率和幅度的正弦波加在一起,可以再现任何波动图案。因此,“频率域”表示只是存储和再现“空间域”图像的另一种方式。“傅里叶变换”就是计算图像由哪些“波形”组成,就像上面的示例中所做的那样。

图像中的二维波

上面展示了如何使用多个正弦波来近似图像单行的轮廓的一个示例。然而,图像都是二维的,因此用来在“频率域”中表示图像的波形也需要是二维的。下面是一个这样的二维波形的示例。该波形包含多个组成部分。图像示例

在 ImageMagick 中使用 FFT/IFT

实现说明

ImageMagick 使用 FFTW,离散傅里叶变换库,该库要求图像转换为浮点数(复数)并从浮点数转换回来,并且最初是在 IM 版本 6.5.4-3 中实现的。为了使其按人们通常对图像的期望工作,任何非正方形图像或具有奇数维度的图像都将被填充(使用 虚拟像素 成为图像最大宽度或高度的正方形)。为了允许在图像中心正确地对齐“FFT 原点”,图像的维度也必须是偶数(2 的倍数)。其结果是,在应用逆傅里叶变换后,需要将图像裁剪回原始尺寸以去除填充。由于傅里叶变换由“复数”组成,因此变换的结果无法直接可视化。因此,复变换将被分成两个分量图像,以两种形式之一表示。
[Diagram]
复数
实部/虚部

实部和虚部

复数”的正常数学和数值表示是一个包含“实部”(a)和“虚部”(b)的浮点数对。不幸的是,这两个数字可能包含负值,因此无法形成可视化的图像。因此,这种表示形式不能在普通版本的 ImageMagick 中使用,因为该版本会裁剪此类图像(参见下面的示例,显示结果效果。但是,在使用 HDRI 版本的 ImageMagick 时,你仍然可以生成、使用甚至保存傅里叶变换图像的这种表示形式。它们可能本身不是有用的或甚至无法作为图像查看,但你仍然可以对它们应用许多数学运算。为了生成这种表示形式,我们使用运算符的“加号”形式,“+fft”和“+ift”,并将分别在下面的 FFT 作为实部和虚部 中进行详细介绍。
[Diagram]
复极坐标
幅度/相位

幅度和相位

复数”的直接数值表示对于图像处理来说不是很有用。但是,通过将值绘制到二维平面上,你可以将值转化为 极坐标表示形式,它包含“幅度”(r)和“相位”(θ)分量。这种形式在图像处理中非常有用,特别是幅度分量,它本质上指定了构成图像的所有频率。“幅度”分量只包含正值,并且直接映射到图像值。它没有固定的值范围,尽管除了直流或零频率颜色之外,值通常都很小。因此,幅度图像通常看起来非常暗(实际上是黑色)。通常需要通过缩放幅度并对其强度值应用对数变换来突出显示任何视觉细节。得到的“对数变换”幅度图像被称为图像的“频谱”。但是,请记住,应该使用“幅度”图像,而不是“频谱”图像来进行逆变换。出现在图像中心“原点”的直流(代表“直流电”)或“零频率”颜色将是整个图像的平均颜色值。此外,由于输入图像不包含“虚部”,因此直流相位值也将始终为零相位,产生纯灰色颜色。“相位”分量在 -π 到 +π 之间变化。首先将其偏差到 0 到 2π 范围,然后将其缩放到实际图像值,范围为 0 到 QuantumRange(由 编译时内存质量 决定)。因此,零相位将具有纯灰色值(适用于每个通道),而否定相位将具有纯黑色('0')值。请注意,纯白色('QuantumRange')几乎与它相同,但并不完全相同。幅度和相位 FFT 表示形式的图像使用正常的 FFT 运算符,“+fft”和“+ift”生成。我们将首先在 生成 FFT 图像及其逆 中介绍这一点。

生成 FFT 图像及其逆
(幅度和相位)

现在,让我们简单地尝试对 Lena 图像进行傅里叶变换往返。也就是说,我们简单地执行正向变换,然后立即应用逆变换以获取原始图像。然后,我们将比较结果以查看生成的质量水平。

  time magick lena.png -fft -ift lena_roundtrip.png


echo -n "RMSE = "
magick compare -metric RMSE lena.png lena_roundtrip.png null:
echo -n "PAE = "
magick compare -metric PAE lena.png lena_roundtrip.png null:
[IM Output] ==> [IM Output]
[IM Text]
上面的 "compare" 程序返回了衡量两张图片差异程度的指标。在本例中,您可以看到整体差异非常小,大约为 0.22%。至少在一个像素上的峰值差异(“PAE”,峰值绝对误差)大约为 1%。您可以通过使用 ImageMagick 的 HDRI 版本来改进这一点。(参见下面的 带 HDRI 的 FFT)。让我们仔细看看上面往返过程中生成的 FFT 图像。

  magick lena.png -fft    +depth +adjoin lena_fft_%d.png
[IM Output]
原始图像
==> [IM Output]
幅度
[IM Output]
相位
正如 John M. Brayer 关于傅里叶变换所说... 我们通常不显示相位图像,因为大多数看到它们的人很快就会屈服于致幻剂或最终进入西藏寺院。请注意,“-fft” 运算符生成了两张图像,第一张图像是“幅度”分量(是的,它大部分是黑色的,中间有一个彩色点),而第二张,看起来几乎随机的图像,包含“相位”分量。PNG 图像只能存储每个文件一张图像,因此“+adjoin” 或输出文件名中的“%d”实际上并不需要,因为 IM 会处理它。但是,我在上面包含了这些选项以确保完整性,以便清楚地表明我正在生成两个单独的图像文件,而不是一个。有关更多详细信息,请参阅 编写多图像序列。由于生成了两个图像,幅度图像(第一个或第零个图像)被保存到“lena_fft_0.png”中,而相位图像(第二个图像)被保存到“lena_fft_1.png”中。
为了防止因保存 FFT 图像而导致的任何扭曲,最好不要将它们保存到磁盘,而是在内存中保存它们,并在您处理图像时使用它们。

如果您必须保存,最好使用 Magick 文件格式“MIFF”,以便以最高质量(位深度)保留图像。此格式还可以将多个图像保存到一个文件中。对于脚本工作,您也可以使用详细的“TXT”枚举像素格式。

请勿使用“JPEG”,“GIF”图像格式保存它们。

如果您必须将这些图像保存到文件中以供实际查看,例如用于 Web 浏览器,请使用图像格式“PNG”,并将其“+depth”重置为内部默认值(正如我们在这些示例中所做的那样)。但是,它只能存储每个文件一张图像。

TIFF”文件格式也可以使用,尽管它不适合 Web 浏览器,但它允许每个文件有多个图像。

将中间图像保存到单个文件的最佳方法是使用“MIFF”文件格式...

  magick lena.png -fft  +depth lena_fft.miff
或者,您可以使用“-write”将它们保存到完全独立的文件名中(参见 写入图像)...

  magick lena.png -fft  +depth \
          \( -clone 0 -write lena_magnitude.png +delete \) \
          \( -clone 1 -write lena_phase.png +delete \) \
          null:
请注意,在上面,我使用了特殊的“NULL:”图像格式来丢弃两个图像,这两个图像仍然保存在内存中,以便进一步处理。最后,我们再次读取这两个图像,以便将其魔术地还原为正常的“空间”图像...

  magick lena_magnitude.png lena_phase.png -ift lena_restored.png
[IM Output] [IM Output] ==> [IM Output]
FFT 过程生成的两个图像对修改非常敏感,即使很小的变化也会导致结果严重失真。因此,重要的是永远不要以任何可能扭曲这些值的图像格式保存它们。重要的是要记住,当从频域恢复图像时,需要这两个图像。因此,如果您打算将它们用于图像重建,那么保存一个图像,而丢弃另一个图像,这样做是没有用的。

仅幅度或仅相位图像

最后,让我们尝试仅从其幅度分量或仅从其相位分量重建图像。

  magick lena_fft_0.png  -size 128x128 xc:'gray(50%)' \
                                                  -ift lena_magitude_only.png

  magick -size 128x128 xc:gray1  lena_fft_1.png  -ift lena_phase_only.png
[IM Output]
仅幅度
[IM Output]
仅相位
您将注意到,实际上是相位图像包含了图像的大部分位置信息,而幅度实际上包含了大部分颜色信息。这不是完全准确的,因为信息存在一些重叠,但这通常是这种情况。'仅幅度'图像将始终具有白色的角,因为使用了恒定的 50% 相位图像。您可以通过使用随机相位图像来消除这些白色区域。但是,请确保中心像素的相位是完美的 50% 灰色,否则整个图像会变暗。'仅相位'图像使用了恒定的 1% 灰色(几乎纯黑色)幅度图像进行转换。即使使用这种恒定的幅度,它仍然会产生非常强烈的像素斑块,尤其是在边缘。您只需要记住,需要这两个图像才能重建原始图像。

频率谱图像

您可能已经注意到,幅度图像(第一个或第零个图像)看起来几乎完全是黑色的。它实际上并非如此,但对于我们的眼睛来说,所有值都非常非常小。这样的图像在查看和研究时并不真正有趣,因此让我们使用对数变换来增强结果,以生成“频率谱”图像。这是通过对 归一化 的“幅度”图像应用强 评估对数变换 来完成的。

  magick lena_fft_0.png -auto-level -evaluate log 10000 \
          lena_spectrum.png
[IM Output] ==> [IM Output]
现在我们可以看到幅度图像的频谱版本的细节。您甚至可能会在频谱图像中看到一些特定的颜色,但通常情况下,频谱图像中的这些颜色并不重要。更重要的是每个频率的整体强度以及它们产生的模式。因此,您可能还希望在增强后将频谱图像灰度化。您需要使用多少对数增强取决于图像,因此您应该调整它,直到获得您需要清楚地看到图像频率谱模式的细节量。
或者,您可以使用以下小型 shell 脚本,来计算用于特定幅度图像的“对数缩放因子”。

  scale=`magick lena_fft_0.png -auto-level \
          -format "%[fx:exp(log(mean)/log(0.5))]" info:`
  magick lena_fft_0.png -auto-level \
          -evaluate log $scale    lena_spectrum_auto.png
[IM Output]
但是请记住,您不能使用频谱图像进行逆“-ift”变换,因为它会产生过于明亮的图像。

  magick lena_spectrum.png lena_fft_1.png -ift lena_roundtrip_fail.png
基本上,当您增强“幅度”图像时,您也以相同的方式增强了结果图像,从而产生了所示的严重“裁剪”结果。
[IM Output]

HDRI FFT 图像

当我们将傅里叶变换的结果映射到图像表示时,我们将浮点 "复数" 的值缩放并转换为整型图像值。这自然会产生 舍入误差和其他“量子”效应,尤其是在较小的低频幅度中。如果精度对您的图像处理很重要,那么您将需要使用 位质量(例如 ImageMagick 的 Q32 或 Q64 位版本),或者更好的是使用 HDRI 版本的 ImageMagick,以便将值存储为浮点数。当使用带有 幅度和相位 表示的傅里叶变换的 HDRI 版本的 IM 时,幅度分量仍然将全部为正值,因此可以像上面所示的那样使用,只是精确度更高。但是,相位分量仍将如前所述进行偏差和缩放。换句话说,HDRI 中的幅度和相位表示完全相同,只是精度更高。
例如,这里我使用 HDRI 版本的 ImageMagick 来生成图像的另一个“往返”转换。

  # HDRI version of IM used
  time magick lena.png -fft -ift lena_roundtrip_hdri.png


echo -n "RMSE = "
magick compare -metric RMSE lena.png lena_roundtrip_hdri.png null:
echo -n "PAE = "
magick compare -metric PAE lena.png lena_roundtrip_hdri.png null:
[IM Output]
[IM Text]
如果您将上面的结果与之前的非 HDRI 比较进行比较...
[IM Text]
您将看到,HDRI 版本的 IM 生成了一个更准确的结果,速度与以前大致相同(速度可能因您的计算机而异)。尽管它需要的内存比正常的 Q16 IM 多得多(参见 编译时质量)。但是,这些图像虽然更准确地表示了图像 FFT 的频率分量,但它们可能包含负值和小数值,只能使用可以处理浮点值的特殊 HDRI 支持的文件格式 保存。
支持浮点的文件格式包括“MIFF”,“TIFF”,“PFM”以及 HDRI 特定的“EXR”文件格式。但是,您可能需要设置“-define quantum:format=floating-point”才能使其正常工作。
在后面的示例中,处理图像的 FFT 将需要这种精度才能产生良好的结果。因此,当我们继续使用快速傅里叶变换时,HDRI 版本的 ImageMagick 将成为一项要求。

FFT 作为实部和虚部

到目前为止,我们只查看了傅里叶变换图像的“幅度”和“相位”表示。但是,如果您编译了 HDRI 版本的 IM,您也可以使用浮点“实数”和“虚数”分量来处理图像。这是通过使用选项“+fft”和“+ift”的“plus”版本来完成的。例如,这里我使用 HDRI 版本的 IM 也执行了图像的“往返”FFT,但这次生成了实数/虚数图像。

  # HDRI version of IM used
  time magick lena.png   +fft +ift   lena_roundtrip_ri.png


echo -n "RMSE = "
magick compare -metric RMSE lena.png lena_roundtrip_ri.png null:
echo -n "PAE = "
magick compare -metric PAE lena.png lena_roundtrip_ri.png null:
[IM Output]
[IM Text]
当您使用 plus 形式生成实数/虚数 FFT 图像时,您必须使用 HDRI 版本。如果您没有,大约 1/2 的值将为零,导致图像看起来“脏”。例如...

  # non-HDRI Q16 version of IM used  -- THIS IS BAD
  magick lena.png   +fft +ift   lena_roundtrip_ri_bad.png
[IM Output]
要记住的另一件事是,您生成的 FFT 图像的任何形式也会影响您要应用于 FFT 图像的所有图像处理操作。它们是非常不同的图像,因此它们必须以非常不同的方式处理,使用不同的数学运算。此外,与之前一样,您必须拥有实数和虚数分量图像才能恢复最终图像。例如,以下是将“黑色”图像替换为其中一个分量时发生的情况。

  # HDRI version of IM used
  magick lena.png +fft -delete 1 \
          -size 128x128 xc:black +ift lena_real_only.png
  magick lena.png +fft -delete 0 \
          -size 128x128 xc:black +ift lena_imaginary_only.png
[IM Output]
仅实数
[IM Output]
仅虚数
从这里您可以看到,实数/虚数 FFT 图像都包含关于原始图像的重要信息,而且信息量大致相同。这两者之间最大的区别是,特殊的 DC 或“平均颜色”没有虚数分量,因此只存在于幅度图像中。您在这两个图像中看到的对角镜面(实际上是 180 度旋转)效果是由另一个分量中包含的“符号”信息的丢失造成的。如果没有另一个分量,波可以认为是 180 度相位差,并产生这种奇怪的外观。这种信息丢失在两种类型的图像之间是相等的。

傅里叶变换的属性

恒定图像的 FFT

让我们演示一下这些属性。首先,让我们简单地取一个常数颜色图像,并获取其幅度。

  magick -size 128x128 xc:gold constant.png
  magick constant.png -fft +delete constant_magnitude.png
[IM Output] ==> [IM Output]
请注意,在这种情况下,幅度图像确实是纯黑色的,除了图像最中心的单个彩色像素外,该像素位于像素位置宽度/2、高度/2。此像素是图像的零频率或 DC(“直流”)值,是唯一不代表正弦波的像素。换句话说,此值仅仅是 FFT 常数分量!
为了更清楚地看到这个单个像素,让我们也放大图像的该区域...

  magick constant_magnitude.png -gravity center -extent 5x5 \
           -scale 2000% constant_dc_zoom.gif
[IM Output]
请注意,DC 点的颜色与原始图像相同。实际上,记住您看到的是三个值是一个好主意。也就是说,生成的图像实际上是三个单独的快速傅里叶变换。每个通道的红、绿、蓝图像通道的 FFT。FFT 本身对颜色没有真正的了解,只有颜色值或“灰度级”。实际上,可以将 FFT 变换应用于几乎任何颜色空间,因为实际上... 它并不关心!对于傅里叶变换来说,图像只是一个值数组,仅此而已。
虽然直流值的“相位”并不重要,但它应该始终为“零”角(相位颜色值为 50% 灰色)。如果未设置为 50% 灰色,则直流值将具有“不真实”分量,并且其值会根据给定的角度进行调制。

直流色的影响

在更典型的非恒定图像中,直流值是图像的平均颜色。如果您将图像完全模糊、平均或调整大小为单个像素或颜色,您通常会获得这种颜色。例如,让我们从“Lena”图像的 FFT 中提取直流像素。


magick lena.png -fft +delete lena_magnitude.png magick lena_magnitude.png -gravity center -extent 1x1 \ -scale 60x60 lena_dc_zoom.gif
[IM Output] ==> [IM Output] ==> [IM Output]
如您所见,图像的平均颜色是一种“暗粉色”。另一种思考这种特殊像素的方法是,它代表所有其他正弦波修改图像颜色周围的中心“偏差”水平。
例如,让我们将该“暗粉色”直流像素替换为其他颜色,例如更橙色的“番茄”颜色…

  magick lena.png -fft \
          \( -clone 0  -draw "fill tomato  color 64,64 point" \) \
          -swap 0 +delete -ift lena_dc_replace.png
[IM Output]
实际上发生的事情是,通过更改 FFT 图像中的直流值,您以相同的方式更改整个图像。实际上,直流值的任何变化(差异)都会从生成的图像中的每个像素中添加(或减去)。这就像我们真的在原始图像的每个像素中添加了一些常数一样。因此,重建图像中的最终像素颜色也可能被最大(白色)或最小(黑色)限制裁剪。因此,这不是推荐的图像颜色着色方法。虽然这比修改整个图像中的每个像素更简单,但 FFT 往返将使其成为总体上更慢的颜色着色技术。

正弦波图像的频谱

接下来,让我们看一下来自具有 4 个周期跨图像的单个正弦(或余弦)波图像的光谱

  magick -size 128x129 gradient: -chop 0x1 -rotate 90 -evaluate sine 4 \
          sine4.png
  magick sine4.png -fft +delete \
          -auto-level -evaluate log 100  sine4_spectrum.png
[IM Output] ==> [IM Output]
上述梯度图像的非寻常创建是必要的,以确保生成的正弦波图像完美地平铺在整个图像上。

正常的“gradient:”图像不能完美地平铺,因此从它生成的正弦波也不能完美地平铺。对这种不完美平铺的图像进行 FFT 变换,将导致一系列不希望的谐波,而不是傅里叶变换频谱中的单个“点”。

有关此问题的更多详细信息,请参见 Generating the Perfect Gradient

在上面的频谱图像(增强幅度图像)中,我们可以看到它有 3 个点。中心点与以前一样是平均直流值。另外两个点代表傅里叶算子在图像中找到的完美正弦波。由于图像宽度上的频率恰好为 4 个周期,因此两个频率像素恰好距离中心直流值 4 个像素。但为什么是两个像素呢?好吧,这是因为正弦单波可以用两种完全不同的方式描述(一种具有负方向和相位)。两种描述在数学上都是正确的,傅里叶变换不会区分它们。如果我们用 16 个周期的正弦波重复此操作,那么我们再次看到它有 3 个点,但这些点更远。在这种情况下,侧点与中心点相隔 16 个像素,分别在左侧和右侧。

  magick -size 128x129 gradient: -chop 0x1 -rotate 90 -evaluate sine 16 \
          -write sine16.png -fft -delete 1 \
          -auto-level -evaluate log 100 sine16_spectrum.png
[IM Output] ==> [IM Output]
由此可以看出,完美的正弦波将仅由两个点表示,并且位于适当的位置。此位置距离中心直流值的距离决定了正弦波的频率。波长越小,频率越高,因此点距离直流值越远。实际上,通过将图像的大小除以频率(点到中心的距离),您将获得波的波长(峰之间的距离)。在上面的情况下:128 个像素除以 16 个周期,得到每个“带”之间的波长为 8 个像素。这是 FFT 变换最重要的区别特征之一。原始图像上小特征的模式需要小波长,因此需要高频率。这会导致频域中的大规模效应。同样,大特征使用较小的频率,因此会生成较小的模式,尤其是在靠近中心的区域。在傅里叶变换中...
小变大,大变小。
这是处理傅里叶变换时需要牢记的最重要方面之一,因为它是从图像中去除噪声(小特征)并保留图像整体更大方面的关键。
让我们仔细看一下这三个“频率”,方法是绘制它们的原始幅度(而不是对数频谱)。

  magick sine16.png -fft -delete 1  miff:- |\
     im_profile - sine16_magnitude_pf.png
[IM Output]
注意直流值(图像的平均值或偏差)的值为 1/2,这是意料之中的(图像的平均值为完美的 50% 灰色),但傅里叶变换找到的两个 16 周期正弦波的实际幅度仅为最大值的 1/4。原始正弦波的幅度实际上是 1/2,但傅里叶变换将该幅度除以 2,在两个绘制的频率波之间共享结果,因此两个分量中的每一个的幅度只有 1/4。也就是说,傅里叶变换的正常部分。 [IM Output] FFT 图像中正负频率的对偶性解释了为什么所有 FFT 图像频谱(例如左侧重复的 Lena 频谱)始终关于中心对称。对于图像一侧的每个点,您总会在图像中心的旋转镜像处获得一个类似的“点”。同样的事情也发生在 FFT 图像对的“相位”分量上,但值也会有 180 度的位移(负相位)。这意味着每张图像的一半实际上是另一半的副本,但您需要两张图像才能重新创建原始图像。换句话说,两张图像仍然包含完全相同的信息,一张图像包含一半,另一张图像包含一半。它们一起产生一个整体。
在生成过程中,FFT 算法只生成图像的左半部分。另一半是通过生成的数据的旋转和复制生成的。

将频域图像转换回空间域图像时,算法再次只查看图像的左半部分。右半部分被完全忽略,因为它只是一个副本。

因此,当(在后面的示例中)您对 FFT 幅度图像进行“陷波滤波”时,您实际上只需要滤波幅度图像的左侧。您还可以通过忽略右半部分来节省一些工作。但是为了清楚起见,我将在两半都进行“陷波滤波”。

直接生成 FFT 图像

现在我们可以利用以上信息来实际生成正弦波图像。您只需要创建一个黑色和 50% 灰色图像对,然后添加具有适当幅度和相位的“点”。例如…

  magick -size 128x128  xc:black \
          -draw 'fill gray(50%)  color 64,64 point' \
          -draw 'fill gray(50%)  color 50,68 point' \
          -draw 'fill gray(25%)  color 78,60 point' \
          generated_magnitude.png
  magick generated_magnitude.png \
          -auto-level -evaluate log 3  generated_spectrum.png
  magick -size 128x128  xc:gray50  generated_phase.png
  magick generated_magnitude.png generated_phase.png \
          -ift  generated_wave.png
[IM Output] [IM Output] ==> [IM Output]
然后就得到一个完美的倾斜(且可平铺)正弦波。当然,您只能生成特定频率的完美正弦波,并且只能在方形图像中平铺(除非稍后调整大小)。不幸的是,所有频率在任何水平或垂直方向上也都是 2 的幂,这是这种技术的主要限制。
实际上,只需要第一个(最左边的)“gray25”点即可生成正弦波,因为 IFT 变换完全忽略图像的右半部分,因为这应该只是左半部分的旋转镜像。
直流值的相位必须具有“零角”(50% 灰色颜色)。如果您不能确保这一点,直流颜色值将受其非零相位的调制,从而产生更暗、可能“裁剪”的图像。
相位中的其他像素可以是您喜欢的任何灰度级,并且将有效地将正弦波在图像中“滚动”。同样,实际上只有最左边的点的相位才重要。右侧被完全忽略。只需确保中心直流相位像素保持 50% 灰色即可。

FUTURE: Perlin Noise Generator using FFT

垂直线的频谱

显示细线和粗线的 FFT 频谱演示小特征如何在图像的 FFT 中变得“大”,而大特征则变得“小”。将它链接回可以视为具有单个谐波的“线”的正弦波。旋转线

矩形图案图像的频谱

接下来,让我们看一下黑色背景中宽度为 8、高度为 16 的白色矩形的光谱。

  magick -size 8x16 xc:white -gravity center \
          -gravity center -background black -extent 128x128 rectangle.png
  magick rectangle.png -fft +delete \
          -auto-level -evaluate log 100 rect_spectrum.png
[IM Output] ==> [IM Output]
如您所见,生成的图像具有非常特定的模式,其中包含许多谐波频率。您还可以看到矩形似乎旋转了 90 度。这是不正确的,您看到的是我们之前提到的相同规则……大特征变小,小特征变大。因此,矩形的较小尺寸变得更大,而较大的尺寸变得更小。现在,让我们将矩形旋转 45 度。我们发现频谱也按相同方向旋转了 45 度。

  magick rectangle.png -rotate 45 -gravity center -extent 128x128 \
          -write rect_rot45.png -fft -delete 1 \
          -auto-level -evaluate log 100 rect_rot45_spectrum.png
[IM Output] ==> [IM Output]
如您所见,频域中的旋转相同。也就是说,某些旋转物体的效果也会在其傅里叶变换中旋转。但是,如果我们现在移动矩形...

  magick rectangle.png -rotate 45  -geometry +30+20 -extent 128x128 \
          -write rect_rot45off.png -fft -delete 1 \
          -auto-level -evaluate log 100 rect_rot45off_spectrum.png
[IM Output] ==> [IM Output]
频率模式没有移动。这是因为所有定位信息都包含在相位图像中。频率模式(幅度或其频谱不会改变,因为它移动了)。这种位置分离是傅里叶变换最重要的特征之一,因为它使您能够在更大的图像中搜索特定的图像模式,而不管产生该傅里叶频谱模式的对象的位置如何。

平坦圆形图案图像的频谱

接下来,让我们看一下来自具有白色、平坦圆形图案的图像的光谱,一种情况的直径为 12(半径为 6),另一种情况的直径为 24(半径为 12)。

  magick -size 128x128 xc:black -fill white  \
          -draw "circle 64,64 64,70" -write circle6.png -fft -delete 1 \
          -auto-level -evaluate log 100 circle6_spectrum.png

  magick -size 128x128 xc:black -fill white  \
          -draw "circle 64,64 64,76" -write circle12.png -fft -delete 1 \
          -auto-level -evaluate log 100 circle12_spectrum.png
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
请注意,第一个图像非常接近我们在上面进一步生成的 jinc 示例。但是,它有点破碎。这些伪像是由圆圈的尺寸小造成的。由于它是数字表示的,因此它的周长不是完美的圆形。我们再次看到,小的细节在变换后的频率空间中变得很大。较大圆圈的变换更好,因为它的周长更接近真正的圆圈。因此,我们得出结论,实际上平坦圆形形状的变换是一个 jinc 函数,并且包含较小直径圆圈的图像会产生更分散和更宽的变换特征。根据傅里叶变换的数学性质,从中心到频谱中第一个暗环中心的距离将为 1.22*N/d。当圆圈的直径为 d=12 时,我们得到一个距离为 1.22*128/12=13 的距离。同样,当圆圈的直径为 d=24 时,我们得到一个距离为 1.22*128/24=6.5 的距离。

高斯图案图像的频谱

接下来,让我们看一下来自两个图像的光谱,每个图像都包含一个白色高斯圆形图案,它们的 sigma 分别为 8 和 16

  magick -size 128x128 xc:black -fill white \
          -draw "point 64,64" -gaussian-blur 0x8 -auto-level \
          -write gaus8.png -fft -delete 1 \
          -auto-level -evaluate log 1000 gaus8_spectrum.png

  im_profile -s gaus8.png gaus8_pf.gif
  im_profile -s gaus8_spectrum.png gaus8_spectrum_pf.gif
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]

  magick -size 128x128 xc:black -fill white \
          -draw "point 64,64" -gaussian-blur 0x16 -auto-level \
          -write gaus16.png -fft -delete 1 \
          -auto-level -evaluate log 1000 gaus16_spectrum.png

  im_profile -s gaus16.png gaus16_pf.gif
  im_profile -s gaus16_spectrum.png gaus16_spectrum_pf.gif
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
除了图案矩形阵列产生的噪声外,结果表明高斯图案产生了几乎相同的高斯频率图案。更重要的是,该图案在外观上非常干净。当然,存在尺寸差异,同样遵循相同的规则,大变小,小变大。从数学性质上讲,频谱中的 sigma 将只是 N/(2*sigma),其中 sigma 来自原始图像。因此,对于大小为 N=128 且 sigma=8 的图像,频谱中的 sigma 将为 128/16=8。类似地,如果图像的 sigma 为 16,则频谱中的 sigma 将为 128/32=4。这是“大变小,反之亦然”规则的数学关系,了解这一点很有用。

网格图案图像的频谱

接下来,让我们转换一个只包含一组间距为 16x8 像素的网格线的图像。

  magick -size 16x8 xc:white -fill black \
          -draw "line 0,0 15,0" -draw "line 0,0 0,7" \
          -write mpr:tile +delete \
          -size 128x128 tile:mpr:tile \
          -write grid16x8.png -fft -delete 1 \
          -auto-level -evaluate log 100000 grid16x8_spectrum.png
[IM Output] ==> [IM Output]
生成的频谱只是一组点,其中间距更近的网格线产生更远的点,反之亦然。根据上面的属性,由于网格线相隔 16x8 像素,则点应该相隔 N/a=128/16=8 和 M/b=128/8=16,这正是此图像中测量到的结果。此模式尤其重要,因为它将使您了解傅立叶变换与图像中规则平铺模式的关系。这种平铺模式在其傅立叶变换中产生非常强的非中心网格模式。这里的关键是形状信息位于中心,但平铺信息位于其傅立叶变换中心之外的类似网格的阵列中。

更多频谱信息

如果您想了解更多关于频谱图像及其属性的信息,请点击以下链接。

实际应用

好的,现在我们已经介绍了基础知识,使用傅立叶变换有哪些实际应用?可以使用傅立叶变换完成的一些事情包括:1)增加或减少图像的对比度,2)模糊,3)锐化,4)边缘检测和 5)噪声去除。

改变图像对比度 - 系数开方

可以通过执行正向傅立叶变换,将幅度图像提高到某个幂,然后将其与相位一起用于逆傅立叶变换来调整图像的对比度。要提高对比度,使用略小于一的指数;要降低对比度,使用略大于一的指数。因此,让我们首先使用 0.9 的指数来提高 Lena 图像的对比度,然后使用 1.1 的指数来降低对比度。

  magick lena.png -fft \
          \( -clone 0 -evaluate pow 0.9 \) -delete 0 \
          +swap -ift lena_plus_contrast.png

  magick lena.png -fft \
          \( -clone 0 -evaluate pow 1.1 \) -delete 0 \
          +swap -ift lena_minus_contrast.png
[IM Output] ==> [IM Output]
[IM Output] ==> [IM Output]
但是,对原始图像执行此操作也会与对原始图像执行此操作具有相同的效果。也就是说,全局修改幅度与对原始图像进行全局修改具有相同的效果。

模糊图像 - 低通滤波

傅立叶变换最重要的性质之一是,空间域中的卷积等效于频域中的简单乘法。在空间域中,使用小的方形简单卷积滤波器(核)通过 -convole 选项模糊图像。这称为低通滤波器。最简单的滤波器只是一个等权重的方形阵列。也就是说,所有值都为一,在应用卷积之前,通过除以其总和进行归一化。这等效于局部或邻域平均。另一个低通滤波器是 -gaussian-blur-blur 提供的高斯加权圆形滤波器。在频域中,一种低通模糊滤波器只是一个由黑色包围的恒定强度白色圆圈。这类似于空间域中的圆形平均卷积滤波器。但是,由于空间域中的卷积等效于频域中的乘法,我们只需要执行正向傅立叶变换,然后将滤波器与幅度图像相乘,最后执行逆傅立叶变换。我们注意到,小尺寸的卷积滤波器将对应于频域中的大圆圈。乘法通过 -composite 使用 -compose 乘法设置进行。因此,让我们尝试使用两种尺寸的圆形滤波器来执行此操作,一种直径为 40(半径为 20),另一种直径为 28(半径为 14)。

  magick -size 128x128 xc:black -fill white \
          -draw "circle 64,64 44,64" circle_r20.png
  magick lena.png -fft \
       \( -clone 0 circle_r20.png -compose multiply -composite \) \
       \( +clone -evaluate log 10000 -write lena_blur_r20_spec.png +delete \) \
       -swap 0 +delete -ift lena_blur_r20.png

  magick -size 128x128 xc:black -fill white \
          -draw "circle 64,64 50,64" circle_r14.png
  magick lena.png -fft \
       \( -clone 0 circle_r14.png -compose multiply -composite \) \
       \( +clone -evaluate log 10000 -write lena_blur_r14_spec.png +delete \) \
       -swap 0 +delete -ift lena_blur_r14.png
[IM Output] ==> [IM Output] x [IM Output] ==> [IM Output] ==> [IM Output]
[IM Output] ==> [IM Output] ==> [IM Output]
因此,我们看到使用较小直径滤波器的图像产生了更多的模糊。我们还注意到结果图像中边缘附近的“振铃”或“波纹”效应。这是因为圆的傅立叶变换,正如我们之前看到的,是一个 sinc 函数,随着它从中心向外扩展,其振荡逐渐减弱。然而,这里 sinc 函数和振荡是在空间域中而不是在频域中,正如我们之前所展示的那样。那么我们该怎么办?最简单的方法是使用各种 窗函数 对圆的边缘进行锥化。或者,可以使用诸如高斯形状之类的滤波器,该滤波器本身就是定义的锥化。所以让我们执行后者,并使用两个高斯模糊圆圈来消除大部分严重的“振铃”效应。

  magick circle_r20.png -blur 0x4 -auto-level gaussian_r20.png
  magick lena.png -fft \
       \( -clone 0 gaussian_r20.png -compose multiply -composite \) \
       \( +clone -evaluate log 10000 -write lena_gblur_r20_spec.png +delete \) \
       -swap 0 +delete -ift lena_gblur_r20.png

  magick circle_r14.png -blur 0x4 -auto-level gaussian_r14.png
  magick lena.png -fft \
       \( -clone 0 gaussian_r14.png -compose multiply -composite \) \
       \( +clone -evaluate log 10000 -write lena_gblur_r14_spec.png +delete \) \
       -swap 0 +delete -ift lena_gblur_r14.png
[IM Output] ==> [IM Output] x [IM Output] ==> [IM Output] ==> [IM Output]
[IM Output] ==> [IM Output] ==> [IM Output]
这当然好多了。理想的低通滤波器不是完全模糊圆圈,而是实际上使用适当的 sigma 高斯曲线而不是 半径。当然,在这个例子中,我们最终模糊了,为了模糊!但是与 FFT 幅度图像相乘的模糊模式是固定的,实际上可以从预生成的缓存中检索。此外,相乘的图像不需要与原始图像的大小相同,可以使用更小的图像。因此,以上方法对于大型图像来说可能快得多,尤其是在处理大量图像时。更重要的是,对于大型强模糊,频域图像很小,并且只进行一次乘法,而不是必须对原始图像中每个像素的所有像素进行平均。对于小型模糊,您可能更适合使用更直接的卷积模糊。

检测图像中的边缘 - 高通滤波

在空间域中,从图像中提取边缘的高通滤波器通常实现为具有正负权重的卷积,使得它们加起来为零。在频域中,事情要简单得多。这里的高通滤波器只是低通滤波器的反转版本。也就是说,低通滤波器明亮的地方,高通滤波器黑暗,反之亦然。因此,在 ImageMagick 中,我们只需要对低通滤波器图像进行 -negate 操作。因此,让我们使用圆形图像对 Lena 图像应用高通滤波器。然后再次使用纯粹的高斯曲线。

  magick circle_r14.png -negate circle_r14i.png
  magick lena.png -fft \
       \( -clone 0 circle_r14i.png -compose multiply -composite \) \
       \( +clone -evaluate log 10000 -write lena_edge_r14_spec.png +delete \) \
       -delete 0 +swap -ift -normalize lena_edge_r14.png

  magick -size 128x128 xc: -draw "point 64,64" -blur 0x14 \
          -auto-level   gaussian_s14i.png
  magick lena.png -fft \
       \( -clone 0 gaussian_s14i.png -compose multiply -composite \) \
       \( +clone -evaluate log 10000 -write lena_edge_s14_spec.png +delete \) \
       -delete 0 +swap -ift -normalize lena_edge_s14.png
[IM Output] ==> [IM Output] x [IM Output] ==> [IM Output] ==> [IM Output]
[IM Output] ==> [IM Output] ==> [IM Output]
仔细检查这两个结果,我们看到简单的圆圈不如高斯曲线好,因为它有“振铃”伪影,而且不太锐利。

锐化图像 - 高增益滤波

锐化图像的最简单方法是对其进行高通滤波(不进行归一化拉伸),然后将其与原始图像混合。

  magick lena.png -fft \
       \( -size 128x128 xc: -draw "point 64,64" -blur 0x14 -auto-level \
          -clone 0 -compose multiply -composite \) \
       -delete 0 +swap -ift \
       lena.png -compose blend -set option:compose:args 100x100 -composite \
       lena_sharp14.png
[IM Output] ==> [IM Output]
这里,高通滤波是在频域中完成的,结果转换回空间域,在那里它与原始图像混合,以增强图像的边缘。

去除噪声 - 陷波滤波

许多噪声图像包含某种模式噪声。这种噪声很容易在频域中去除,因为模式显示为几个点或线的模式。回想一下,一个简单的正弦波是一个重复的模式,并且在频谱中仅显示为 3 个点。为了去除这种噪声,人们只需要(但不幸的是)手动屏蔽(或陷波)幅度图像中的点或线。我们通过转换为频域、创建频谱的灰度版本、屏蔽点或线、对其进行阈值处理、将二值掩模图像与幅度图像相乘,然后转换回空间域来做到这一点。让我们在 小丑图像 上尝试一下,该图像包含对角条纹状抖动模式。首先,我们将小丑图像转换为以生成其幅度和相位图像。

  magick clown_orig.jpg -fft \
          \( +clone  -write clown_phase.png +delete \) +delete \
          -write clown_magnitude.png  -colorspace gray \
          -auto-level -evaluate log 100000  clown_spectrum.png
[IM Output]
原始图像
==> [IM Output]
频谱
[IM Output]
相位
我们看到频谱包含四个明亮的星形点,每个象限一个。这些不寻常的点表示我们想要消除的图像中的模式。图像中间的明亮点和线无关紧要,因为它们代表直流分量(平均图像颜色)以及来自图像边缘的影响,不应该修改。请注意,在生成频谱图像时,我强制生成的图像为纯灰度图像。这样我就可以将图像加载到编辑器中,并使用任何非灰色颜色(例如红色)来屏蔽掉这 4 个星形图案的区域。编辑完成后,我可以通过提取未编辑版本的对差图像来提取我着色的区域。像这样...

  magick clown_spectrum_edited.png clown_spectrum.png \
          -compose difference -composite \
          -threshold 0 -negate clown_spectrum_mask.png
[IM Output] ==> [IM Output]
现在,我们只需将掩模与幅度相乘,并将结果与原始相位图像一起使用,以转换回空间域。我们将原始图像与其并排显示以进行比较

  magick clown_magnitude.png clown_spectrum_mask.png \
          -compose multiply -composite \
          clown_phase.png -ift clown_filtered.png
[IM Output] ==> [IM Output]
非常好的结果。但我们还可以做得更好。正如您在前面的示例中所看到的,简单的“圆圈”对 FFT 图像并不特别友好,因此让我们稍微模糊一下掩模...

  magick clown_spectrum_mask.png \
          -blur 0x5 -level 50x100%  clown_mask_blurred.png
[IM Output]
并过滤小丑,这次在内存中重新生成 FFT 图像。

  magick clown_orig.jpg -fft \
          \( -clone 0 clown_mask_blurred.png -compose multiply -composite \) \
          -swap 0 +delete -ift clown_filtered_2.png
[IM Output]
一个惊人的结果!并且可以通过调整该掩模以更好地适合“星形”形状来进一步改进。
我们甚至可以获取原始图像和结果之间的差异,以创建噪声去除区域的图像。

  magick clown_orig.jpg clown_filtered_2.png -compose difference \
          -composite -normalize clown_noise.png
[IM Output]
让我们在另一个示例上尝试一下。这次在 RoboRealm 网站上找到的“树枝”图像上尝试一下,该图像包含水平和垂直条纹的不规则图案。我们再次提取灰度频谱图像,就像之前一样。

  magick twigs.jpg -fft +delete -colorspace gray \
          -auto-level -evaluate log 100000 twigs_spectrum.png
[IM Output] ==> [IM Output]
在这种情况下,由于图像中的噪声是水平和垂直方向的,因此它显示为沿中心线但不在图像实际中心的厚水平和垂直带。我们再次使用图像编辑器屏蔽掉这些部分,这次使用“蓝色”颜色(使用哪种颜色并不重要)...

  magick twigs_spectrum_edited.png twigs_spectrum.png \
          -compose difference -composite \
          -threshold 0 -negate twigs_spectrum_mask.png
[IM Output] ==> [IM Output]
现在,我们再次将掩模与 FFT 幅度图像相乘,并重建图像。

  magick twigs.jpg -fft \
          \( -clone 0 twigs_spectrum_mask.png -compose multiply -composite \) \
          -swap 0 +delete  -ift twigs_filtered.png
[IM Output] ==> [IM Output]
我们可以获取原始图像和结果之间的差异,以创建噪声去除区域的图像。

  magick twigs.jpg twigs_filtered.png -compose difference -composite \
          -normalize twigs_noise.png
[IM Output]
对掩模添加一点模糊,可以进一步改善结果。作为练习,尝试从图像中删除字符串。提示,请记住实际图像中一条线的效应如何在 FFT 中旋转 90 度。如果您做错了,您可能会删除树枝而不是字符串。

高级应用

使用傅立叶变换的一些更高级的应用包括:1)运动模糊和散焦图像的去卷积(去模糊)和 2)归一化互相关,以找到小图像在更大图像中最佳匹配的位置。FFT 乘法和除法(去卷积)的示例已移至 子目录,因为它正在等待更正式定义的图像处理运算符。