ImageMagick 示例 --
图像卷积

索引
ImageMagick 示例前言和索引
卷积介绍
模糊图像(低通滤波)
边缘检测卷积(高通滤波)
方向卷积(斜率,罗盘滤波)
相关
邻居计数
卷积使用像素的局部“邻域”来修改图像。它通过合并和平均每个像素周围的所有颜色值来做到这一点,以模糊图像,突出显示边缘和边界,以及锐化图像。卷积变体“相关”也用于扫描和搜索特定模式,生成一个表示图像匹配程度的图像。

卷积介绍

Convolve”和与其密切相关的“Correlate”方法,在很多方面与形态学非常相似。实际上,它们的工作方式几乎完全相同,在每个位置匹配一个邻域“内核”,使它们只是另一种特殊的形态学“方法”。实际上,它们也使用与基本内核用户定义内核中定义的相同代码,甚至相同的内核定义。有关专门为该运算符设计的使用内核(有很多),我建议您参考模糊内核边缘检测内核。最重要的内核是“Gaussian”内核。但是,卷积比形态学要古老得多,它会生成更多灰度梯度效果,而不是形态学通常生成的二进制形状研究效果。这就是为什么它通常被认为是与形态学截然不同或独立的运算,并且它是图像处理的核心运算。基本上,卷积或相关执行对指定邻域内所有像素的“加权平均”。也就是说,它将每个附近像素的值乘以内核中给出的量,然后将所有这些值加在一起以产生最终结果。因此,最终图像中的每个像素通常将包含周围源图像中所有其他像素的至少一小部分。从另一个角度来看,图像中每个像素的颜色将被添加到(模糊)或从(锐化/边缘检测)其所有附近邻居的颜色中,如使用的内核所定义。‘convolve’ 和 ‘correlate’ 是相同的操作,只是在非常细微但重要的方面有所不同,对于我们现在将要查看的示例和控件,您可以将它们视为基本上相同的事物。稍后(参见卷积与相关),我们将研究这两个运算符在实际上的差异,以及为什么它们以如此细微的方式有所不同。但在大多数情况下,它们是相同的方法。

卷积  ( )

如上所述,‘Convolve’ 方法通过根据内核中的浮点值对局部邻域中的每个像素进行加权来工作。然后简单地将加权值加在一起以生成结果图像中新的替换像素。例如,让我们使用一个非常小的用户定义的卷积核来对单个像素进行卷积。我还设置了特殊的显示内核设置,这样您就可以看到正在定义和使用的内核的详细信息(显示的图像已放大)。

  magick xc: -bordercolor black -border 5x5 pixel.gif
  magick pixel.gif -define morphology:showKernel=1 \
         -morphology Convolve '3x3: 0.0, 0.5, 0.0
                                    0.5, 1.0, 0.5
                                    0.0, 0.5, 0.0' pixel_spread.gif
[IM Text]
[IM Output] [IM Output] ==> [IM Output]
如您所见,图像中的单个像素现在已扩展以生成其周围的 50% 灰色像素。也就是说,当内核的“原点”(在本例中是其中心)位于原始图像中的单个像素旁边时,只有该单个像素具有非零值。然后将该像素值乘以内核的“0.5”值,并将得到的“半亮”像素添加到结果图像中。类似地,当内核的原点精确地位于原始像素上时,它将获得“1.0”的值,并复制原始像素,而周围没有其他值(黑色)在邻域中添加任何成分到结果中。请注意,任何内核值为“0.0”将不会参与最终计算。零值实际上不属于“邻域”,就像形态学内核中的任何“Nan”值都不会参与一样。因此,该内核由 5 个元素的邻域组成。在很多方面,‘Convolve’ 方法与形态学‘Dilate’ 方法非常相似,但是 ‘Dilate’ 仅将内核视为一种类型的位图掩码,在邻域内定位最大值。另一方面,‘Convolve’ 是邻域内所有值的加权和,因此每个内核元素的值都在最终结果中起作用。卷积操作的语法是…

  -morphology Convolve {convolution_kernel}
但您也可以使用更旧、更直接的运算符…

  -convolve {convolution_kernel}
在 IM v6.5.9 之前,较旧的 "-convolve" 不理解形态学内核定义。它只接受用户定义内核的“旧样式”,它仅包含一串逗号分隔的值以生成一些奇数大小的正方形内核。现在它将接受“新样式”的卷积内核定义。

但是它仍然仅限于“奇数大小”的正方形内核。并且将保持这种状态,直到它开始使用新的“形态学”卷积方法。

较旧的 "-convolve" 运算符与新的形态学 ‘Convolve’ 方法并不完全相同。以下是两种操作的差异列表…

  • 旧运算符实现为 相关,而不是真正的卷积。这意味着内核不会以反射形式覆盖在源图像上。有关此操作对结果的影响,请参见卷积与相关

  • 它只接受奇数大小的正方形内核。形态学内核允许任何矩形数组,数组中的任何点都可以声明为原点。

  • 旧运算符将始终归一化内核,而不会对内核内核缩放进行任何用户控制。新的运算符不会自动归一化,您需要请求它。但是大多数生成的内核都为您预先归一化。

  • 您不能使用任何形式的与单位核混合,尽管输出输出偏差是正常执行的。
  • 但是,如果主机计算机具有此类功能,它将使用快速“GPU”代码。形态学还没有启用此功能。

  • 目前,其他与卷积相关的运算符,如 "-gaussian_blur","-blur","-sharpen","-unsharpen",使用旧版本的运算符。

  • 默认情况下,旧命令将只针对颜色通道(由 "-channel" 设置定义)进行卷积。如果使用 "-channel RGBA" 设置进行卷积,它还会根据 alpha 通道对内核值进行加权,以确保与透明度相关的正确模糊。

    形态学 ‘convolve’ 方法默认情况下会自动处理颜色通道的透明度加权。也就是说,使用它进行图像模糊默认情况下会将透明颜色视为透明,从而避免 模糊透明度错误

    但是,如果用户修改了默认 "-channel" 设置(不包含特殊的 ‘Sync’ 标志),那么它将处理卷积作为纯通道的灰度运算符。

    有关更多信息,请参见 "-channel" 设置文档,或查看 图像通道数学,它以相同的方式使用相同的标志。



最终,随着事物与新的形态学 ‘Convolve’ 方法合并,上述大多数差异将发生变化。

如果您想了解 ‘Convolve’ 的实际工作原理,我建议您还查看 EECE \ CS 253 图像处理,第 7 讲,空间卷积维基百科,卷积 文章中有一些关于卷积过程的不错的一维动画。

卷积核缩放

上面的示例对于像单个像素这样的几乎全黑图像效果很好,但是如果您将其应用于真实图像,您将遇到问题…


magick logo: -resize 50% -crop 80x80+150+60 +repage face.png
magick face.png \ -morphology Convolve '3x3: 0.0,0.5,0.0 0.5,1.0,0.5 0.0,0.5,0.0' \ face_spread.png
[IM Output] [IM Output] ==> [IM Output]
如您所见,结果图像非常亮(实际上亮了 3 倍),因为是原始图像。发生的情况是每个像素都被共享了 3 次。两侧的 4 × ‘0.5’,加上原始像素的完整副本。也就是说,内核中所有值的总和为 3,这使得结果图像亮了三倍!如果您返回并查看上面的‘showKernel’ 输出,您会看到它列出了该内核,其“卷积输出范围为 0 到 3”。这表明该内核通常会使图像亮度增加三倍。为了解决这个问题,您需要将内核中的所有值除以 3。也就是说,“0.5”的值实际上应该约为“0.1667”,而中心值“1.0”应该约为“0.3333”。这个过程被称为“内核归一化”。例如,这是手动“归一化”的结果和内核定义…

magick face.png -define morphology:showKernel=1 \
       -morphology Convolve \
       '3x3: 0.0,.1667,0.0  .1667,.3333,.1667  0.0,.1667,0.0' \
       face_spread_norm.png
[IM Text]
[IM Output] [IM Output] ==> [IM Output]
如您所见,您将获得人脸图像的轻微模糊版本,因为每个像素都被分散到其周围的每个相邻像素。
上面显示的“内核图像”(使用特殊的 内核到图像脚本 生成)也显示了生成的归一化内核。如您所见,内核本身现在非常暗,因为其所有值都很暗,尽管它们加起来的值为“1.0”。

从现在开始,显示的所有卷积内核图像都将进行调整,使其最大值设置为白色,否则您通常只会看到一个黑暗且基本无用的“内核图像”。

自己归一化内核并不令人愉快,而且正如您所见,它使生成的内核定义更难理解。因此,提供了替代方法。从 IM v6.5.9-2 开始,特殊的专家选项“-define convolve:scale={kernel_scale}”允许您为内核指定一个全局缩放因子,从而调整整体结果的亮度。

magick face.png -define convolve:scale=0.33333 \
       -morphology Convolve '3x3: 0.0,0.5,0.0  0.5,1.0,0.5  0.0,0.5,0.0' \
       face_spread_scale.png
[IM Output]
实际上,这样做会调整内核结果的整体强度。正如您将在后面的示例中看到的那样,您可能希望使卷积结果或多或少强大。此“kernel_scale”因子允许您做到这一点。

内核归一化(自动缩放)

您无需计算缩放因子(如上所述),只需让 IM 通过提供特殊的“!”归一化标志来在内部计算此“归一化缩放因子”。

magick face.png -define convolve:scale=\! \
       -morphology Convolve  '3x3: 0,1,0  1,2,1  0,1,0' \
       face_spread_normalize.png
[IM Output]
!”字符有时也用于各种 UNIX 命令行 shell 的特殊用途。因此,您可能需要使用反斜杠对其进行转义,即使在引号中也是如此。建议谨慎。
请注意,由于内核现在已归一化,我可以使用整数以更简单的方式定义它。归一化内核将与以前的“缩放”内核相同。通常,您始终希望归一化内核,并且出于此原因,更简单的“-convolve”变体将自动执行此归一化。您可以让 IM 归一化内核,然后通过给定量再次对其进行缩放以调整其输出范围。为了使这更容易,您可以将缩放因子指定为百分比。例如,这里我归一化了内核,但随后将值重新缩放为计算大小的 50%,从而产生更暗的结果。

  magick face.png -define convolve:scale=50%\! \
         -morphology Convolve  '3x3: 0,1,0  1,2,1  0,1,0' \
         face_spread_norm_half.png
