ImageMagick 示例 --
傅里叶乘/除法

索引
ImageMagick 示例前言和索引
傅里叶变换
DIY FFT 数学入门
FFT 乘法
FFT 除法


DIY FFT 数学入门

下面是一些使用两种不同风格的快速傅里叶变换图像进行乘法和除法的技术的示例。

基本上有两种方法可以进行数学运算。使用 FX 数学,以及使用合成数学……
FX 数学
使用 FX,DIY 运算符 直接应用公式。但由于它是被解释执行的,因此非常慢。但是,所有公式都包含在一个表达式中,避免了需要中间图像。对于 IM 的非 HDRI 版本,这将减少量子舍入效应。

合成数学
这利用图像合成来执行数学运算,这比“FX 数学”快得多。但是您一次只能执行一个数学运算,这意味着每次运算后都必须将图像保存到另一个内存中的图像中。对于非 HDRI 版本,这将在运算之间产生额外的“舍入效应”。

上面提到的另一个方面是 HDRI 的使用。

ImageMagick 的 HDRI 版本 将颜色值保存为浮点数,而不是 QuantumRange 缩放的整数(参见 质量)。因此,在生成 FFT 图像或将中间结果保存到新图像时,不会导致“舍入效应”,尤其是在涉及较小值(FFT 图像中很常见)时。

此外,由于实部/虚部 FFT 图像对需要使用负数,因此在处理此类 FFT 图像时,必须使用 HDRI 版本的 image magick。

然而,负数对于幅度/相位 FFT 图像来说不是问题,因此您可以在不使用 HDRI 的情况下处理此类图像,尽管您仍然会在每个图像处理步骤之间产生强烈的“舍入效应”。

因此,无论您使用哪种类型的 FFT 图像,最好在 HDRI 版本的 ImageMagick 上使用快速傅里叶变换。HDRI 和非 HDRI 版本的 ImageMagick 使用完全相同的命令。

将中间 HDRI 图像保存到磁盘文件时,您需要使用极少数的浮点图像文件格式之一。这些格式包括 NetPBM PFM 文件格式、TIFFMIFF 文件格式,并使用特殊设置“-define quantum:format=floating-point”。


这是我们将要使用的卷积核图像……

  # motion blur kernel
  #magick -size 128x128 xc:black -fill white -draw 'line 60,64 68,64' \
  #        -alpha off convolve_kernel.png

  # disk blur kernel (Fred Wienhaus's version)
  magick -size 128x128 xc:black -fill white -draw 'circle 64,64 54,64' \
          -alpha off convolve_kernel.png

[IM Output]

卷积核必须被滚动,以便像素 0,0 包含卷积的中心。在这种情况下,需要立即在读取内核图像后应用“-roll -64-64”。

内核图像还需要除以其平均值,以保持其被 FFT 乘或除的图像的强度。这是 FFT 乘法和除法的一个主要复杂因素。

由于非 HDRI 图像无法以这种方式归一化,因此通常在应用 FFT 转换后以及乘法之前进行。

对于 IM 的 HDRI 版本,您可以在任何时间进行此操作。


FFT 乘法  ( )

幅度/相位图像的 FFT 乘法,使用 IM Q16

    R = A  B       ( FFT Multiply )

    Rm = Am × Bm
    Rp = mod( Ap + Bp +0.5, 1.0)

    mean = the DC value (center pixel) of the magnitude image
使用 FX 数学,
(注意值 v.p{64x64} 是 DC,或卷积核的平均值)

  # non-HDRI
  magick convolve_kernel.png -roll -64-64 -fft \
          \( cameraman_sm.png -fft \) \
          \
          \( -clone 0,2 -fx 'u*v / p{64,64} ' \) \
          \( -clone 1,3 -fx 'mod(u + v + 0.5, 1.0)' \) \
          \
          -delete 0-3 -ift  cameraman_convolve_1.png
[IM Output] [IM Output] ==> [IM Output]