[IM Output]
请注意,使用“!”的值实际上等同于使用“1!”甚至“100%!”。如果您想翻转内核中的正值和负值,您甚至可以使用负缩放因子。有关此示例,请参阅 “非锐化”使用模糊的图像。如果内核已以这种方式归一化,则 显示内核 输出将告诉您它已归一化。

归一化工作原理

实际的“内核归一化”工作方式是将所有内核值加在一起(包括任何可能的负值)。如果结果非零,则对所有值进行缩放,以使它们的组合值加起来等于 1(“1.0”)。请注意,如果您有负值,这实际上可能会创建一个值大于 1 的内核,通常在原点处。它特别发生在 非锐化 内核中。但是,重要的是,内核作为一个整体加起来等于“1.0”,因此最终图像不会因 卷积 操作而变暗或变亮。如果加法结果为零(“0.0”),则假设内核是特殊的 零和内核。在这种情况下,内核将被缩放以使所有正值等于“1.0”,同样,所有负值加起来将等于“-1.0”。这些内核在 边缘检测 技术中尤其普遍。 显示内核 输出也将指定它是零和,如果内核采用这种形式,即使它实际上不是一个归一化的零和内核,尽管这也可以从显示的其他数字中轻松看出。大多数数学确定的内核都是预先归一化的。这包括数学推导的内核:“Unity”、“Gaussian”、“LoG”、“DoG”、“Blur”、“Comet”。但是,离散常数内核未预先归一化,因此您必须使用 内核归一化设置(如上所述)来执行此操作。这包括内核:“Laplacian”、“Sobel”、“Roberts”、“Prewitt”、“Compass”、“Kirsch”、“FreiChen”。请注意,“FreiChen”内核具有专门为更具体目的而预先加权的子类型。FreiChen 内核不应归一化,而应按原样使用。

零和归一化

并非所有卷积内核都使用正值。您也可以获得使用正值和负值的混合内核,并且这些内核的值通常旨在加起来为零,以产生 零和内核。这些内核对于更高级的图像卷积非常重要,因为它们提供了 边缘检测锐化图像 的技术。正如我在上一节中提到的,通常的归一化标志“!”将适用于这些内核。但有时由于特殊情况,您希望确保内核保持“零和”。特殊的“^”归一化方法只是提供了一种在以下情况下确保内核为“零和”的方法...
  1. 如果用户的内核定义不够精确,无法确保零和。例如,您无法将“1/3”或任何其他 3 的分数因子指定为精确的浮点小数。
  2. 数学曲线被内核大小(半径)“裁剪”,因此它可能不再是零和。例如,这发生在基于无限响应曲线的“LoG”或“DoG”内核中。出于这个原因,IM 实际上在这些内核上内部使用这种特殊的归一化。
  3. 确保 相关“形状蒙版”是零和,以便在搜索中,IM 可以平等地寻找正负匹配。请参阅下面的 相关形状搜索
发生的事情是,它将内核的所有正值和负值作为单独的实体进行归一化。也就是说,所有负值将被缩放以加起来等于“-1.0”,所有正值将被缩放以加起来等于“+1.0”。结果是,内核作为一个整体,将保证加起来等于零。请注意,如果您将此归一化方法用于诸如“高斯”之类的全正内核,您仍然会获得一个正确归一化的内核。因此,这种形式的归一化仍然可以与 模糊内核 一起使用。但是,它不应用于直接定义的 锐化 甚至 非锐化 内核,因为这可能包含负值,但需要加起来等于 1(使用正常归一化方法)。

与单位核混合内核

内核缩放设置的完整语法是...
-define convolve:scale='{kernel_scale}[!^] [,{origin_addition}] [%]'
-set option:convolve:scale '{kernel_scale}[!^] [,{origin_addition}] [%%]'
请注意,使用“-set”时百分号要加倍。可选的归一化标志“!”或“^”将首先应用于用户定义的或内置的内核(如果需要)。之后,内核将根据“kernel_scale”因子进行缩放,从而增加或减少卷积对结果的有效“功率”。默认缩放因子为“1.0”。最后,内核的“原点”值将添加逗号后的数字。默认“origin_addition”为“0.0”。最后一步实际上是在先前生成的归一化和缩放内核中“添加”一个给定“缩放”的 Unity 内核。这会生成可以... 的内核。请注意,如果您给出了百分比(“%”)标志,则该百分比将应用于“kernel_scale”因子和“origin_addition”。当涉及分数时,这可以使比例更容易阅读和理解。内核缩放定义的示例用法...

  -define convolve:scale='!50%,100%'  -morphology Convolve Laplacian:2  
将生成请求的“Laplacian:2”内核...
0 -1 0
-1 4 -1
0 -1 0
将其归一化(“!”标志)
0 -0.25 0
-0.25 1 -0.25
0 -0.25 0
按 50% 缩放
0 -0.125 0
-0.125 0.5 -0.125
0 -0.125 0
添加一个 Unity 内核(将原点值增加 100%)
0 -0.125 0
-0.125 1.5 -0.125
0 -0.125 0
您现在可以使用“Laplacian:2”作为锐化内核进行卷积,但锐化强度仅为“50%”。请记住,在比例设置中给出的任何“%”标志都将使这两个值都成为百分比。如果不存在,则这两个值只是简单的直接乘数。例如,所有这些缩放选项都是等效的

     50,100%     50%,100    %50,100      .5,1      0.5,1.0   
这两个归一化标志也是如此。它们可以出现在卷积缩放设置中的任何位置,但它们始终会在任何其他缩放发生之前应用。

输出结果偏差控制

当您处理包含负值的内核时,结果图像中的一些像素应该被分配负值。这在 零和内核(见下文)中尤其如此。不幸的是,除非您拥有专门构建的 HDRI 版本的 ImageMagick 来保留生成的负值,否则任何负结果都会被裁剪为零(黑色)。您只会从卷积中获得正结果。它只是不能存储在普通的图像格式中,留下了一半的结果。您可以构建 HDRI 版本的 ImageMagick 来保留生成的负值,然后提取您想要的信息。或者,您可以使用负缩放因子将内核取反。例如,使用...
-define convolve:scale='-1'
但是,然后您只会获得负结果,而正结果会被裁剪。但是,通过使用 IM 设置“-bias”,您仍然可以保留正负结果。用于非 HDRI 版本的 IM 的设置是...

     -define convolve:scale=50%\!  -bias 50%
第一个设置将输出缩放到您通常获得的一半大小(在归一化之后),以便为正负结果留出空间。然后,它将在保存结果回到图像之前将 50% 的灰色添加到像素输出中。使用这些设置,任何“零”结果都将变为纯灰色,负结果比此更暗,而正结果比此更亮。黑色代表“-1.0”,白色代表“+1.0”。在下面的 相关形状搜索 示例中展示了一个这样做的示例。

模糊图像(低通滤波)

IM 示例的另一部分,特别是 模糊和锐化图像,实际上处理了该主题的实际方面。这里我们查看更多具体细节。但是,首先我们将描述基本内核以及如何直接使用它们而无需修改。稍后我们将研究修改模糊以生成其他效果的方法。

模糊内核

[IM Output]

Unity

这是一个特殊的内核,它实际上什么也不做。仅指定一个内核元素,因此每个像素都被替换为其自身,没有任何变化。例如,这里是一个无操作的 卷积...

  magick face.png -morphology Convolve Unity face_unity.png
[IM Output] [IM Output] ==> [IM Output]
从 IM v 6.6.9-4 开始,内核可以接受一个参数,作为内核特定的比例参数。这使您可以使用它来乘以图像的值,例如使图像更亮或更暗。

  magick face.png -morphology Convolve Unity:0.5 face_dimmed.png
[IM Output]
这可能看起来不太有用,但它可以用于生成 柔化模糊锐化 效果,或者在您可能无法使用 内核缩放内核恒等混合 的多内核序列中。相同的单元素内核也可以使用“Disk:0.5”生成,这也允许您在内核生成中指定一个额外的缩放参数。(例如:最后一个示例中的“Disk:0.5,0.5”)。类似的内核(用于 卷积)也可以由带有“sigma”值为“0.0”的“Gaussian”内核生成器生成。但是,它只能生成一个小的 3x3 内核,由一个中心值为“1.0”的值和周围 8 个值为“0.0”的值组成。 [IM Output]

使用形状内核的均值或平均滤波

虽然下面定义的大多数卷积内核通常在某种程度上涉及使用高斯曲线,但您仍然可以使用以前的 形态形状内核 来简单地对给定(较大)区域内的像素进行平均。当然,您需要对内核进行 归一化,以便实际生成平均值,而不仅仅是邻域的总和。例如,这里我使用一个较小的“Octagon”形状内核,来对每个像素周围圆形区域内找到的所有像素值进行平均。

  magick face.png -define convolve:scale=! \
         -morphology Convolve Octagon:2 face_mean.png
[IM Output] [IM Output] ==> [IM Output]
结果是,每个像素的值在定义的邻域中的所有 25 个像素上平均分配。也就是说,它相当于对给定形状的“均值”或“平均”滤波器。如果您想从该平均值中排除原始像素,只使用周围的像素,那么您可以使用“Ring”内核(只提供一个半径)。其他 形状内核 也可以以相同的方式使用,例如对“Diamond”、“Square”或大的“Disk”形状的像素值进行平均,以及您想要的任何尺寸。然而,虽然对形状区域的恒定平均会模糊图像,但它往往会在结果图像中产生不寻常的效果(特别是 混叠伪像)。更具体地说,使用“平坦”的平均内核往往会将尖锐的边缘转换为更厚的线性斜率,并在加厚的边缘处突然改变斜率。结果的厚度是内核“radius*2-1”。不同的边缘角度如何影响斜率厚度和斜率线性取决于“平坦”或平均内核的形状。

  magick -size 80x80 xc: -draw 'polygon 15,15 15,65 60,15' shape.png
  magick shape.png \
         -define convolve:scale=! -morphology Convolve Square:5 \
         shape_mean_square.png
  magick shape.png \
         -define convolve:scale=! -morphology Convolve Disk:5 \
         shape_mean_disk.png
[IM Output] ==> [IM Output] [IM Output]
请注意,上面所示的斜向模糊对于方形内核来说与圆形内核不同。生成方形“线性斜率”模糊的另一种方法是使用具有特定半径的非常大的 sigma。例如,上面的方形内核卷积也可以使用 -blur 5x65535 来实现。在形态学可用之前,Fred Wienhaus 在他的脚本中经常使用这种方法。 [IM Output]

高斯内核(2d 高斯模糊)

您可能已经注意到,“Gaussian”内核是用于 卷积 图像的最常用的内核。这是用于模糊效果的数学理想内核。例如,这里是一个小的“Gaussian”内核的 Show Kernel(它们可以非常快地变得非常大)...

  magick xc: -define morphology:showKernel=1 \
         -morphology Convolve:0 Gaussian:0x0.8 null:
[IM Text]
我实际上不想对上面进行卷积,因为我只想显示它将要使用的内核。因此,我使用了一个“:0”的 迭代次数,所以它什么都不做。同样,我使用特殊的“null:”文件格式来丢弃生成的图像输出。正如您从卷积输出范围中看到的那样,“Gaussian”内核已经为您进行了归一化(缩放)。但是您也会注意到它仍然是一个相当大的内核,充满了小的分数值。如果您仔细观察,您会发现最大值(2.48678,也在第一行中列出)在中心,最小值在边缘和角落(大约 .000000194 的值)。这里是一个典型的使用卷积的高斯模糊...

  magick face.png -morphology Convolve Gaussian:0x2 face_gaussian.png
[IM Output] [IM Output] ==> [IM Output]
内核语法很简单...

   Gaussian:[{radius}]x{sigma}
这些参数实际上与“-gaussian-blur”运算符所使用的参数完全相同,该运算符实际上使用该内核执行 卷积。第一个数字,像大多数 形态内核 一样,是内核的“radius”或大小。这只是一个整数,最小值为 1,使最小的可能内核大小为 3x3 个元素。最好的方法是始终指定零,这允许 ImageMagick 为提供的“sigma”值计算一个适当的半径。第二个更重要的参数是“sigma”,它定义了每个像素应该模糊或“散布”多少。值越大,图像越模糊。这是一个浮点数。必须提供sigma。如果给出“0.0”的 sigma 值,您将得到一个相当无用的“Unity”内核(具有给定的半径,或半径为 1,因此产生一个 3x3 内核,其中包含一个周围为“0.0”值的“1.0”值)。正如您在上面看到的,使用任何类型的“Unity”内核进行卷积对图像没有任何影响!如果您确实指定了一个“radius”,那么通常最好使其至少是“sigma”的两倍,IM 通常计算一个大约是其 3 倍的半径(实际上是提供有意义结果的最大半径),尽管这取决于您特定 IM 安装的 编译时质量。有关“Gaussian”内核参数的影响以及一般图像模糊的更多信息,请参见... 模糊图像 [IM Output]

模糊内核(1d 高斯模糊)

Blur”内核与 高斯内核 非常相似,甚至采用相同的参数(见下文)。但高斯是二维曲线,而“Blur”内核产生一维曲线。也就是说,它会生成一长串薄薄的单行值。这里是一个小的“Blur”内核的 Show Kernel 输出。

  magick xc: -define morphology:showKernel=1 \
         -morphology Convolve:0 Blur:0x0.8 null:
[IM Output]
[IM Text]
上面显示的图表是默认“Blur”内核的实际配置文件。它是使用 Kernel Image 脚本“kernel2image”创建的,然后使用“im_profile”脚本对该图像进行绘制。它清楚地显示了此内核表示的“高斯钟形曲线”。
以下是如何使用此内核水平模糊图像的示例。

  magick face.png -morphology Convolve Blur:0x4 face_blur.png
[IM Output]
内核语法与“Gaussian”完全相同,但还有一个可选的旋转角度。

   Blur:[{radius}]x{sigma}[,{angle}]
如前所述,第二个值“sigma”是必需的,如果设置为零,您将得到一个“Unity”内核的线性等效项。
angle”允许您将内核旋转 90 度,从而使您可以垂直模糊图像。

  magick face.png -morphology Convolve Blur:0x4,90 face_blur_vert.png
[IM Output]
目前,只能进行 90 度旋转。这可能会在 ImageMagick 的后续版本中发生变化。此内核的目的是实际创建一个比“Gaussian”内核产生的 2 维图像模糊更快的形式。有关如何实现此操作的详细信息,请参阅下面的 高斯与模糊内核 [IM Output]

彗星内核(一半 1d 高斯模糊)

Comet”内核几乎与“Blur”内核完全相同,但实际上只是模糊内核的一半。

  magick xc: -define morphology:showKernel=1 \
         -morphology Convolve:0 Comet:0x1.0 null:
[IM Text]
请注意,原点的定义位置在左侧边缘,而不是内核的中心。这对卷积内核来说很不寻常,因此产生了非常不寻常的结果。它像手指在湿润的油漆表面上涂抹一样,在一个方向上模糊图像,留下颜色痕迹。有点像彗星的尾巴,或者流星或流星坠落的痕迹。

  magick face.png -morphology Convolve Comet:0x5 face_comet.png
[IM Output]
请注意,前景和背景颜色都被“涂抹”。如果您只想模糊前景颜色,请使背景透明,并在模糊前景后添加它。您还可以提供第三个angle参数,以将其围绕“原点”旋转任何 90 度倍数的内核。

  magick face.png -morphology Convolve comet:0x5+90 face_comet_vert.png
[IM Output]
此内核实际上与专用 运动模糊 运算符使用的内核相同,尽管该运算符也进行一些非常花哨的坐标查找处理,以使模糊在任何角度都能正常工作。虽然它做得不好,在较大的角度(如 45 度)会产生颜色“块”。希望将实现适当的内核旋转,以便在 90 度增量以外的角度产生更好的运动模糊类型效果。 [IM 输出]

高斯与模糊核

如前所述,“Gaussian”和“Blur”内核密切相关,实际上可以完成相同的工作。两者都是 高斯曲线 的表示,前者是二维表示,而后者是一维表示。例如,以下是“-gaussian-blur 0x2”的重复,它等效于“-morphology Convolve Gaussian:0x2”操作。

  magick face.png -gaussian-blur 0x2 face_gaussian-blur.png
[IM Output]
这可以通过使用两个相互旋转 90 度的线性或一维模糊操作来代替(顺序并不重要)...

  magick face.png -morphology Convolve Blur:0x2 \
         -morphology Convolve Blur:0x2+90 face_blur_x2.png
[IM Output]
您可以通过将两个内核都作为内核列表来提供,而不是指定两个单独的卷积。例如

  magick face.png -morphology Convolve 'Blur:0x2;Blur:0x2+90' face_blur_x2.png
默认情况下,IM 会根据 多内核合成 设置,使用第一个卷积内核的结果与第二个(以及后面的)卷积内核进行“重新迭代”。您甚至可以通过要求 IM 将一个内核扩展为 旋转内核列表 来进一步简化上面的操作,方法是使用“>”来进行 90 度旋转列表(在本例中为两个内核)。例如...

  magick face.png -morphology Convolve 'Blur:0x2>' face_blur_x2.png
以上所有示例都是等效的,也是“-blur”运算符的工作原理。

  magick face.png -blur 0x2 face_blurred.png
[IM Output]
这代表了“-blur”和“-gaussian-blur”运算符之间的真正区别。在后者中,使用了一个大的二维内核,而在前者中,使用两个小的二维内核。然而,在速度方面,“-blur”运算符通常快一个数量级,因为它使用两个小得多的内核,而不是一个非常大的内核。模糊参数越大(sigma参数的大小),内核就越大,两种操作之间的速度差异就越大。因此,“-blur”运算符通常是推荐使用的运算符。两种运算符之间结果的唯一区别是小的量子舍入效应(除非您使用 HDRI)和边缘效应(取决于 虚拟像素设置)。这两者都是由由于在“blur”卷积的两个单独步骤之间保存中间图像而导致的信息丢失造成的。这种差异通常小到肉眼看不见,对任何实际应用都没有影响。

柔化模糊(与原始图像混合)

您可以通过将任何类型的模糊与原始图像的某些部分混合来柔化其影响。尤其是在应用非常强的模糊时。例如...

  magick face.png -morphology Convolve Gaussian:0x3 face_strong_blur.png
  magick face.png face_strong_blur.png \
         -compose Blend -define compose:args=60,40% -composite \
         face_soft_blur.png
[IM Output] + [IM Output] ==> [IM Output]
这使用了“`混合”合成方法,将模糊图像(合成源图像)的“`60%`”与原始图像(合成目标图像)的“`40%`”混合,从而在最终图像上产生“柔化模糊”效果。但是,您可以直接通过`将内核与单位内核混合`来完成相同操作,并使用相同的比例。

  magick face.png -define convolve:scale=60,40% \
         -morphology Convolve 'Gaussian:0x3' face_soft_blur2.png
[IM Output]
请注意,缩放数字的顺序是相同的。第一个数字(“`60%`”)缩放给定内核,以减少其对输出的影响,而第二个数字(“`40%`”)添加足够多的“`单位”(或“单位”)内核,以防止结果变暗。重要的是,对于`模糊内核`,这两个数字加起来为“`100%`”,就像您对`合成混合`一样。您也可以使用更快的两遍模糊,但在此情况下,我们不能直接将“混合”合并到内核中,因为两个独立的卷积不会“干净地”分离。因此,我们将需要再次进行`混合合成`。

  magick face.png \( +clone -blur 0x3 \) \
         -compose Blend -define compose:args=60 -composite \
         face_soft_blur3.png