使用合成数学……

  # non-HDRI
  magick convolve_kernel.png -roll -64-64 -fft \
          \( -clone 0 -crop 1x1+64+64 +repage -scale 128x128 \
             -clone 0 -compose divide -composite \) -swap 0 +delete \
          \( cameraman_sm.png -fft \) \
          \
          \( -clone 0,2 -compose multiply -composite \) \
          \( -clone 1,3 -compose add -background gray50 -flatten \) \
           -delete 0-3 -ift  cameraman_convolve_2.png
[IM Output] [IM Output] ==> [IM Output]

请注意,“add”是模加合成,它用于将 50% 偏差的值相加,以生成 50% 偏差的值。

替代方案
用原始图像的平均值除

  # non-HDRI
  magick convolve_kernel.png \( -clone 0 -roll -64-64 -fft \) \
          \( -clone 0 -scale 1x1 -scale 128x128 \
             -clone 1 -compose divide -composite \) \
          -delete 0,1 +swap \
          \( cameraman_sm.png -fft \) \
          \
          \( -clone 0,2 -compose multiply -composite \) \
          \( -clone 1,3 -compose add -background gray50 -flatten \) \
           -delete 0-3 -ift  cameraman_convolve_2b.png
[IM Output] [IM Output] ==> [IM Output]

替代方案……
请注意,DC 值除以 DC 值 ==> 1.0

由于 DC 值是幅度谱中最大的值,为什么不直接归一化!例如:-auto-level

这只能对幅度图像进行,并且不适用于实部/虚部对,因为它们必须以相同的量进行拉伸。


  # non-HDRI
  magick convolve_kernel.png -roll -64-64 -fft \
          \( -clone 0 -auto-level \) -swap 0 +delete \
          \( cameraman_sm.png -fft \) \
          \
          \( -clone 0,2 -compose multiply -composite \) \
          \( -clone 1,3 -compose add -background gray50 -flatten \) \
          \
          -delete 0-3 -ift  cameraman_convolve_2c.png
[IM Output] [IM Output] ==> [IM Output]

实部/虚部图像的 FFT 乘法,使用 IM HDRI

    R = A  B       ( FFT Multiply )

    Rr = Ar×Br - Ai×Bi
    Ri = Ar×Bi + Ai×Br

    mean = the DC value (center pixel) of the real image
使用 FX……
涉及 5 幅图像,第 5 幅图像为缩放的平均值 - FX 进行除法

  # HDRI
  magick convolve_kernel.png -roll -64-64 \( +clone +fft \) \
          \( cameraman_sm.png +fft \) +matte \
          \( -clone 0 -scale 1x1 \) -delete 0 \
          \
          \( -clone 0-4 -fx '( u[0]*u[2] - u[1]*u[3] ) / u[4].p{0,0}' \) \
          \( -clone 0-4 -fx '( u[0]*u[3] + u[1]*u[2] ) / u[4].p{0,0}' \) \
          -delete 0-4 +ift  cameraman_convolve_3.png
[IM Output] [IM Output] ==> [IM Output]

使用 FX……
使用 DC 值作为平均值(没有第 5 幅图像)

  # HDRI
  magick convolve_kernel.png -roll -64-64 +fft \
          \( cameraman_sm.png +fft \) \
          \
          \( -clone 0-3 -fx '( u[0]*u[2] - u[1]*u[3] ) / u[0].p{64,64}' \) \
          \( -clone 0-3 -fx '( u[0]*u[3] + u[1]*u[2] ) / u[0].p{64,64}' \) \
          -delete 0-3 +ift  cameraman_convolve_3b.png
[IM Output] [IM Output] ==> [IM Output]