[IM Output]
请注意,您只需要提供要与原始图像混合的模糊(源)图像的比例。因此,值“`100`”将给出模糊图像,而“`0`”将给出原始图像。
请记住,“`-blur`”运算符与使用更快的两遍`模糊内核`完全相同。

使用模糊“取消锐化”图像(从原始图像中减去)

通过进一步进行内核混合,使您开始使用负缩放,您可以从原始图像中减去模糊效果。结果是一种称为“反锐化”的技术。请参见`反锐化,维基百科`,了解它是如何获得如此不幸的名字的。

  magick face.png -define convolve:scale=-100,200% \
         -morphology Convolve 'Gaussian:0x2' face_unsharp.png
[IM Output]
请注意,即使使用负内核缩放因子,这两个数字仍然加起来为“`100%`”,与上面完全一样。您也可以使用`合成混合`来完成此操作。上面的示例实际上是错误命名的“`-sharpen`”运算符的工作原理,但仅使用“*sigma*”模糊控制。但是,没有提供对操作的其他控制。混合与上面给出的完全相同。您可以使用更快的两遍一维`模糊内核`,但同样,您需要将混合操作作为单独的步骤进行。

  magick face.png \( +clone -blur 0x2 \) \
         -compose Blend -define compose:args=-100,200 -composite \
         face_unsharp_fast.png
[IM Output]
您可能已经了解到这与`软化模糊`几乎相同,但模糊图像是从原始图像中减去而不是添加。一种称为`外推混合`或超出正常 0 到 100% 范围的混合的混合方法。同样,您只需指定要从原始图像中减去的模糊图像的比例即可。例如,让我们过度锐化图像,导致一些混叠和颜色失真。

  magick face.png \( +clone -blur 0x2 \) \
         -compose Blend -define compose:args=-200 -composite \
         face_unsharp_200.png
[IM Output]
完整的“`-unsharp`”运算符提供了另一种类型的控制。具体来说,一个差异阈值,使得锐化仅在给定差异较大时应用,例如在图像中的实际边缘附近。该阈值可以用于防止“锐化”小的缺陷,如皱纹或相机噪声。图像反锐化通常与非常小的模糊(*sigma*=0.75 数量级)一起使用,在调整大小或扭曲图像后,以改进最终结果。请参见`锐化调整大小的图像`,以了解一些示例。使用“反锐化”技术进行图像锐化的另一种方法是实际定位图像边缘并使用它们来锐化图像。请参见下面`使用边缘检测锐化图像`,以了解详细信息。但是,它通常被认为速度较慢,尽管不是快很多。

边缘检测卷积(高通滤波)

边缘检测是卷积大量使用的另一个领域。这里任务是以各种方式突出显示或增强图像的边缘。这可以是尽可能准确地定位边缘,也可以是确定每个边缘的斜率角度或方向。但是,图像中存在噪声会使这项工作变得更加困难,例如由扫描仪、数码相机产生的噪声,甚至仅仅是由 JPEG 图像文件格式的有损压缩引起的噪声。一般来说,较大的内核可以更好地处理噪声,但会损失边缘定位精度,而较小的内核会产生清晰的边缘定位结果,但会产生更多由图像噪声引起的虚假结果。有很多小的、众所周知的内核,这些内核已经被开发出来并用于边缘检测。这些内核大多以研究数学或开发特定内核类型的数学家的名字命名。因此,您有诸如“`拉普拉斯`”、“`索贝尔`”和“`普雷维特`”之类的内核。这些“命名”内核通常非常小,并且使用整数定义,因此可以构建到专门设计的优化软件和硬件中以提高速度。也就是说,它们被称为“离散”内核。因此,您需要将内核作为其使用的一部分进行`缩放`或`归一化`。边缘检测也具有锐化图像边缘的副作用。``

零和内核

所有边缘检测内核都具有一个共同特征。它们都是零和的。这意味着它们包含负值,但内核中的所有值加起来为零。对于平滑的单色图像,使用此类内核进行`卷积`将产生一个“零”或黑色图像。但是,对于任何其他图像,您将获得包含负值和正值的结果。例如,这里我在包含一些基本形状的图像上应用了一个离散的“`索贝尔`”边缘检测器...

  magick -size 80x80 xc:black \
         -fill white -draw 'rectangle 15,15 65,65' \
         -fill black -draw 'circle 40,40 40,20' shapes.gif
  magick shapes.gif -define convolve:scale='!' \
         -morphology Convolve Sobel shapes_sobel.gif
[IM Output] ==> [IM Output]
如果您查看结果,您会发现内核是有方向的,因为只有垂直边缘被找到(由角度为零的“`索贝尔`”内核定义)。但是,它只找到了一组边缘,“正”从左到右从黑到白的斜率。为了获得“负”斜率,您需要使用`内核缩放设置`来对内核取反。例如...

  magick shapes.gif -define convolve:scale='-1!' \
         -morphology Convolve Sobel shapes_sobel_neg.gif
[IM Output]
对于“`索贝尔`”内核,您也可以将其旋转 180 度以获得与“缩放取反”相同的结果,但并非所有内核都是以这种方式对称的。另一种解决方案是向结果添加`输出偏差`。也就是说,向结果图像添加 50% 的灰色,使负值比此值更浅,正值更亮。但是,您还需要`缩放内核`以确保结果不会被图像的“黑色”和“白色”限制“裁剪”。

  magick shapes.gif -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Sobel shapes_sobel_bias.gif
[IM Output]
如果您不关心极性,可以使用一些技巧来获得结果的绝对值。

  magick shapes.gif -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Sobel -solarize 50% -level 50,0% \
         shapes_sobel_abs.gif
[IM Output]
请参见“`索贝尔`”内核,以了解有关结果处理技术的更多信息,尤其是涉及方向确定的技术。使用`输出偏差`的另一种方法是构建 Imagemagick 的特殊`HDRI` 版本。这使用浮点值在内存中存储图像,这意味着图像值不会因使用整数而被“裁剪”或“舍入”。但是,即使您使用此特殊版本的 IM,您仍然需要在保存到普通图像文件格式之前对结果进行后处理,或者您需要使用特殊支持浮点的图像文件格式。但是,您无需担心中间图像结果中的裁剪或舍入效果,从而使处理变得更容易。``

边缘检测内核

[IM Output]

LoG:高斯函数的拉普拉斯算子


   LoG:{radius},{sigma}
“`LoG`”或“高斯函数的拉普拉斯算子”是您可以获得的最佳边缘检测内核之一。它也称为“墨西哥帽”内核。基本上,它是一个“`拉普拉斯`”微分(斜率)运算符,已通过添加高斯模糊而平滑。这反过来又消除了图像中大多数噪声的影响,可以通过“*sigma*”设置进行调整。内核包含负值,这些负值围绕一个强烈的中心峰形成一个环。在上图所示的“内核图像”中,负值显示为深色(接近黑色)的颜色,边缘衰减到零(深灰色)朝向边缘。以下显示了它的效果,说明了它是如何突出显示图像边缘的。

  magick face.png -bias 50% -morphology Convolve LoG:0x2 face_log.png
[IM Output] [IM Output] ==> [IM Output]
拉普拉斯内核是无方向的,但在边缘的两侧都会产生正脊和负脊。为了定位边缘,您将寻找正脊和负脊之间的零交叉点,这种技术被称为`马尔和希尔德里斯边缘检测`。此内核也非常适合`锐化图像`。
[IM Output]

DoG:高斯函数的差


   DoG:{radius},{sigma1}[,{sigma2}]
这将生成一个“`DoG`”或“高斯函数的差”内核,其中由“*sigma1*”生成的高斯函数将减去由“*sigma2*”生成的高斯函数。通常,“*sigma2*”更大,以便内核的“中心峰”为正。反转这两个数字将有效地对结果内核取反。“`高斯函数的拉普拉斯算子`”的主要批评之一是,由于它是一个非常不寻常的数学曲线,因此很难实现。它也不是一个记录得很好的曲线。另一个方面是,它不能像高斯函数那样“分离”成更快的两遍解决方案(请参见`高斯函数与模糊内核`)。但是,通过生成两个“`高斯函数`”内核,它们具有略微不同的 *sigma* 值(大约为 1.6 的比例),并将它们相互减去,您可以实际生成“`高斯函数的拉普拉斯算子`”的近似值。结果是,与“`LoG`”内核相比,“`DoG`”更容易在硬件中生成。例如,这里我并排放置了“`LoG`”和“`DoG`”内核的`内核图像`,以便比较。
[IM Output] [IM Output]
如果您查看`高斯函数的差,维基百科` 网页,您会看到一些图表,它们还比较了“`LoG`”(或“墨西哥帽”)和“`DoG`”的轮廓,显示了匹配曲线之间的非常小的差异。*需要更多有关如何映射 LoG 的 sigma 以生成近似等效的 'DoG' 的信息。如果您知道,请通过页脚中的地址给我发送电子邮件。*
应用的结果也非常相似。

  magick face.png -bias 50% -morphology Convolve DoG:0,1.8,2.4 face_dog.png
[IM Output]
请注意,两个“*sigma*”值都应定义,至少一个应非零。任一 sigma 分量的值为零将等效于“`单位`”内核,这意味着它保持图像不变。如果两个值都为零,则两个高斯函数将是“`单位`”内核,当它们被减去时将产生一个完全为零或黑色的结果(加上任何偏差值)。当参数为“`Dog:0,0,*非零*`”时,DoG 变成了一个简单的带通滤波器,它被定义为“单位”内核(生成原始图像)减去低通滤波器内核(模糊图像)。因此,以下内容会生成一个带通滤波后的图像,其滤波器值为 *sigma2*=2

  magick face.png -bias 50% -morphology Convolve DoG:0,0,2 face_dog_unity.png
[IM Output]
具有滤波器值半径=2 的 Photoshop 带通滤波器会产生相同的结果。请注意,使用“`DoG:0,2,0`”将返回一个图像,该图像基本上是前一个图像的取反版本(围绕输出偏差)。此技术还可用于生成 3x3 的“各向同性拉普拉斯”内核,即在所有方向上产生相同结果的“`拉普拉斯`”内核,而不是具有不等的对角线偏差。例如,半径=1(对于 3x3 内核)和 sigma 为 1 将生成...

  magick face.png -define morphology:showKernel=1 -bias 50% \
          -morphology Convolve DoG:1,0,1 face_laplacian_isotropic.png
[IM Text]
[IM Text]
使用“高斯差”的另一个要点是,您可以使用速度更快的“-blur”运算符(在内部使用“Blur”内核)来生成相同的结果。但是要做到这一点,您需要分别生成两个“模糊”图像,然后将结果相减,并加上适当的缩放和偏差。例如...

  magick face.png \
         \( -clone 0 -blur 0x1.8 \) \( -clone 0 -blur 0x2.4 \) -delete 0 \
         -compose Mathematics -define compose:args=0,-4,4,0.5 -composite \
         face_diff_of_blurs.png
[IM Output]
以上使用了特殊的 数学合成方法 来避免在 IM 的非 HDRI 版本中图像相减期间出现“裁剪”问题。有关更多详细信息,请参阅 添加偏差梯度。唯一的另一个因素是在减法过程中使用更大的缩放因子(数学合成 参数中的两个“4”)。这是因为,减去两个归一化的模糊不会产生与在“DoG”内核中将两个减去的高斯曲线归一化在一起产生的相同(增加的)幅度结果。但是除了幅度之外,上面的示例图像等效于第一个“DoG”内核结果,只是生成速度更快,特别是对于较大的 sigma 值。重点在于,尽管工作量更大,这种复杂方法比直接使用“DoG”或“LoG”内核更快。

离散拉普拉斯内核


   Laplacian:{type}
许多形式的小型“拉普拉斯内核”已在许多科学研究论文中发表。这里我提供了我在学术文献中找到的更常见形式的内置版本。这些内核基本上是使用“LoG”内核计算的,但经过缩放以在小型内核数组中使用离散整数。这样,您可以使用生成的专用快速图像过滤器,这些过滤器仅使用整数数学来处理图像数据。但是 ImageMagick 是一个更通用的图像处理器,因此不提供这种超快的专用过滤器。但是人们喜欢使用它们,因为它们更容易理解,因此其中许多已内置到 IM 中。这里提供的内核都不旋转,大多数是“各向异性的”,这意味着它们不是完美的圆形,尤其是在对角线方向。但是请参阅上一节(“DoG”内核),了解如何生成真正的“各向同性 3x3 拉普拉斯内核”。前两个“Laplacian:0”和“Laplacian:1”内核是使用最广泛的“离散拉普拉斯内核”形式。它们非常小,这意味着它们可以非常准确地定位边缘,但也容易增强图像噪声。请注意,并非所有“类型”数字都已定义,为将来定义更多离散内核留出了空间。使用的数字被选中以更好地匹配该数字定义的内核。

Laplacian:0(默认)

8 邻域拉普拉斯算子。可能是最常见的离散拉普拉斯边缘检测内核。这里我使用 显示内核 来提取“离散”和“未归一化”内核,然后在向您展示归一化内核的结果后,再加上 输出偏差

  magick xc: -define morphology:showKernel=1 -precision 2 \
         -morphology Convolve:0 Laplacian:0 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:0 face_laplacian_0.png
[IM Text]
[IM Text]
有时,拉普拉斯算子,无论是离散拉普拉斯算子(如上例所示),还是生成的“LoG”或“DoG”,都会产生比预期更复杂的结果。在这种情况下,生成无偏差图像(没有任何 输出偏差)效果会更好。因此,让我们在没有偏差的情况下重复上面的操作,以便只保留较亮的“正”边缘。

  magick face.png -define convolve:scale='!' \
         -morphology Convolve Laplacian:0 \
         -auto-level face_laplacian_positives.png
[IM Output]
在这种情况下,我们在较亮(白色)的颜色上具有暗(黑色)线条。这反过来会导致过滤器“加倍”边缘,这可以在显示的结果中看到。对于此图像,使用负缩放因子(保留负边缘,而不是正边缘),并且似乎在我们的测试图像上效果更好。

  magick face.png -define convolve:scale='-1!' \
         -morphology Convolve Laplacian:0 \
         -auto-level face_laplacian_negatives.png
[IM Output]
如您所见,对于此图像,使用负尺寸会产生更强的边缘,而不会出现正结果产生的“双重”效果。这是因为在使用的图像中,使用了“黑色”边缘线在白色背景上。另外:您在黄色星星周围获得蓝色边缘的原因是,“黄色”星星与“白色”背景之间的差异是对蓝色颜色的减法。如果背景是黑色,您将获得黄色的边缘颜色。

Laplacian:1

4 邻域拉普拉斯算子。也经常使用。

  magick xc: -define morphology:showKernel=1 -precision 2 \
         -morphology Convolve:0 Laplacian:1 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:1 face_laplacian_1.png
[IM Text]
[IM Text]
结果不强,但通常比 8 邻域拉普拉斯算子更清晰。

Laplacian:2

3x3 拉普拉斯算子,中心:4 边缘:1 角点:-2

  magick xc: -define morphology:showKernel=1 -precision 2 \
        -morphology Convolve:0 Laplacian:2 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:2 face_laplacian_2.png
[IM Text]
[IM Text]

Laplacian:3

3x3 拉普拉斯算子,中心:4 边缘:-2 角点:1

  magick xc: -define morphology:showKernel=1 -precision 2 \
             -morphology Convolve:0 Laplacian:3 null:
  magick face.png -define convolve:scale='400%!' -bias 50% \
         -morphology Convolve Laplacian:3  face_laplacian_3.png
[IM Text]
[IM Text]
此内核突出显示对角线边缘,并往往使垂直和水平边缘消失。但是您可能需要缩放结果(如我在上面所做的那样)才能看到任何可见结果。

Laplacian:5

5x5 拉普拉斯算子

  magick xc: -define morphology:showKernel=1 -precision 2 \
         -morphology Convolve:0 Laplacian:5 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:5 face_laplacian_5.png
[IM Text]
[IM Text]
拉普拉斯内核的经验法则是在涉及错误时,内核越大,结果越干净,尤其是在涉及错误时。但是您也会获得更少的细节。

Laplacian:7

7x7 拉普拉斯算子

  magick xc: -define morphology:showKernel=1 -precision 2 \
         -morphology Convolve:0 Laplacian:7 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:7 face_laplacian_7.png
[IM Text]
[IM Text]

Laplacian:15

离散 5x5 LoG(Sigma 大约为 1.4)

  magick xc: -define morphology:showKernel=1 -precision 2 \
         -morphology Convolve:0 Laplacian:15 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:15 face_laplacian_15.png
[IM Text]
[IM Text]

Laplacian:19

离散 9x9 LoG(Sigma 大约为 1.4)

  magick xc: -define morphology:showKernel=1 -precision 2 \
         -morphology Convolve:0 Laplacian:19 null:
  magick face.png -define convolve:scale='!' -bias 50% \
         -morphology Convolve Laplacian:19 face_laplacian_19.png
[IM Text]
[IM Text]

使用边缘检测锐化图像(增强原始图像的边缘)

LoG”和“DoG”内核也可用于锐化图像,而不是 使用模糊来使图像不清晰。基本上,您需要做的就是将内核结果(包括负结果)添加到原始图像。要做到这一点很容易,只需将 100% 加权的“Unity”或“身份”内核添加到缩放因子即可。这就是提供它的原因。
例如...

  magick face.png -define convolve:scale='100,100%' \
         -morphology Convolve 'Log:0x2' face_sharpen.png
[IM Output]
这比 非锐化技术 生成的图像更宽泛、更平滑的锐化(结果显示在右侧)。也就是说,因为它是对图像的真正锐化,而不是通过减去模糊而伪造的锐化。 [IM Output]
与以前一样,当仅执行单次传递卷积时,您可以直接使用 混合内核
例如,锐度较低...

  magick face.png -define convolve:scale='50,100%' \
         -morphology Convolve 'Log:0x2' face_sharpen_50.png
[IM Output]
或者更锐利...

  magick face.png -define convolve:scale='150,100%' \
         -morphology Convolve 'Log:0x2' face_sharpen_150.png
[IM Output]
您可以使用 高斯差 的 2 次传递方法来生成更快的多步锐化操作,但如上所述,这种方案需要 4 次卷积和一个单独的混合操作才能实现相同的结果。未来:添加此示例正是由于这种复杂性,使用 非锐化 才是更常用的图像锐化方法。但如您所见,对于重度锐化过程,使用适当的锐化内核比 非锐化锐化 更受欢迎。但是对于轻微锐化,例如 锐化调整大小的图像,使用非锐化没有问题。

方向卷积(斜率和罗盘)

与上面一样,这些内核寻找图像颜色强度的斜率,但不是任何斜率,它们寻找特定方向的斜率。在数学上,这被称为“导数”,这实际上只是“斜率”的另一种说法。但是了解不同方向的斜率信息也可能有用,因为它可以作为一种方法,您可以从中确定斜率或图像边缘的角度或“罗盘”方向。也就是说,图像中某个特定点的斜率的二维方向。斜率也用于称为图像“浮雕”和“阴影”的图像处理技术中。目前还没有可用的“生成”内核,只有“命名”的预定义内核,例如 SobelRoberts。但是我确信,浮雕和阴影内核生成函数将在未来某个时间点被移入形态学/卷积内核集。因此,让我们看看一些“命名”的方向内核。

方向核

Sobel


   Sobel:{angle}  
[IM Text]
我们在讨论 零求和内核 时已经看到了“Sobel”内核。此内核是一个原始的方向(一阶导数)内核,旨在返回某个特定正交方向的边缘斜率。默认情况下,它被设计用于使用“convolve”操作进行从左到右的斜率检测。结果本质上是图像的 X 导数(斜率)。

  magick -size 60x60 xc:black xc:white +append slope_positive.gif
  magick slope_positive.gif -morphology Convolve Sobel slope_sobel.gif
[IM Output] [IM Output] ==> [IM Output]
如果您查看内核,您可能会认为它被声明反了。从某种意义上说,您实际上是正确的。但是,这是由于“Convolve”的实际工作方式造成的。

您可以在下面的 Convolve 与 Correlate 中详细了解这种“反转”。