使用合成数学……
使用缩放图像的平均值!

  # HDRI
  magick convolve_kernel.png -roll -64-64 \
          \( -clone 0 -scale 1x1 -scale 128x128 \) \
          \( -clone 0 +fft \) \
          \( -clone 1,2 -compose divide -composite \) \
          \( -clone 1,3 -compose divide -composite \) \
          -delete 0--3 \
          \
          \( cameraman_sm.png  +fft \) \
          \
          \( -clone 0,2 -compose multiply -composite \) \
          \( -clone 1,3 -compose multiply -composite \) \
          \( -clone 0,3 -compose multiply -composite \) \
          \( -clone 1,2 -compose multiply -composite \) \
          \
          \( -clone 4,5 +swap +matte -compose minus -composite \) \
          \( -clone 6,7 -compose plus -composite \) \
          \
          -delete 0--3 +ift  cameraman_convolve_4.png
[IM Output] [IM Output] ==> [IM Output]

优化的合成数学……
来自图像缩放的平均值

  # HDRI
  magick convolve_kernel.png -roll -64-64 \
          \( -clone 0 -scale 1x1 -scale 128x128 \) \
          \( -clone 0 +fft    null: +insert    -clone 1 +insert \
             -compose divide -layers composite \) -delete 0,1  \
          \
          \( cameraman_sm.png +fft \) \
          \
          \( -clone 0,1,0,1 null: -clone 2,3,3,2 \
                -compose multiply -layers composite \) \
          \( -clone 4,5 +swap +matte -compose minus -composite \) \
          \( -clone 6,7 -compose plus -composite \) \
          \
          -delete 0--3 +ift  cameraman_convolve_4b.png
[IM Output] [IM Output] ==> [IM Output]

替代方案……
使用 DC 值作为平均值……

  # HDRI
  magick convolve_kernel.png -roll -64-64 +fft \
          \( -clone 0 -crop 1x1+64+64 +repage -scale 128x128 \) \
          null: +insert +insert   -compose divide -layers composite \
          \
          \( cameraman_sm.png +fft \) \
          \
          \( -clone 0,1,0,1 null: -clone 2,3,3,2 \
                -compose multiply -layers composite \) \
          \( -clone 4,5 +swap +matte -compose minus -composite \) \
          \( -clone 6,7 -compose plus -composite \) \
          \
          -delete 0--3 +ift  cameraman_convolve_4c.png
[IM Output] [IM Output] ==> [IM Output]

替代方案……
在 FFT 乘法之后应用 DC 值平均值……

  # HDRI
  magick convolve_kernel.png -roll -64-64 +fft \
          \( cameraman_sm.png  +fft \) \
          \
          \( -clone 0,1,0,1 null: -clone 2,3,3,2 \
                -compose multiply -layers composite \) \
          \( -clone 4,5 +swap -compose minus -composite \) \
          \( -clone 6,7 -compose plus -composite \) \
          \
          \( -clone 0 -crop 1x1+64+64 +repage -scale 128x128 \
             null: -clone -2,-1  -compose divide -layers composite \) \
          \
          -delete 0--3 +ift  cameraman_convolve_4d.png
[IM Output] [IM Output] ==> [IM Output]


FFT 除法  ( )

这里我们使用除法来去除或反卷积(为了便于理解)添加到上述图像中的模糊。它基本上完全相同,只是“归一化”的卷积核从主图像中除去了。

每个示例都使用使用与上述相同技术生成的图像。

幅度/相位的 FFT 除法,使用 IM Q16

    R = B ø A       ( FFT Divide )

    Rm = Bm / Am
    Rp = mod( -Ap + Bp +1.5, 1.0)

    mean = the DC value (center pixel) of the magnitude image
注意:将 QuantumScale 的最小值添加到分母以避免完全除以零。这稍后也用于去除图像中的噪声。

使用 FX 数学……

  # non-HDRI
  noise=QuantumScale
  magick convolve_kernel.png -roll -64-64 -fft \
          \( cameraman_convolve_1.png -fft \) \
          \
          \( -clone 0,2 -fx "v/(u/p{64,64}+$noise)" \) \
          \( -clone 1,3 -fx 'mod( -u + v + 1.5, 1.0)' \) \
          \
          -delete 0--3 -ift cameraman_deconvolve_1.png