请注意,此内核也可以产生“负斜率”指示,但除非与该卷积操作一起使用“50%”的 偏差,否则无法看到这一点。虽然前面的例子中没有负斜率,但下面的例子中有一个,因此我还添加了一个 偏差设置,以便您可以看到它。

  magick -size 40x60 xc:black xc:white xc:black +append slope_both.gif
  magick slope_both.gif -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Sobel slope_sobel_bias.gif
[IM Output] [IM Output] ==> [IM Output]
如果您将此内核与“Correlate”一起使用,您将发现与内核定义方式“匹配”的斜率。在这种情况下,您将获得从左侧高(白色值)到右侧低(黑色值)的斜率的正结果。在上面的示例中,两条线将被交换。

但是上面是“卷积”,而不是“相关”(意思是匹配内核)。再次查看 Convolve 与 Correlate,了解有关差异的更多详细信息。
如您所见,当我们从黑色到白色向上斜坡时,现在得到一条白线(正斜率),当我们从白色到黑色向下斜坡时,得到一条黑线(负斜率)。以下是将默认的“Sobel”内核应用于人脸图像的结果。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Sobel face_sobel.png
[IM Output] [IM Output] ==> [IM Output]
请注意,sobel 和大多数其他边缘检测内核倾向于在非常强的边缘产生 2 像素厚的响应,并在单像素宽的线上产生 3 像素厚的响应。这比拉普拉斯边缘检测器要强得多。您可以使用“angle”参数旋转此内核,通常以 90 度的倍数旋转。但是您也可以以 45 度的倍数旋转它,即使它不是为此设计的。这对于从所有 45 度旋转导数结果的最大值中获取 45 度量化的方向导数或梯度幅度很有用。
这里再次出现,但旋转了 90 度(从上到下)。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Sobel:90 face_sobel_90.png
[IM Output]

使用“Sobel”内核收集图像所有边缘的一种方法是,在所有方向上将内核应用 4 次,并收集看到的最大值(使用 淡化数学合成)。这近似于梯度幅度。

   magick face.png -define convolve:scale='!' \
          \( -clone 0 -morphology Convolve Sobel:0 \) \
          \( -clone 0 -morphology Convolve Sobel:90 \) \
          \( -clone 0 -morphology Convolve Sobel:180 \) \
          \( -clone 0 -morphology Convolve Sobel:270 \) \
          -delete 0 -background Black -compose Lighten -flatten \
          face_sobel_maximum.png
[IM Output]
您可以通过利用 IM 形态学的 多个内核处理 功能来简化上述操作。也就是说,您可以创建一个旋转列表,其中包含“Sobel”内核的所有 90 度旋转。

   magick face.png -define convolve:scale='!' \
          -define morphology:compose=Lighten \
          -morphology Convolve 'Sobel:>' face_sobel_maximum_2.png
[IM Output]
如果您想确切了解上述代码的功能,请添加 显示内核 设置和 详细 设置。生成梯度幅度的更有效技术是利用这样一个事实:180 度旋转只是产生与内核取反相同的结果,从而使结果取反。因此,X 和 Y 导数(90 度旋转卷积),通过一些技巧来获取卷积的绝对值,可以在更少的处理量下实现这样的结果。

   magick face.png -define convolve:scale='50%!' -bias 50% \
          \( -clone 0 -morphology Convolve Sobel:0 \) \
          \( -clone 0 -morphology Convolve Sobel:90 \) \
          -delete 0 -solarize 50% -level 50,0% \
          -compose Lighten -composite face_sobel_maximum_3.png
[IM Output]
这对于大多数目的来说通常已经足够好了。可以通过对两个 X 和 Y 导数进行向量加法(如 勾股定理 所述)来提取所有斜率的更精确幅度。

   magick face.png -define convolve:scale='50%!' -bias 50% \
          \( -clone 0 -morphology Convolve Sobel:0 \) \
          \( -clone 0 -morphology Convolve Sobel:90 \) \
          -delete 0 -solarize 50% -level 50,0% \
          +level 0,70% -gamma 0.5 -compose plus -composite -gamma 2 \
          -auto-level face_sobel_magnitude.png
[IM Output]
上述示例中使用的 "-gamma" 函数用于对 "索贝尔" 结果返回的值执行数学上的 "平方" 和 "平方根" 操作。有关更多详细信息,请参阅 数学函数的幂

额外的 "+level" 确保 加法合成 不会超出图像量子范围。有关详细信息,请参阅 量子效应,非 HDRI 与 HDRI
除了幅度之外,您还可以从两个边缘检测结果中提取斜率的方向。

  magick -size 30x600 xc:'#0F0' -colorspace HSB \
         gradient: -compose CopyRed -composite \
         -colorspace RGB -rotate 90  rainbow.jpg
  magick shapes.gif -define convolve:scale='50%!' -bias 50% \
         \( -clone 0 -morphology Convolve Sobel:0 \) \
         \( -clone 0 -morphology Convolve Sobel:90 \) \
         -delete 0 \
         \( -clone 0,1 -fx '0.5+atan2(v-0.5,0.5-u)/pi/2' rainbow.jpg -clut \) \
         \( -clone 0,1 -fx 'u>0.48&&u<0.52&&v>0.48&&v<0.52 ? 0.0 : 1.0' \) \
         -delete 0,1 -alpha off -compose CopyOpacity -composite \
         face_sobel_direction.png
[IM Output] ==> [IM Output]
第一个 "-fx" 表达式是使用 "atan()" 函数将 X、Y 向量转换为角度的表达式。然后,它使用外部 彩虹渐变图像 作为 颜色查找表 进行着色。第二个 "-fx" 表达式创建一个阈值透明度蒙版,使任何没有斜率的区域都变得透明。但是,上述技术往往会为真实图像生成大量混乱的信息,因为它没有考虑斜率的幅度。以下是另一个更复杂的版本。它几乎所有计算都在绿色 "G" 通道中进行,从而将所需的图像处理量减少了三分之一。然后,它使用 HSB 色彩空间来创建方向(色调)和幅度(亮度)。

  magick face.png -colorspace Gray    -channel G \
         -define convolve:scale='50%!' -bias 50% \
         \( -clone 0 -morphology Convolve Sobel:0 \) \
         \( -clone 0 -morphology Convolve Sobel:90 \) \
         -delete 0 \
         \( -clone 0,1 -fx '0.5 + atan2(v-0.5,0.5-u)/pi/2' \) \
         \( -clone 0   -fill white -colorize 100% \) \
         \( -clone 0,1 -fx 'hypot(u-0.5,v-0.5)*2' \) \
         -delete 0,1 -separate +channel \
         -set colorspace HSB -combine -colorspace RGB \
         face_sobel_magnitude_n_direction.png
[IM Output]

罗伯茨


   Roberts:{angle}  
[IM Text]
"罗伯茨" 内核比之前的 "索贝尔" 内核简单得多,并且会产生更紧密的边缘位置(低至 2 个像素)。当然,这也使其更容易受到噪声影响。通常,此内核由更小的 2x1 或甚至 2x2 内核表示,但是通过将其实现为 3x3 内核,我可以在 45 度增量中 "循环" 旋转内核。
例如,以下是 45 度的结果,更常见地称为 "罗伯茨十字" 内核。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Roberts:45 face_roberts.png
[IM Output]
与 "索贝尔" 一样,您也可以使用 多内核处理 从所有方向生成最大斜率。但是这次我们将得到 8 x 45 度方向,而不是只有 4 个。

   magick face.png -define morphology:compose=Lighten \
          -morphology Convolve 'Roberts:@' face_roberts_maximum.png
[IM Output]
请注意,将此内核旋转 180 度不会生成取反结果(由于存在偏移)。因此,您不能像 "索贝尔" 一样简单地合并一半数量的卷积。基本上,仅使用一个 "罗伯茨" 卷积生成的斜率比与实际图像对齐的斜率偏移了半个像素。也就是说,计算出的斜率位于 "+1" 和 "-1" 值之间,位于像素之间,但存储在中心 "-1" 像素中。但是,这也意味着通过保存围绕像素的所有斜率并将它们加在一起,您将获得一个更小的更锐利的边缘检测,只有 2 个像素(而不是 4 个像素)突出显示了清晰的边缘边界。

普雷维特


   Prewitt:{angle}  
[IM Text]
"普雷维特" 内核与 "索贝尔" 非常相似,虽然对特定边缘检测的精确方向要宽松得多。因此,结果有点模糊。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Prewitt face_prewitt.png
[IM Output]

罗盘


   Compass:{angle}  
[IM Text]
这是 "普雷维特罗盘" 内核,据称比 "索贝尔" 具有更强的方向感。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Compass face_compass.png
[IM Output]

基尔希


   Kirsch:{angle}  
[IM Text]
这是另一个强大的方向感边缘检测器。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Kirsch face_kirsch.png
[IM Output]

弗莱-陈

此内置函数提供了三组内核。第一个是 "索贝尔" 的 "各向同性"(统一方向)变体,其中 "2" 值已替换为 2 的平方根。

   Frei-Chen:[{type},][{angle}]  
[IM Text]
上面的内核是 "弗莱-陈" 内核的核心,它是默认的未加权内核。

  magick face.png -define convolve:scale='50%!' -bias 50% \
         -morphology Convolve Frei-Chen face_freichen.png
[IM Output]
与 "索贝尔" 一样,此内核应使用 90 度倍数的角度进行应用。为了使事情更简单,提供了两个内核(具有相同的权重),一个用于正交使用,另一个用于对角线使用。
弗莱-陈:1
[IM Text]
弗莱-陈:2
[IM Text]
第三组类型包含 9 个经过专门设计和加权的内核,这些内核不仅用于特定方向的边缘检测,还用于确定锋利边缘的实际角度。此处的 "类型" 是一个从 "11" 到 "19" 的数字,允许您从该集中提取任何一个内核。但是,如果您提供 "类型" 值为 "10",您将获得一个包含所有 9 个预加权内核的多内核列表。
The kernels are each applied to the original image, then the results are added
together to generate the edge detection result.

This is best done using a HDRI version of
ImageMagick.


   magick image.png \
          \( -clone 0 -morphology Convolve FreiChen:11 \) \
          \( -clone 0 -morphology Convolve FreiChen:12 \) \
          \( -clone 0 -morphology Convolve FreiChen:13 \) \
          \( -clone 0 -morphology Convolve FreiChen:14 \) \
          \( -clone 0 -morphology Convolve FreiChen:15 \) \
          \( -clone 0 -morphology Convolve FreiChen:16 \) \
          \( -clone 0 -morphology Convolve FreiChen:17 \) \
          \( -clone 0 -morphology Convolve FreiChen:18 \) \
          \( -clone 0 -morphology Convolve FreiChen:19 \) \
          -delete 0 -background Black -compose Plus -flatten \
          result.pfm
If a type of 10 is given then a multi-kernel list of all the 9 weighted kernels
shown above is generated. This lets you use multi-kernel composition to do the
above, much more simply...


    magick image.png -define morphology:compose=Plus \
           -morphology Convolve FreiChen:10 \
           result.pfm
I have not however found out what the supposed meaning of the results are.  If
anyone has any experience or knowledge about how this is actually used, please
let me know, so I can include it here for others to use.  


相关  ( )

"卷积" 方法基本上用于图像处理,而 "相关" 方法则更适合于模式匹配。也就是说,它对图像与其内核执行 "互相关" 操作,以寻找图像中给定形状的匹配。实际上,"卷积" 和 "相关" 是相同的操作。它们之间的唯一区别实际上非常小,即内核的 x 和 y 反射(相当于 180 度旋转)。我发现的关于相关和卷积的工作原理以及它们如何相互区别的最佳指南是 David Jacobs 撰写的 CMSC 426,2005 年秋季课程笔记

卷积与相关(非对称内核效果)

正如我上面提到的,"卷积" 和 "相关" 这两种运算符本质上是相同的。实际上,用户经常说卷积,而他们真正想表达的是相关。而且相关实际上是更容易理解的方法。对于围绕中心 "原点" 对称的内核(这种情况非常典型),这两种方法实际上是相同的。只有当您使用非对称或不均匀的内核时,这种差异才会显现出来。例如,这里我使用 "L" 形 "平面" 内核与我们的 "单个像素" 图像进行比较。

  magick pixel.gif  \
         -morphology Convolve '3: 1,0,0
                                  1,0,0
                                  1,1,0' convolve_shape.gif
[IM Output] [IM Output] ==> [IM Output]
如您所见,"卷积" 将中心处的单个像素扩展为围绕它形成 "L" 形状。即使原点本身不是 "邻域" 的一部分。现在让我们重复此示例,但改用 "相关"。

  magick pixel.gif  \
         -morphology Correlate '3: 1,0,0
                                   1,0,0
                                   1,1,0' correlate_shape.gif
[IM Output] [IM Output] ==> [IM Output]
如您所见,"相关" 也扩展了单个像素,形成了 "L" 形状,但它是一个 "旋转" 的 "L" 形状。这本质上是这两种方法之间的唯一区别。"相关" 方法按 "原样" 应用内核,这会导致单个像素扩展为 "旋转" 的形式。另一方面,"卷积" 实际上使用内核的 180 度 "旋转" 形式,以便每个像素都被扩展为相同的非旋转形状。如果您想查看一些关于 "卷积" 的工作原理的精彩示例,我建议您也查看 EECE \ CS 253 图像处理,第 7 课,空间卷积。第 22 页的图表实际上将 "反射" 内核应用于单个像素,就像我在上面所做的那样。
这种旋转差异可能看起来并不大,但这意味着在数学方面,卷积操作(用星号 ( '*' ) 符号表示)是 交换的,也就是说,如果将内核和图像都视为一个值的数组(或两个图像),那么 F * G == G * F。这也意味着卷积是 结合的,也就是说 ( F * G ) * H == F * ( G * H )。有关这方面的更多信息,请参阅 卷积属性,维基百科。"相关" 操作既不是 交换的,也不是 结合的。尽管它与之密切相关(通过内核的旋转)。基本上,"卷积" 更像是数学上的 "乘法",而 "相关" 则不是。所有这些麻烦的例外情况是,当所使用的内核在旋转 180 度后是相同的。也就是说,内核关于 "原点" 是对称的。在这种特殊情况下,这两种操作都会生成等效的结果。令人困惑的是,大多数用于卷积的内核(如高斯模糊、拉普拉斯算子等)是对称的,在这种情况下,您实际上是在进行卷积还是相关并不重要。因此,人们对这些含义变得放松和模糊。只有当它们不对称时,例如在 形状搜索(见下文)的情况下,或者使用方向内核(如 索贝尔)时,这种差异才会变得很重要。

相关和形状搜索

"相关" 方法的实际用途(按 "原样" 应用内核邻域,不进行旋转)是一种古老但简单的方法,用于定位与提供的内核中找到的形状大致匹配的形状对象。例如,如果我们要使用 "相关" 和 "L" 形内核来尝试搜索我们使用卷积方法示例创建的图像,我们将得到...

  magick convolve_shape.gif -define convolve:scale='1!' \
         -morphology Correlate '3: 1,0,0
                                   1,0,0
                                   1,1,0' correlate.gif
[IM Output] [IM Output] ==> [IM Output]
附注:请注意,上面的内核图像中的 "黑色" 区域表示值为零。此内核中没有负值,只有正值代表要匹配的形状。

请注意,我使用了 IM 的 内核归一化 来防止最终结果变得太亮,并将 "峰值" 淹没在一堆白点中。如您所见,"相关" 方法在内核 "原点" 与图像中相同形状完全匹配的位置产生了最大亮度。但它也会产生较低的亮度结果,在这种情况下,您只会得到形状的局部匹配。匹配的形状越多,像素就越亮。但是我要提醒您,虽然 "相关" 在这种情况下成功了,但它并不是真正很好的方法。例如,它可以在亮度非常高的区域生成大量的错误匹配。可以通过对不应与图像的黑暗背景匹配的区域使用负值来缓解此问题。也就是说,不匹配背景的区域应该使生成的像素亮度降低。

  magick convolve_shape.gif -define convolve:scale='1^'  \
         -morphology Correlate '4x5+2+2:  0 -1  0  0
                                         -1 +1 -1  0
                                         -1 +1 -1  0
                                         -1 +1 +1 -1
                                          0 -1 -1  0 ' correlate_pattern.gif
[IM Output] [IM Output] ==> [IM Output]
附注:为了使内核图像更清晰,我生成了内核图像,以便正值(前景)为白色,负值(背景)为黑色,零值(不关心)为透明。但是,实际上使用的内核是完全定义的,它以数字形式表示,其 "邻域" 是一个完整的矩形。

如您所见,匹配峰值更加突出,因为您现在不仅匹配了前景像素,还匹配了背景像素。请注意上面使用的特殊归一化标志 "^"。这很重要,因为它将分别对内核中的正值和负值进行归一化。也就是说,您希望与背景像素一样地搜索前景像素。这意味着您可以通过使用 IM 的 HDRI 版本 或适当使用 输出偏差(见上文)来搜索给定形状的正匹配和负匹配。例如,这里我将 "L" 形搜索应用于包含正 "L" 形和负 "L" 形的测试图像。(显示的图像已放大)

  magick test_morphology.gif  -bias 50% -define convolve:scale='50%^' \
         -morphology Correlate '4x5+2+2:  0 -1  0  0
                                         -1  1 -1  0
                                         -1  1 -1  0
                                         -1  1  1 -1
                                          0 -1 -1  0 ' correlate_bias.gif
[IM Output] [IM Output] ==> [IM Output]
输出偏差使搜索的正常输出为中灰色,而匹配形状则根据实际与“形状内核”匹配的像素数量,呈现更亮或更暗的颜色。如果只检查输出图像的实际值,则会产生一个纯白色像素和一个纯黑色像素,表示完美匹配。但是,也有相当多的近似匹配。如果我不关心负匹配或“黑色”匹配,我可以移除输出偏差和“50%”缩放因子,这样不匹配的像素就是黑色,而完美匹配的像素就是白色。一旦你拥有一个“相关”匹配图像,你需要尝试找到匹配的“峰值”。这可以使用另一个相关来完成,但并不总是很有效。更好的方法是使用更精确的模式匹配方法“命中与不命中”形态学,以及为此目的创建的特殊“峰值”。这将找到任何仅被更暗颜色像素包围的单个像素。其他“峰值”内核可用于查找“更松散”的匹配。

  magick correlate_bias.gif  -morphology hitandmiss peaks:1.9 \
         -auto-level correlate_peaks.gif
[IM Output] [IM Output] ==> [IM Output]
在这里,你可以轻松找到形状最佳匹配的位置,尽管匹配程度已经丢失。你可能想查看比较和子图像搜索的“峰值查找”部分。但也要查看 Fred Weinhaus 的脚本“maxima”。将来:使用快速傅立叶变换的归一化互相关,用于生成具有非常大图像(源图像和子图像)的快速图像相关。

相关与命中与不命中形态学

如果你将我表示的内核图像与命中与不命中形态学方法使用的内核进行比较,你会发现它们实际上代表的是同一件事。
'命中与不命中' '相关'
前景 值为“1.0 值为“1.0”(归一化前)
不关心 值为“Nan”或“0.5 值为“Nan”或“0.0
背景 值为“0.0 值为“-1.0”(归一化前)
结果 从背景的最大值中减去前景的最小值。因此,只有完全匹配才会产生正结果,阈值处理将产生二进制匹配图像。 生成图像与形状匹配程度的范围。只要整体模式存在,一些背景像素的值可能大于前景像素的值。可能难以定位特定的“匹配”峰值。你还可以找到负匹配。
如你所见,它们相互对应。因此,一个的内核可以转换为另一个的内核。但是“命中与不命中”只会找到具有明确前景与背景差异的完全匹配。因此,它对噪声和近似匹配的容忍度远低于“相关”。另一方面,'相关'可以使用线性图像处理来执行,更具体地说,可以使用快速傅立叶变换。这可以使使用更大模式和内核的模式匹配速度更快,尤其是在涉及多个模式时,为你节省将图像和模式转换为频域的成本。它也适用于实际图像,尽管可能还需要一些预处理和使用HDRI。使用哪种方法取决于你,以及你想要的结果。仅完全匹配,或具有更多错误的近似匹配,以及更快速算法的可能使用。请注意,对于在较大图像中查找较小的彩色图像的完全匹配,程序“magick compare”的子图像定位功能将提供比“命中与不命中”或“相关”方法更好的方法。这是因为它使用“颜色向量差异的最小二乘”进行子图像匹配,这可以为匹配结果提供更好的指标。但是它同样缓慢,尤其是对于大型图像。