[IM Output] [IM Output] ==> [IM Output]

  echo -n "Peak = "
  magick compare -metric PAE cameraman_sm.png cameraman_deconvolve_1.png null:
  echo -n "Avg = "
  magick compare -metric RMSE cameraman_sm.png cameraman_deconvolve_1.png null:
[IM Text]

使用 IM 的 HDRI 版本进行往返 MP 卷积 FFT(FX 数学)
[IM Output] ==> [IM Output] ==> [IM Output]
[IM Text]

使用合成数学……

  # non-HDRI
  noise=1
  magick convolve_kernel.png -roll -64-64 -fft \
           \( -clone 0 -crop 1x1+64+64 +repage -scale 128x128 \
              -clone 0 -compose divide -composite \) -swap 0 +delete \
          \( cameraman_convolve_2.png -fft \) \
          \
          \( -clone  0  -evaluate add $noise \
             -clone  2  -compose divide -composite \) \
          \( -clone 1,3 -compose subtract -background gray50 -flatten \) \
          \
          -delete 0--3 -ift cameraman_deconvolve_2.png
[IM Output] [IM Output] ==> [IM Output]

  echo -n "Peak = "
  magick compare -metric PAE cameraman_sm.png cameraman_deconvolve_2.png null:
  echo -n "Avg = "
  magick compare -metric RMSE cameraman_sm.png cameraman_deconvolve_2.png null:
[IM Text]

使用 IM 的 HDRI 版本进行往返 MP 卷积 FFT(合成数学)
[IM Output] ==> [IM Output] ==> [IM Output]
[IM Text]

实部/虚部的 FFT 除法,使用 IM 的 HDRI 版本

    R = B ø A       ( FFT Divide )

    Denom = Ar×Ar + Ai×Ai + noise
    Rr = ( Ar×Br + Ai×Bi ) / Denom
    Ri = ( Ar×Bi - Ai×Br ) / Denom

    mean = the DC value (center pixel) of the real image
使用 FX……

  # HDRI
  #noise=QuantumScale
  noise=Epsilon
  magick convolve_kernel.png -roll -64-64 +fft \
          \( -clone 0 -fx "u/p{64,64}" \) \( -clone 0,1 -fx "v/u.p{64,64}" \) \
          -delete 0,1 \
          \( cameraman_convolve_3.png +fft \) \
          \
          \( -clone 0-3 -fx "u[0]*u[0] + u[1]*u[1] + $noise" \) \
          \( -clone 0-3 -fx 'u[0]*u[2] + u[1]*u[3]' \) \
          \( -clone 0-3 -fx 'u[0]*u[3] - u[1]*u[2]' \) \
          \( -clone 4,5 -fx 'u==0?0:v/u' \) \
          \( -clone 4,6 -fx 'u==0?0:v/u' \) \
          \
          -delete 0--3 +ift cameraman_deconvolve_3.png
[IM Output] [IM Output] ==> [IM Output]

  echo -n "Peak = "
  magick compare -metric PAE cameraman_sm.png cameraman_deconvolve_3.png null:
  echo -n "Avg = "
  magick compare -metric RMSE cameraman_sm.png cameraman_deconvolve_3.png null:
[IM Text]

我在上面使用的 -fx 除法不如合成除法好。可能是由于涉及额外的 FX 缩放。

  # HDRI
  #noise=QuantumScale
  noise=Epsilon
  magick convolve_kernel.png -roll -64-64 +fft \
          \( -clone 0 -fx "u/p{64,64}" \) \( -clone 0,1 -fx "v/u.p{64,64}" \) \
          -delete 0,1 \
          \( cameraman_convolve_3.png +fft \) \
          \
          \( -clone 0-3 -fx "u[0]*u[0] + u[1]*u[1] + $noise" \) \
          \( -clone 0-3 -fx 'u[0]*u[2] + u[1]*u[3]' \) \
          \( -clone 0-3 -fx 'u[0]*u[3] - u[1]*u[2]' \) \
          \( -clone 4,5 -compose divide -composite \) \
          \( -clone 4,6 -compose divide -composite \) \
          \
          -delete 0--3 +ift cameraman_deconvolve_3b.png