邻居计数

卷积可以用于的一个更不寻常的事情称为邻居计数。也就是说,计算图像中每个像素点周围特定区域中存在的像素数量。

计数邻居

基本上,通过使用非常简单的卷积内核,你可以创建一个包含二进制图像中特定点周围邻居数量计数的图像。通过卷积大小为“1.5”的环形内核,你可以获得邻居计数。这里是对小区域中每个像素的邻居进行计数,并显示前后单个像素的放大图(使用放大图像脚本生成)...

  magick area.gif -define convolve:scale=\! \
         -morphology Convolve Ring:1.5 neighbour.gif
[IM Output] ==> [IM Output]
如你所见,所有像素的灰度级都显示了它们有多少邻居,包括边缘上的任何虚拟像素邻居。如果你想将当前像素包含在计数中,可以使用方形内核。通过适当的转换(包括级别调整)并使用PbmPlus 文件格式,你可以将上述灰度级转换为实际数字,如果你需要的话。

  magick neighbour.gif +depth +level 0,8 pgm: | pnmnoraw | tail -n +4
[IM Text]
如果你想排除实际形状内部的像素,你可以使用一个中心像素为强负数的内核,然后钳位任何负结果(如果你使用IM 的 HDRI 版本)。生成具有围绕大型负中心周围的正 1 的这种内核的一种简单方法是对标准离散拉普拉斯内核进行负缩放。

  magick area.gif -define convolve:scale=-1\! \
         -morphology Convolve Laplacian:0 -clamp neigh_edge.gif
[IM Output] ==> [IM Output]
当然,我们也可以使用原始图像作为蒙版来移除不感兴趣的像素。

生命游戏

1970 年,英国数学家约翰·霍顿·康威在《科学美国人》杂志上发表了一个特殊的模拟,它变得非常流行。现在它被称为康威的生命游戏。它基于一个点网格,每个点要么“活着”,要么“死了”。然后,在下一代中,哪些“细胞”被归类为“活着”或“死了”,取决于一组非常简单的规则,这些规则纯粹基于它们周围的活邻居细胞的数量。
  • 邻域是围绕每个“细胞”的 8 个像素。
  • 如果一个“活”细胞有 2 或 3 个邻居,它将继续存活。
  • 如果一个“死”细胞正好有 3 个邻居,它将“活”起来(出生)。
  • 否则,细胞将变为或保持“死亡”。
这些规则对二进制模式的结果是惊人的,因为你会得到似乎扩展、收缩、振荡甚至缓慢地跨越网格移动的“细胞”簇。它成为理论研究的一个主要焦点,以观察你是否甚至可以生成“DNA”风格的更大“生命模式”的复制。补充说明:似乎这是可能的,但它太脆弱,不切实际,这使得当前的 DNA 生命更加非凡。它还激发了人们对其他形式的细胞自动机的研究和实施的兴趣,作为一种使用非常非常简单的规则在非常小的尺度上生成和研究大型影响的方法。这很像在原子和分子水平上的化学反应,但更为复杂。因此,让我们使用 ImageMagick 来实现“生命”。首先,为了便于操作,我们将“活”细胞设为白色,“死”细胞设为黑色。这样一来,我们只需要计算 8 个像素邻域中每个细胞周围的“白色”像素。但是我们也可以用黑色和白色交换来实现,尽管这会更难理解它是如何完成的。但是,规则强烈依赖于中心细胞是活着还是死了。因此,我们需要将“死”细胞的邻域计数与“活”细胞的邻域计数分开。这可以通过简单地赋予中心细胞比所有邻居之和更大的值来完成。值为“10”很适合此目的。它是一个很好的圆整数字,大于 8 的最大邻域计数。这使得“生命游戏”卷积内核等效于...

    '3: 1,  1,  1     
        1, 10,  1
        1,  1,  1'
这将得到每个像素周围 8 个邻居(是否为“白色”)的计数,加上如果中心像素为“活”或“白色”,则值为 10。因此,此内核的值将为死像素的“0”到“8”,或活像素的“10”到“18”。如果我们将此内核按值 20 缩放(实际上按“0.05”缩放以生成渐变,见下文),你将生成一个具有 21 种可能的灰度级的图像。也就是说,你将获得一个“黑色”值以表示“0”灰度级,以及一个白色值以表示“21”灰度级,但内核实际上无法生成这种值。现在,我们可以将“生命游戏”规则编码到一个颜色查找表图像中,以便将上述内核生成的邻居计数“灰度级”魔术般地转换为根据“生命规则”的适当“生死”结果。

  magick -size 21x1 xc:black -fill white \
          -draw 'point 3,0  point 12,0  point 13,0' \
          life_clut.gif
  enlarge_image -25.3 -ml 'Life Rules' life_clut.gif life_clut_enlarged.png
[IM Output]
该图像非常小,所以我使用了放大图像脚本来生成一个更大的版本以在上面显示,每个像素清晰地分开。基本上,前 10 个像素是针对“死细胞”的操作,接下来的 10 个像素是针对“活细胞”的操作。左侧的第一个白色像素(死细胞周围的邻居计数 = 3)是“出生”,而右侧的两个白色像素(活细胞旁边的邻居计数为 2 和 3)允许现有的“活细胞”继续存活。任何其他结果都会使结果保持黑色(死亡)。颜色查找表 长度为 21 个像素,因为我将除以 20 的缩放因子,这意味着我们可以生成 0 到 20 范围内的值,或 21 个不同的灰度级。实际上,我们可以使用 10 以外的值作为中心(细胞之前状态),以及 20 作为缩放比例,但这些值易于使用。总之,我们将卷积内核除以 20,并使用一个长度为 21 像素的 CLUT(使用整数插值)将卷积结果(灰度级)与正确的输出颜色值匹配。补充说明:这种“生命规则”CLUT 可以被认为是通用的细胞自动机规则表。用于邻居计数的邻域模式也是细胞自动机的一部分。这种技术也在细胞自动机规则词典中概述,在规则表部分中,作为定义其列出的细胞自动机的一般“无限制”形式的一种方法。基本上,几乎所有自动机都可以使用这种邻域/表组合来定义,尽管大多数自动机使用更简化的形式来定义。因此,让我们将此应用于包含“生命”模式的图像,多次应用以查看模式如何从一代变为下一代,并检查它是否按预期工作。

  magick -size 15x15 xc:black -fill white \
         -draw 'line  3,2 3,4  line 10,10 12,10  point 10,11  point 11,12' \
         life_gen_000.gif
  magick life_gen_000.gif -define convolve:scale=0.05 \
         -morphology Convolve '3:1,1,1 1,10,1 1,1,1' \
         life_clut.gif -interpolate integer -clut \
         life_gen_001.gif
  magick life_gen_001.gif -define convolve:scale=0.05 \
         -morphology Convolve '3:1,1,1 1,10,1 1,1,1' \
         life_clut.gif -interpolate integer -clut \
         life_gen_002.gif
  magick life_gen_002.gif -define convolve:scale=0.05 \
         -morphology Convolve '3:1,1,1 1,10,1 1,1,1' \
         life_clut.gif -interpolate integer -clut \
         life_gen_003.gif
  magick life_gen_003.gif -define convolve:scale=0.05 \
         -morphology Convolve '3:1,1,1 1,10,1 1,1,1' \
         life_clut.gif -interpolate integer -clut \
         life_gen_004.gif
[IM Output] ==> [IM Output] ==> [IM Output] ==> [IM Output] ==> [IM Output]
请记住,上面的图片已被放大,要查看生成的原始“微小”图片,请点击放大的图片。正如你所看到的,“生命”模式表现如常(如果你熟悉这些模式)。左上角的“闪烁器”来回翻转,而底部的“滑翔机”在4个“世代”中向它移动了1个对角线步长,我们迭代了生命规则。 [IM 输出] 这里还有一个更大的例子,称为 戈斯珀滑翔机枪,我从一个特殊的生命模式中生成一个包含60帧的动画。正在使用的图片的实际大小显示在右侧,但我缩放了生成的动画以便更好地观看。

  magick glider_gun.gif life_pattern.gif
  for i in `seq 59`; do
    magick life_pattern.gif -define convolve:scale=0.05 \
           -morphology Convolve '3:1,1,1 1,10,1 1,1,1' \
           life_clut.gif -interpolate integer -clut \
           -write life_pattern.gif miff:-
  done | magick - -scale 500% \
                -set delay 10 -layers Optimize -loop 0  glider_gun_anim.gif
  magick glider_gun.gif -scale 500% life_pattern.gif
[IM Output] ==> [IM Output]
请注意,滑翔机在底部边缘“爆炸”的原因是 convolve 使用的默认“虚拟像素”处理,以及生命信息在超出图像范围时的丢失。可以在 生命模式目录 中找到更多生命模式图片,不过你需要重新着色(取反)这些图片才能在上面的生命处理器中使用。我将把它留给其他人作为练习,让他们将上述内容放入一个脚本中,该脚本可以为某些特定的输入图片生成一个生命序列。
这只是 IM 可以处理的各种“元胞自动机”的一个例子。当然,还有许多更快专门用于“生命”和“元胞自动机”的程序,它们通常做的事情完全相同,但我希望展示 IM 足够灵活,也能做到这一点。由于结果是简单的二进制图像,你还可以使用 IM 的 形态学 方法,例如 命中和错失模式搜索互相关 来搜索特定的生命模式,这使得使用 IM 进行生命研究更实用,尽管速度很慢。