[IM Output] [IM Output] ==> [IM Output]

  echo -n "Peak = "
  magick compare -metric PAE cameraman_sm.png cameraman_deconvolve_3b.png null:
  echo -n "Avg = "
  magick compare -metric RMSE cameraman_sm.png cameraman_deconvolve_3b.png null:
[IM Text]

使用合成数学(详细)……

  # HDRI
  noise=0.00000001  # Epsilon
  magick \( convolve_kernel.png -roll -64-64 +fft \
             \( -clone 0 -crop 1x1+64+64 +repage -scale 128x128 \) \
             \( -clone 2,0 -compose divide -composite \) \
             \( -clone 2,1 -compose divide -composite \) \
             -delete 0-2 \) \
          \( cameraman_convolve_4.png +fft \) \
          \
          \( -clone 0,0 -compose multiply -composite \) \
          \( -clone 1,1 -compose multiply -composite \) \
          \( -clone 0,2 -compose multiply -composite \) \
          \( -clone 1,3 -compose multiply -composite \) \
          \( -clone 0,3 -compose multiply -composite \) \
          \( -clone 1,2 -compose multiply -composite \) \
          \
          \( -clone 4,5 -compose plus  -composite -evaluate add $noise \) \
          \( -clone 6,7 -compose plus -composite \) \
          \( -clone 8,9 +swap -compose minus -composite \) \
          \
          \( -clone 10,11 -compose divide -composite \) \
          \( -clone 10,12 -compose divide -composite \) \
          \
          -delete 0--3 +ift cameraman_deconvolve_4.png
[IM Output] [IM Output] ==> [IM Output]


  echo -n "Peak = "
  magick compare -metric PAE cameraman_sm.png cameraman_deconvolve_4.png null:
  echo -n "Avg = "
  magick compare -metric RMSE cameraman_sm.png cameraman_deconvolve_4.png null:
[IM Text]

优化的合成数学……

  # HDRI
  noise=0.00000001  # Epsilon
  magick \( convolve_kernel.png -roll -64-64 +fft \
             \( -clone 0 -gravity center -extent 1x1 -scale 128x128 \) \
             null: +insert +insert -compose divide -layers composite \) \
          \( cameraman_convolve_4.png +fft \) \
          \
          \( -clone 0,1,0,1,0,1 null: -clone 0,1,2,3,3,2 \
                -compose multiply -layers composite \) \
          \( -clone 4,5 -compose plus  -composite -evaluate add $noise \) \
          \( -clone 6,7 -compose plus  -composite \) \
          \( -clone 9,8 -compose minus -composite \) \
          \( -clone 10 null: -clone 11,12 \
                -compose divide -layers composite \) \
          \
          -delete 0--3 +ift cameraman_deconvolve_4b.png
[IM Output] [IM Output] ==> [IM Output]

  echo -n "Peak = "
  magick compare -metric PAE cameraman_sm.png cameraman_deconvolve_4b.png null:
  echo -n "Avg = "
  magick compare -metric RMSE cameraman_sm.png cameraman_deconvolve_4b.png null:
[IM Text]


更新:上述中使用颜色“gray50”应更改为“gray(50%)”以生成更准确的 50% 灰色值。之前的“命名”颜色实际上只是一个 8 位颜色,因此不太准确。

此外,在新的色彩空间处理中,gray50 位于 sRGB 色彩空间中,而 gray(50%) 位于线性色彩空间中,这应该用于上述计算。

还需要检查这些示例在使用线性灰度色彩空间方面的使用情况。