StupidBeauty
Read times:108386Posted at: - no title specified
ImageMagick文档翻译:ImageMagick v6示例——图片的卷积,ImageMagick v6 Examples -- 
 Convolution of Images

内容目录

卷积简介

卷积()

卷积内核的缩放

内核归一化(自动缩放)

归一化具体是如何进行的

零和归一化

混合内核及标识内核

输出结果的倾斜程度控制

使图片模糊(低通滤波)

模糊化内核

统一

使用带形状的内核来实现均值滤波

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

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

彗星内核(半个1维高斯模糊)

高斯曲线

高斯内核与模糊化内核对比

柔化模糊(与原始图片进行混合)

使用模糊化操作来将图片'反锐化'(从原始图片中减去特定值)

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

零和内核

边缘检测内核

LoG:拉普拉斯高斯

DoG:高斯差分

离散拉普拉斯内核

Laplacian:0 (默认)

Laplacian:1

Laplacian:2

Laplacian:3

Laplacian:5

Laplacian:7

Laplacian:15

Laplacian:19

利用边缘检测来对图片进行锐化(增强原始图片中的边缘)

带有方向性的卷积(坡和盘)

方向性内核

Sobel

Roberts

Prewitt

Compass

Kirsch

Frei-Chen

相关度(  )

卷积与相关度的比较(非对称内核效果)

相关度及形状搜索

相关度计算与命中还是不命中形态学操作的比较

邻居计数

计算邻居数

生命游戏

卷积,就是使用各个像素的“邻居”像素来修改图片本身。具体做法就是:将每个像素附近的颜色值融合起来,或者取其平均值。得到的效果是:将图片模糊化;高亮显示边缘和边界;将图片锐化。卷积的一个变种,“相关性”('Correlation'),也会被用来扫描图片,并寻找特定的模式,产生出一张图片,其表明了,两张图片之间的相似度。

卷积简介

“卷积”,以及非常相近的“相关度”方法,在狠多方面都与 形态学方法 极为相似。实际上,它们的工作原理几乎完全相同,都是,将一个邻居“内核”在图片中各处移动并做计算,这使得,它们成为了另一种特殊的形态学“方法”。

实际 上,它们使用的代码也狠相似。甚至,会使用与 基本内核 用户定义的内核 中定义的那些内核相同的内核。 欲了解那些针对此算子设计的更特殊的内核,(真的有好多),则,可参考 模糊化内核 边缘检测内核 。最重要的内核就是“ 高斯 ”内核。

但是,卷积,比形态学操作要古老得多,并且,它会产生出更多的灰度渐变效果,而不是形态学操作通常会产生的二分式形状跟踪效果。因此,它通常被认为是与形态学操作不同的操作,并且通常会被专注于用在图片处理上。

简单来说,卷积,或者,相关度计算,实际上就是,取得指定的邻居范围内的所有像素的“加权平均值”。也就是说,将附近每个像素的值与内核中指定的值相乘,然后,将所有的乘积相加,得到最终结果。

于是,最终图片中,每个像素,都会包含有源图片中在它周围的所有像素的一部分。换个角度来看,每个像素的颜色,或者是加上了它周围像素的颜色(模糊),或者是减去了它周围像素的颜色(锐化/边缘检测),具体是何效果,则由内核确定。

“卷积”与“相关度计算”是相同的操作,它们之间只有一个狠小却狠重要的区别,并且,对于我们目前要关注的那些示例及控制参数来说,妳可以认为它们是同一个东西。日后(参考 卷积与相关度计算的对比 ),我们会搞清楚,这两个操作究竟是在哪里不同,以及,它们为什么只有这么一丢丢区别。但是,在大部分情景下,它们就是同一个方法。

卷积 ()

前面已经说过 ,“卷积”方法,就是,根据内核中的浮点数值,将周围各个像素按照权值相加。然后将结果像素放置在结果图片中的相应位置。

举个例子,让我们来用一个小小的 用户定义的 卷积内核来对单个像素进行卷积操作。我还设置了特殊的 显示内核的选项 ,这样,妳就可以看到这里所定义及使用的内核的细节(这里显示的图片已被放大)。

  convert xc: -bordercolor black -border 5x5 pixel.gif

  convert pixel.gif -define 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

如妳所见,图片中的单个像素被扩展了,在周围产生了几个50%灰度的像素。

来看看,当内核的“原点” ( 这个例子中是就它的中心点 ) 被放置在原始图片中那个单个像素点的旁边的时候,只有那个单个像素点具有非0值。然后,该像素点被按照内核中的' 0.5 '值取得权重,最后产生的那个“半亮度”的像素被放置在结果图片中。

类似 地,当内核的原点被放置在原始像素上时,它会得到 ' 1.0 ' 的权重,产生的效果是,在邻居范围内,只有该原始像素,而没有其它值(黑),使得不会向结果中附加任何额外的像素。

注意,内核 中,值为 ' 0.0 ' 的部分,不会参与最终计算。0值,可以认为不存在于“邻居”范围内,这就类似于,形态学操作内核中, ' Nan ' 值不会起作用。因此,这个内核实际上由5个元素邻居组成。

从狠多方面来看,“ 卷积 ”方法都与形态学的“ 膨胀 ”方法类似,但是, ' 膨胀 '只是 将内核当成某种位图掩码,用来取得邻居范围内的最大值。而' 卷积 '呢,是将邻居范围内的所有值带权相加,这样,内核中的每个元素值都对最终结果起到一定的作用。

卷积操作的语法是……

-morphology Convolve { convolution_kernel }

但是,妳也可以使用一个较旧的,更直接的操作符……

-convolve { convolution_kernel }

如果 妳想看一些美妙的示例,以了解 ' 卷积 ' 的实际效果的话,则,建议妳参考 EECE \ CS 253 图片处理, 7 次演讲 , 立体卷积

维基百科,卷积 文章中,有一些漂亮的一维动画,展示了卷积过程。

卷积内核的缩放

上面的这个例子,对于大部分内容为黑色的图片(例如单个像素点)有狠好的效果,但是,如果妳用这个内核来处理实际图片的话,妳会遇到麻烦……

  convert logo: -resize 50% -crop 80x80+150+60 +repage  face.png

  convert 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

妳可以看到,结果图片变得非常的亮(实际上,其亮度达到了原来的3)

原因 是,每个像素都被分享了 3 次。在4个边缘,得到了 4 × ' 0.5 ' 的像素值,再加上对原始像素的完整复制。换句话说,内核中所有值的和是 3 ,使得,结果图片的亮度达到原来的3倍!

回过头去看看上面的'showkernel'输出内容,妳会发现,它已经告知,该内核,"卷积输出范围03"。这表明,这个内核,一般情况下会使得图片的亮度变成原来的3倍。

为了修复这个问题,妳可以将内核中所有的值都除以3。这样的话,原来的值 ' 0.5 '应当变成' 0.1667 ' ,而中心点的值 ' 1.0 '应当变成' 0.3333 ' 。这个过程被称作' 内核归一化 '。

例如,以下是一个手动'归一'的结果,以及内核定义……

convert face.png  -define showkernel=1 \

        -morphology Convolve \

                '3x3: 0.0,.1667,0.0  .1667,.3333,.1667   0.0,.1667,0.0' \

        face_spread_norm.png

可以看到,结果是,产生了一张稍微有点模糊效果的图片,原因是,每个像素都分散到周围的邻居中去了。

亲自对内核进行归一化的话,狠不爽的,并且,妳也可以看到,所产生出来的内核定义,比较难以理解。因此,我们提供咯替代手段。

IM v6.5.9-2 开始 ,引入了一个特殊的砖家选项 " -define  convolve:scale={ kernel_scale } ' ,它使得妳能够针对内核指定一个全局的缩放因子,于是就可以调整整张图片的亮度了。

convert 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

这个参数的实际作用是,调整内核结果的整体亮度。日后的示例中,妳会发现,可能需要对卷积结果做各种各样的调整。'kernel_scale'因子就可以做到这一点。

内核归一化(自动缩放)

其实 不需要亲自计算出这个缩放因子 ( 如上所示 ) ,妳可以让 IM 来为妳计算出这个 ' 归一化缩放因子 ' ,具体做法就是,指定一个特殊的 ' ! '归一 化操作标志。

convert face.png  -define convolve:scale=\! \

        -morphology Convolve  '3x3: 0,1,0  1,2,1  0,1,0' \

        face_spread_normalize.png

在多种UNIX 命令行外壳中, ' ! '字符 都被用作特殊用途。因此,妳可能需要使用反斜杠来对它进行转义,甚至当它位于引号中也是如此。请注意。

现在,既然内核会被归一化,我就可以以更简单的方式来定义内核,例如使用整数。归一化之后的内核,将会与之前“缩放”过的内核相同。

一般情况下,妳应当总是对内核进行归一化,由于这一点,更简单的那个 " -convolve "选项 中,会自动进行这种归一化操作。

妳可以让IM对内核进行归一化,然后仍然给它设置一个缩放因子,以调整输出图片的亮度。为了让这个 操作更简单,妳甚至可以以百分比来指定缩放因子。

例如,下面示例中,我将内核归一化了,然后将计算结果缩放到了50%,产生出咯一张较暗的图片。

  convert face.png  -define convolve:scale=50%\! \

          -morphology Convolve  '3x3: 0,1,0  1,2,1  0,1,0' \

          face_spread_norm_half.png

注意,使用' ! ' 这个值,实际上相当于使用 ' 1! ' 或者甚至是 ' 100%! ' 。甚至 ,如果妳想将内核中的正数和负数值反转的话,妳还可以使用一个负数的缩放因子。参考 使用模糊功能来将图片“反锐化” 以观摩相应的示例。

如果 妳是使用这种方式将内核归一化的,则, 显示内核 的输出内容会告诉妳说,它已被归一化。

归一化具体是如何进行的

' 内核归一 ' 的实际工作过程,是,将所有的内核值相加 (包含任何 的负数,它们也是可能出现的 ) 。如果结果不为0,则,将所有的值进行缩放,使得,它们的和为1(' 1.0 ')。

注意,如果 妳的内核中有负数值,则,这个过程中创建出来的内核,可能有某个值大于1,并且一般是在原点处大于1。尤其对于 反锐化 内核容易发生这种事。但是,关键点在于,这个内核中,各个值的和为 ' 1.0 ' ,因此,通过 卷积 操作得到的最终图片,不会比原图暗,也不会比原图亮。

如果各个 值相加的结果为0 (' 0.0 ') ,则,该内核被认为是一个特殊的 零和内核 在那种情况下,会对该内核进行缩放,使得,所有的正数值的和为 ' 1.0 ' ,在这种过程中,就会使得,所有的负数值的和为 ' -1.0 ' 这种内核,在 边缘检测 技术中狠流行。

如果内核 是这种形式的,则 显示内核 的输出内容也会表明它是零和的。即使它并不是一个零和的归一化内核,也可以通过所显示的其它数字轻易看出来。

大部分以数学公式计算出来的内核都是预先就做过归一化处理的。包括以下这些通过数学公式推导的内核: ' 统一 ', ' 高斯 ', ' 拉普拉斯高斯 ', ' 高斯 差分 ', ' 模糊 ', ' 彗星 '

而,那些离散常数内核呢,并不是预先归一化的,所以, 妳需要使用 内核归一化选项 (上面说明过)来做这一步。包括以下内核: ' 拉普拉斯 ', ' 索贝 ', ' 罗伯茨 ', ' 雷威特 ', ' 罗盘 ', ' 柯斯 ', ' 弗雷陈 '

注意,' 弗雷陈 '内核 ,其中包含着一些针对特殊目的而预先调整过权值的子类型。 弗雷陈内核应当原样使用,不应当做归一化操作。

零和归一

并非所有 的卷积内核都只使用正数值的。妳也会用到一些既包含正数值也包含负数值的内核,并且,通常情况下,这些值的和会是0,以构成一个 零和内核 。这种类型的内核,在更高级的图片卷积中非常重要,因为,它们提供了 边缘检测 图片锐化 技能。

上一小节中已经提到,通用的归一化标志 ' ! '对于 这种内核也起作用。但是, 有些时候,因为一些特殊因素的影响,妳需要确保,该内核是保持在“零和”状态的。

特殊的 ' ^ '归一 化方法,提供了一种手段,用于在以下情况下仍然确保内核是“零和”的……

  1. 1. 用户定义 的内核,不是足够精确的零和。例如,妳无法使用一个精确的浮点数来表示 ' 1/3 ' 或任何 3 为分母的最简分数。

  2. 2. 数学曲线 会被内核的尺寸(半径)所“裁剪”,于是,可能不再保有零和特性。例如,' 拉普拉斯高斯 '或' 高斯差分 '内核就是这样的,因为它们是基于无限响应曲线的。正是由于 这个原因, IM实际 上会在内部针对这些内核用上这种特殊的归一化选项。

  3. 3. 确保 一个 相关 度计算 '形状掩码' 是零和的,这样,在搜索过程中, IM 可以同时寻找正数和负数匹配值。参考下面的 相关度形状搜索

具体 发生了什么呢?实际上,它会将内核中的正数和负数分别对待,分别进行归一化。也就是说,所有的负数都会经过缩放,使得其和为 ' -1.0 ' ,所有的正数也会经过缩放,使得其和为 ' +1.0 ' 。结果 就是,内核,作为一个整体,其中各个值的和为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 '

最后 的这一步,其效果是,向之前生成的经过归一化和缩放的内核上“添加”了一个具有指定“缩放” 因子的 统一内核

这就产生了具有以下功能的内核……

注意,如果 妳指定了百分比 (' % ')标志, 则,会对' 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

加上一个统一内核(向原始值加上100%)

0

-0.125

0

-0.125

1.5

-0.125

0

-0.125

0

于是,妳可以将' Lapl acian:2 '用作一个锐化内核来进行卷积了,但是,其锐化强度只是'50%

记住, 在缩放因子的任何位置指定了 ' % '标志, 都会使得两个值都变成以百分比为单位。如果未指定,则,两个值都是表示简单的直接乘法关系。例如, 以下这些缩放选项都是等价的

     50,100%     50%,100    %50,100      .5,1      0.5,1.0   

对于两个归一化标志位,也是同样的规则。它们可以出现在卷积的缩放因子的任何位置,但是,在让任 何的其它缩放参数生效之前,都会先应用这两个标志位。

输出结果的倾斜程度控制

当妳使用一个包含负数值的内核的时候,结果图片中的某些像素可能会被赋予一个负数值。对于 零和内核 (下面 会说到 )来说尤其 是如此。

遗憾 的是,除非妳专门编译了一个 HDRI版本的ImageMagick ,以保留在卷积过程中产生的负数值,否则的话,任何负数结果值都会被裁剪为0 (黑色) 妳只会得到卷积过程中产生的正数结果值。 它就是无法用普通的图片格式来保存,这样,就使得,妳只得到一半的结果。

妳可以编译出 HDRI版本的ImageMagick ,以保留在卷积过程中产生的负数值,然后再提取出妳想要的信息。或者 ,也可以使用负数的缩放因子来将内核反转。例如,使用以下参数……

-define convolve:scale='-1'

但是,这样的话,妳就只得到负数的结果了,而将正数的结果裁剪掉了。

但是,通过使用 IM" -bias "选项 ,妳仍然可以同时保留正数和负数结果。

针对非HDRI 版本的IM 所使用的选项是……

     -define convolve:scale=50%\!  -bias 50%

第一个参数,将输出结果相对于妳在一般情况下得到的结果缩放了一半(当然是在做了归一化之后),于是就为正数和负数结果值都留下了空间。然后,在将结果回写到一张图片中去之前,它会向输出的各个像素上加上一个50%的灰度值。

使用 了这些选项之后,任何一个“为0”的结果值,都会被显示成纯灰色,而负数结果值会比这个灰色要暗,正数结果值会比这个灰色要亮。黑色会代表 ' -1.0 ' ,而白色会代表 ' +1.0 '

在下面的 相关度形状搜索 示例中,有一个这方面的例子。

使图片模糊 ( 低通滤波 )

IM 示例 中的另一个方向,特别是 模糊化和图片锐化 ,是与此主题的实际应用相关的。下面让我们来看更多细节。

首先,我们还是要说明一下那些基本的内核,以及,妳可以如何在不修改它们的情况下直接使用。日后,我们会学习,如何修改其中的模糊程度,以产生其它效果。

模糊化内核

统一

这是一个特殊的内核,它实际上什么都不做。只指定了一个内核元素,这样,所以产生的效果是,每个像素都被它自身所替换,没有任何改变。

例如,以下就是一个空操作 卷积 ……

  convert face.png -morphology Convolve Unity  face_unity.png

IM v 6.6.9-4开始,这个内核可以带一个单个的参数,用来指定与这个内核相关的缩放因子。这样,妳就可以用它来对图片中的值做一个乘法,其效果就是,让一张图片变亮或者变暗。

  convert face.png -morphology Convolve Unity:0.5  face_dimmed.png

初看起来好像没什么用,但是,它实际可以用于以下情景:生成 软模糊 反锐化 效果;或者,用于一个无法使用 内核缩放 内核标识混合 的多内核序列中。

这种只有单个元素的内核,也可以使用' Disk:0.5 '来生成,它也允许妳在生成内核的过程中设定一个缩放参数。 (例如 :上一个例子中,对应的参数是 ' Disk:0.5,0.5 ')

也可以使用' 高斯 '内核生成器来生成一个类似的内核(用于 卷积 ),将' sigma '参数设置成' 0.0 '即可。但是, 它只能产生一个小小的 3x3内核 ,由中间的1个 ' 1.0 ' 和周围的 8 ' 0.0 '组成

使用带形状的内核来实现均值滤波

尽管 ,以下所定义的大部分卷积内核,一般都会以某种程度用到高斯曲线,但是,妳仍然可以使用之前所定义的某个 形态学内核 来在某个指定的(大)区域中将那些像素平均化。当然 妳需要将该内核 归一 这样才会真正起到平均化的作用, 而不是简单地将周围像素点进行相加。

例如,下面示例 中,我用了一个较小的 ' 八边形 '形状内核 ,用来,将每个像素周围的一个圆形区域内的像素值平均化。

  convert face.png  -define convolve:scale=! \

          -morphology Convolve Octagon:2  face_mean.png

结果就是,每个像素的值,会在所定义的邻居范围的25 个像素上平均分散。也就是说,它相当于,在指定的形状中,做一个“均值”滤波。

如果 妳想要在平均效果中排除掉原始像素,只使用周围的那些像素的话,则,可以使用 ' '内核(半径设置 为1 )

也可以用相同的方式来使用其它的 形状内核 ,以实现,按照' 菱形 '、' 正方形 '或' 碟形 '来针对妳想要的任何尺寸来对像素值进行均值化。

然而 ,尽管,根据带形状的区域进行的常量均值化会对图片产生模糊效果,但是,它还会倾向于产生一些特殊效果 (尤其 混淆伪影 )

更具体地说,使用一个'平整'均值化内核,会倾向于,将尖锐的边缘转换成厚度较大的线性坡,并且,在这个坡中,变厚的边缘处会有突变。

结果 的厚度会是, ' radius*2-1 ' 。至于 ,不同的边缘角度是如何影响坡的厚度及坡的线性特性的,则,取决于所用的“平滑”或均值化内核。

  convert -size 80x80 xc: -draw 'polygon 15,15 15,65 60,15' shape.png

  convert shape.png \

          -define convolve:scale=! -morphology Convolve Square:5 \

          shape_mean_square.png

  convert shape.png \

          -define convolve:scale=! -morphology Convolve Disk:5 \

          shape_mean_disk.png

注意,上图中,对角线的模糊情况,对于正方形内核和碟形内核是不同的。

另外 一种生成正方形的“线性斜坡”模糊效果的方式是,使用特定的半径和巨大的和值。例如,上图的正方形内核卷积效果,也可通过 -blur 5x65535 来实现。在形态学操作可用之前,Fred Wienhaus 通常会在他的脚本中使用这个参数。

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

妳可能已经感受到了, '高斯'内核 是在图片 卷积 中最常用的内核。 从数学方面来看,这是用来产生模糊效果的最理想的内核。

例如, 以下是对于一个小(它们可以狠快变得狠大)的'高斯'内核的 显示内核 选项的输出结果……

  convert xc:  -define showkernel=1 \

               -morphology Convolve:0 Gaussian:0x0.8  null:

在上面的例子中,我并不想真正做一次卷积操作,我只是想显示出它即将使用的内核。因此,我使用了' :0 '作为 迭代次数 ,于是它什么也没做。类似地,我使用特殊的' null: '文件格式来丢弃了结果图片。

妳可以从卷积输出范围上看到, '高斯'内核已经 被归一化 ( 已缩放 ) 。但是,妳仍然可以发现,它还是一个巨大的内核,其中充满了小的小数值。 如果仔细看一下,妳会发现,最大值 ( 在输出内容的第一行也说明了这个值 )位于 正中间, 而那些最小的值呢,都位于边缘和角落处。

以下是典型的使用卷积来进行高斯模糊的例子……

  convert face.png   -morphology Convolve Gaussian:0x2  face_gaussian.png

这个内核的语法狠直观……

   Gaussian:[{ radius }]x{ sigma }

这些参数,实际上与" -gaussian-blur "操作符所使用的参数完全相同,而,该操作符,实际上就是使用这个内核来进行 卷积 操作的。

第一 个数值,就像大部分 形态学内核 一样,指的是内核的' 半径 '或尺寸。它只是一个整数,最小值是 1 ,使得,可能采用的最小的内核就是 3x3 个元素的尺寸。对于 这个参数,最佳取值是0,这将使得 ImageMagick按照 所提供的' sigma '值来计算适当的半径。

第二个,也是更重要的参数是,'sigma',它定义的是,模糊程度有多大,或者说,每个像素将会被“分散”到多大的范围。这个值越大,图片就越模糊。它是一个浮点值。 sigma 参数 必须 被提供

如果 sigma 值设置为 ' 0.0 ' ,则,会产生一个没什么卵用的 ' 统一 '内核(当然 ,具有指定的半径,或者半径是 1 ,于是,产生一个 3x3 的内核,中间是一个 ' 1.0 ' 值,周围是 ' 0.0 ' 值。 ) 。正如 妳上面所看到的,使用任何类型的' 统一 '内核进行卷积,实际上相当于 什么都不做

如果 妳要指定一个 ' radius ' 值的话,那么,一般情况下,较好的值是最少 ' sigma ' 值的两倍, IM通常 会计算出大约 3 倍大小的半径 (实际 上,这是能够产生有意义的结果的最大的半径 ) ,当然,具体的值还取决于在安装IM 的时候设定的 编译 期质量

欲知更多关于'高斯'内核各个参数 的效果的信息,以及常规的图片模糊操作的信息,则参考…… 图片模糊

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

'模糊'内核 高斯内核 非常类似,甚至还使用相同的参数 (参考下文) 。区别 在于,高斯内核产生的是一个 2 维曲线,而 '模糊'内核 产生 的是一个 1 维的曲线。也就是说,它产生的是,长长的一个单行值。

以下是对于一个小的'模糊'内核的 显示内核 选项的输出内容。

  convert xc:  -define showkernel=1 \

               -morphology Convolve:0 Blur:0x0.8  null:

上面图片中显示的是,默认的' Blur '内核的实际特征曲线。 它是如下产生的:使用 内核图片 脚本" kernel2image ",然后,将该图片使用" im_profile "脚本来绘制出来。 它明显地显示出了此内核所代表的'高斯钟形曲线'。

以下是使用这个内核来在水平方向上对图片进行模糊操作的示例。

  convert face.png -morphology Convolve Blur:0x4  face_blur.png

这个内核的语法,与' 高斯 '的语法相同,但是,多了个额外的可选参数,即,旋转角度。

   Blur:[{ radius }]x{ sigma }[,{ angle }]

就跟前面说的一样,第二个参数值 ' sigma ' 是必须提供的,并且,如果它的值被设置为0,则,将产生一个线性效果,与' 统一 '内核等价。

'angle'参数使得妳可以以90度为单位来对该内核进行旋转,这样,就可以在竖直方向上对图片进行模糊化操作了。

  convert face.png -morphology Convolve Blur:0x4,90  face_blur_vert.png

目前,只支持以90度为单位的旋转。在日后版本的ImageMagick 中,可能会发生改变。

这个内核的目的,实际上是,产生出比' 高斯 '内核更快的 2 维图片模糊操作。参考后文的 高斯内核与模糊内核的比较 ,以了解,为什么可以实现这一点。

彗星内核( 半个 1 维高斯模糊 )

'彗星'内核几乎 ' 模糊 '内核相同 ,但是,实际上,它是模糊内核的一半。

  convert xc:  -define showkernel=1 \

               -morphology Convolve:0 Comet:0x1.0  null:

注意,此处定义的原始点的位置是处于左侧边缘处,而不是位于内核的中心点。对于一个卷积内核来说,这是一种奇特的特性,因而也产生了狠奇特的结果。

它会朝向一个方向来对图片进行模糊化,看起来,就像是,用一个手指揉搓一幅湿湿的油画,造成了一条拖影。它看起来有点像是彗星的尾巴,或者说是流星的尾迹。

  convert face.png -morphology Convolve Comet:0x5  face_comet.png

注意,前景和背景颜色同时被“揉搓”了。如果妳只想对前景进行模糊操作的话,则,让背景变成透明。的

妳还可以提供第三个参数,即为 角度参数,以实现,以其“原点”为中心,按照90 度为单位来旋转该内核。

  convert face.png -morphology Convolve comet:0x5+90  face_comet_vert.png

这个内核,实际上与特殊的 运动模糊 操作符所使用的内核相同, 不过,那个操作符中,还会进行一些高端的坐标查表操作,使得可以在任意角度上实现模糊操作。 不过,它做得并不好,会在角度较大的时候产生“色块”,例如 45 度角。

但愿,日后,会有合适的内核旋转算法,使得,不以90 度为单位的旋转角度下,运动模糊也能产生良好的效果。

高斯曲线

高斯曲线 ,是一个经典且完美的密度函数,它的特点是,曲线下方的面积为1。而'sigma'值,表示的是,曲线上那个值为峰值50%的点与中心点的距离。

sigma的值越大,曲线的宽度就越大,于是,中心点周围的“邻居范围”就越大。同时,sigma的值越大,每个单独的点被分散出去的范围就越大。如果这个值非常大(几乎是无限),则,它会产生一个全局平均效果,或者说是背景“均值化”。

'半径'只是限制了整个邻居范围与该像素的距离,更多地是一个'内存'及处理速度因素。

日后 :展示一个图像示例

如果妳必须设置这个值的话,那么,请将它设置为sigma值的2倍,否则,请将它设置为0,以让IM自主确定一个合适的值(在内部可能会狠大)

它的独特之处是,当妳用一个对焦失败的镜头来拍照时,所得到的照片就是这种效果,因此,它经常被用于图像处理。

高斯内核与模糊化内核对比

之前已经说过 ' 高斯 ' ' 模糊 '内核之间 有狠深的联系,并且,它们实际上可以完成同样的功能。它们 都是对于 高斯曲线 的表现形式,前者是一个 2 维的表现形式,而后者是一个 1 维的表现形式。

例如, 我们在下面重复了一次 " -gaussian-blur 0x2 " ,它等价于 " -morphology Convolve Gaussian:0x2 "操作

  convert face.png    -gaussian-blur 0x2      face_gaussian-blur.png

它可以用两个相差90度的单独的 线性 1维模糊化操作来替代(顺序不重要)……

  convert face.png -morphology Convolve Blur:0x2 \

                   -morphology Convolve Blur:0x2+90  face_blur_x2.png

妳不需要执行两次单独的卷积命令,只需要将两个内核以内核列表的形式给出即可。例如

  convert face.png -morphology Convolve 'Blur:0x2;Blur:0x2+90' face_blur_x2.png

默认情况 下, IM 会对第一个卷积内核的结果进行 '重新遍历' ,在这个过程中会用上第二个卷积内核,这一点是在 多内核合成 选项中定义的。

妳甚至还可以更过分地简化上面所说的命令,具体地就是,要求 IM 将一个内核扩展为一个 旋转内核列表 ,具体做法就是,使用 ' > ' 来构造出一个90 度旋转的内核列表( 在这个例子中是两个内核 )。例如……

  convert face.png -morphology Convolve 'Blur:0x2>' face_blur_x2.png

上面的各个示例之间是等价的,也展示了" -blur "操作符是如何工作的。

  convert face.png    -blur 0x2      face_blurred.png

这个例子展示了" -blur "和" -gaussian-blur "操作符之间真正的差异。 在后者之中,使用了单个较大的 2 维内核,而在前者之中,使用了两个较小的 1 维内核。

然而 ,从速度的方面来看, " -blur "操作符通常 会快得多,因为,它使用的是两个小得多的内核,而不是一个巨大的内核。模糊参数越大 ( sigma 参数 的大小 ) ,内核就越大,于是,两种操作符之间的速度差距就越大。因此,通常建议使用" -blur "操作符。

这两种操作符的结果之间唯一的差异就是:微小的量子舍入效应 (除非 妳使用 HDRI) 和边缘效应 (取决 虚拟像素选项 ) 这两种效应的原因是同一个原因,就是,在保存两次单独的'模糊'卷积过程之间的中间图片时,发生了信息丢失。这种差异一般是非常小的,以至于根本就不可见,在实际使用过程中可以忽略不计。

柔化模糊( 与原始图片进行混合 )

通过将模糊化之后的结果与原始图片进行混合,妳可以柔化模糊操作的影响。尤其是当妳在使用一个狠强的模糊操作时,更是如此。例如……

  convert face.png  -morphology Convolve Gaussian:0x3  face_strong_blur.png

  convert face.png  face_strong_blur.png \

          -compose Blend -define compose:args=60,40% -composite \

          face_soft_blur.png

这个例子中,使用了 ' 混合 ' 这个合成方法,将模糊化之后的图片(合成操作的源图片)的 ' 60% ' 与原始图片(合成操作的目标图片)的' 40% '进行混合,在最终图片上产生了一个'柔化模糊'效果。

然而 ,妳可以用另一种方法来直接实现同样的效果,即, 将该内核与标识内核进行混合 ,并且使用相同的比例。

  convert face.png -define convolve:scale=60,40% \

          -morphology Convolve 'Gaussian:0x3' face_soft_blur2.png

注意,缩放数字 的顺序是相同的。 第一个数字 (' 60% ') 对指定的内核进行了缩放,于是削弱了它在输出结果中的影响,而第二个数字 (' 40% ')加上 了足够份量的 ' 统一 '( '唯一')内核 ,以避免结果图片变暗。

重要之处 在于,对于 模糊 化内核 ,这两个数值相加应当等于 ' 100% ' 这就跟 合成混合 一样。

妳也可以使用更快的 2 遍模糊化操作,但是,在这种情况下,我们无法直接对该内核应用一个 '混合'效果 ,因为,这两个单独的卷积操作并不是完全 '独立' 的。因此,我们仍然需要在后面再次进行 混合合成

  convert face.png \( +clone -blur 0x3 \) \

          -compose Blend -define compose:args=60 -composite \

          face_soft_blur3.png

注意, 妳只需要指定,要以模糊(源)图片的多大比例来与原始图片进行混合。因此 ,如果值为 ' 100 ' 则会完全保留模糊化图片,而值为 ' 0 ' 则会完全完全保留原始图片。

使用模糊化操作来将图片'反锐化'( 从原始图片中减去特定值 )

现在 让我们来更进一步研究内核的混合,妳可以试着使用一个负数的缩放值,这样产生的效果是,从原始图片中减去模糊效果。所产生的结果,被称作“反锐化”。参考 Unsharp, Wikipedia ,以了解,它为什么被人起了这么个不幸的名字。

  convert face.png -define convolve:scale=-100,200% \

          -morphology Convolve 'Gaussian:0x2' face_unsharp.png

注意,尽管 这里使用了一个负数的内核缩放因子,但是,所给出的两个数字仍然要保持其和为 ' 100% ' ,就跟前面的例子一样。妳也可以对 合成混合 使用这种数值。

上面这个例子,实际上与命名错误的" -sharpen "操作符所做的事完全相同,不过,它只对 ' sigma ' 这个模糊参数进行控制。而不提供对于该操作的其它参数的控制。其混合效果与上面所说的完全相同。

妳也可以使用更快的 2 遍的 1 模糊 化内核 ,但是,同样地,妳需要将混合操作作为一个单独的步骤来进行。

  convert face.png \( +clone -blur 0x2 \) \

          -compose Blend -define compose:args=-100,200 -composite \

          face_unsharp_fast.png

这个小节,所讲述的东西,几乎与 柔化模糊 中的内容相同,唯一区别就是,模糊化之后的图片是从原始图片中减去,而不是加上。 有一种混合方法叫做 外推混合 ,它是超出了一般的0到100百分比范围的混合。同样地,妳可以简单地指定,要将模糊化图片中多大的份量从原始图片中减去。例如, 我们在这里过度地进行图片的反锐化,使得,产生一些锯齿和颜色失真效果。

  convert face.png \( +clone -blur 0x2 \) \

          -compose Blend -define compose:args=-200 -composite \

          face_unsharp_200.png

整的 " -unsharp "操作符 ,提供了另一种控制手段。尤其是,提供了一个差值阈值,这样,只有当特定的差值较大时才会进行锐化操作,例如,在图片中实际边缘附近的差值就会比较大。这个阈值,可以用来避免对象较小的局部缺陷图案的 '锐化' ,例如皱纹或者相机噪声。

图片 的反锐化,一般是在对图片进行尺寸变更或者扭曲之后,使用狠小的模糊化(sigma=0.75这个级别)操作来进行的,用来改善最终的结果。参考 对改变了尺寸的图片进行锐化 ,以了解这方面的一些示例。

除了“反锐化”技巧之外,还有一种用来对图片进行锐化的技巧,就是,实际检测出图片中的边缘,并且利用这些边缘来对图片进行锐化。参考下面的 利用边缘检测来进行图片锐化 以了解细节。但是,这种做法一般会比较慢,因而用得不多。

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

边缘检测,是另一个重试使用卷积的领域。

此处要实现的事情是,以各种手段来高亮显示或者增强一张图片中的边缘。要做到两件事中的一件:尽可能精确地定位到一条边缘;或者,确定每条边缘的角度或者说是坡度方向。

但是,这项工作会因为图片中的噪声而变得困难重重,例如以下噪声:扫描仪造成的噪声;数码相机造成的噪声;甚至JPEG 图片文件格式本身的有损压缩算法也会产生噪声。

然而,一般来说,较大的内核能够较好地处理好噪声的问题,但是会损失掉定位边缘的精度,而较小的内核呢,会产生出锐利的边缘定位结果,但是,会因为噪声而检测出较多的假边缘。

有狠多小巧而出名的内核,它们被开发及研究以用来做边缘检测。它们大部分是以研究相关数学知识或开发了相应内核类型的数学家的名字来命名的。因此,妳会遇到名为' 拉普拉斯 '、' 索贝尔 '和' 普雷威特 '的内核。 这些著名的内核,一般都非常小,而且是用整数来定义的,这样,它们就可以内置于专门设计的优化过的软件和硬件中,以提升速度。也就是说,它们是“离散”内核。由于这个原因,妳在使用它们的过程中,需要 缩放 归一 该内核。

边缘检测还有个副作用,就是,它提供了一些手段,可用来增强图片中的边缘。

零和内核

所有的边缘检测内核,都有一个共同的特性。它们都是零和的。这就意味着,它们包含着负数值,但是,内核中所有值的和为0。

对于 一个平滑且平整的单色图片,使用这种 卷积 内核所进行的操作,会产生一个全“零”或者说全黑的图片。然而,对于任何其它类型的图片,在其结果中,都会既有负数值也有正数值。

例如,下面 的例子中,我针对一个包含着若干基本形状的图片使用了一个离散的' 索贝尔 '边缘检测算子……

  convert -size 80x80 xc:black \

          -fill white -draw 'rectangle 15,15 65,65' \

          -fill black -draw 'circle 40,40 40,20'       shapes.gif

  convert shapes.gif  -define convolve:scale='!' \

          -morphology Convolve Sobel  shapes_sobel.gif

如果 妳仔细看一下结果,妳会发现,这个内核是带有方向性的,它只检测到了竖直方向上的边缘(在 ' 索贝尔 '内核 的定义中角度为0)。实际上,它只检测到了部分边缘,即,“正向的”从左向右的从黑到白的坡度。

要检测到“反向的”坡度,则,妳需要将内核反转,也就是利用 内核缩放选项 。例如……

  convert shapes.gif  -define convolve:scale='-1!' \

          -morphology Convolve Sobel  shapes_sobel_neg.gif

对于 ' 索贝尔 '内核 ,妳也可以将它旋转 180 度,以产生与“缩放负数”等价的效果,但是,并非所有的内核都有这种等价特性。

另一种解决方案就是,向结果中添加一个 输出扭曲 。也就是,向结果图片中加上 50% 的灰度值,这样的话,负数值就会比这个值稍微亮一点,而正数值会亮得多。然而,妳还需要 缩放该内核 ,以确保,结果图片保持在一种“未裁剪”状态,即,它的值位于图片的“黑色”和“白色”限制范围内。

  convert shapes.gif  -define convolve:scale='50%!' -bias 50% \

          -morphology Convolve Sobel  shapes_sobel_bias.gif

如果妳不在乎这个过程中的极化效果,那么,妳可以在结果中使用绝对值,只是要稍微变暗……

  convert shapes.gif  -define convolve:scale='50%!' -bias 50% \

          -morphology Convolve Sobel  -solarize 50% -level 50,0% \

          shapes_sobel_abs.gif

参考' 索贝尔 '内核 ,以了解更多结果处理技巧,尤其是,如果进行方向的确定。

除了使用 输出扭曲 之外,另一种解决方法就是,编译一个特殊的 HDRI 版本的Imagemagick。 这种版本,会在内存中以浮点数值来存储图像信息,这就意味着,图片中的那些值不会因为使用整数而被“裁剪”或“舍入”。

然而,即使是使用这种特殊版本的IM,妳仍然需要在将数据保存为一个普通图片文件格式之前对它进行后期处理,否则,妳就需要使用一种特殊的支持浮点数的图片文件格式。不过呢,妳不需要担心在中间的图片结果中会发生裁剪或舍入效应,这多少会让事件变得简单点。

边缘检测内核

LoG:拉普拉斯高斯

   LoG:{ radius },{ sigma }

' LoG ' ,或者说, "拉普拉斯高斯" ,是妳如今能够找到的最好的边缘检测内核之一。它也被称作 "墨西哥草帽"内核

本质 上,它是一个 ' 拉普拉斯 ' 差分 (斜面)操作符 ,而它本身已经通过加上高斯模糊而被平滑过了。这反过来又去除了图片中大部分噪声的影响,而这种去除效果还可以通过 ' sigma '选项 来调整。

这个内核中包含了一些负数值,它们在中心点的峰值周围形成了一个环。在上面显示的'内核图片'中,负数值被显示为暗色(几乎纯黑),而边缘会退化成零值(暗灰)

下面就是它的效果……展示了,它如何将图片中的边缘给高亮的。

  convert face.png -bias 50% -morphology Convolve LoG:0x2  face_log.png

拉普拉斯内核 ,是无方向性的,但是,对于任意尺寸的边缘,它都会产生出正数和负数的山脊值。要想定位到边缘,妳需要查找过零点,也就是正数山脊和负数山脊之间的点,这个技巧被称作 马尔和希尔得斯边缘检测

这个内核也可用于 将图片锐化

DoG:高斯差分

   DoG:{ radius },{ sigma1 }[,{ sigma2 }]

这条命令会生成一个 ' DoG '或者 说是 "高斯差分"内核 ,具体地,由' sigma1 '生成的高斯内核会减去由' sigma2 '生成的高斯内核。 一般情况下, ' sigma2 ' 是较大的那个,这样, ' 中心的峰值 ' 就是正数。将两个数字交换位置的话,会将结果内核反转。

拉普拉斯高斯 遇到 的主要的批评是说,它难以实现,因为它是一个狠不平常的数学曲线。同时,该曲线也没有较好的文档。另一方面,它无法被 '分割' 为更快的 2 遍的解决方法,这与高斯内核不同 (参考 高斯内核 与模糊内核对比 )

然而 ,通过生成两个具有微小差异的 sigma 值(其比例大约为1.6)的' 高斯 '内核,两将它们相减,妳就可以实际产生一种与 拉普拉斯高斯 狠近似的效果。

比较结果 就是, ' 高斯差分 ' 更容易由硬件来生成,比' LoG '内核容易得多。

例如, 以下,我将一个' LoG '和一个' DoG '内核的 内核图像 并排放在一起进行比较。

如果 妳去看看 高斯差分,维基百科 网页的话,妳会看到一些图像,它们通过图像来比较 ' LoG '(或者 "墨西哥草帽") ' DoG ' ,以展示出,两者的匹配曲线之间只有微小的差异。

所产生的结果也是非常的相似。

  convert face.png -bias 50% -morphology Convolve DoG:0,1.8,2.4  face_dog.png

注意,两个'sigma'值都应当定义,并且,至少一个值不为0。如果任一个sigma分量的值为0,则,其效果会等价于一个' 统一 '内核,意味着,它不会对图片做任何修改。如果两个值都是0,则,这两个高斯内核都会成为' 统一 '内核,然后,当它们相减时,会产生一个完美的零值或者黑色结果(当然会加上任何扭曲值)

如果参数'Dog:0,0, non-zero ,那么,这个DoG内核会成为一个简单的高通滤波内核,其定义为,一个'统一'内核(产生原图片的完美副本)减去一个低通滤波内核(将图片模糊)。在这种情况下,sigma1=0就是定义了一个'统一'内核,而sigma2=non-zero 就是定义了一个高斯低通滤波(模糊)内核

因此,下面的示例,就会产生一个高通滤波图片,其中,过滤值为sigma2=2

  convert face.png -bias 50% -morphology Convolve DoG:0,0,2  face_dog_unity.png

注意,使用' DoG:0,2,0 ' 的话,所返回的图片基本上(针对输出扭曲进行了舍入)会是之前图片的反转版本。

这种技巧,也可以用来生成一个 3x3 ' 各向同性拉普拉斯 '内核 ,即,一个在所有方向上都产生同等结果的 ' 拉普拉斯 '内核 ,而不会产生不等同的对角线扭曲。

例如,radius=1(针对一个3x3的内核)sigma值为1,会生成……

  convert face.png  -define showkernel=1 -bias 50% \

          -morphology Convolve DoG:1,0,1  face_laplacian_isotropic.png

使用"高斯差分" 的另一个原因是,妳可以使用快得多的 " -blur "操作符( 其内部使用的是 ' 模糊 '内核), 它会产生相同的结果。然而,要想实现这一点的话,妳需要分别生成两张 '模糊 '图片 ,然而将结果相减,并且要注意使用一个适当的缩放及扭曲选项。

例如……

  convert 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

上面的例子中,使用了特殊的 数学合成 来避免在非 HDRI 版本的IM 中进行图片相减时出现的“裁剪”。欲知更多细节,则参考 加入扭曲渐变

仅有 的另一个因子是,在相减过程中使用的一个较大的缩放因子 ( 数学合成 参数中的两个 ' 4 ') 这是因为,将两个归一化的模糊内核相减,其产生的范围,与妳在' 高斯差分 '内核中将两个相减后的高斯曲线进行归一化后得到的范围不同。

然而 ,除了范围之外,上面这个例子,与第一个' 高斯差分 '内核的结果是等价的,但是它会快得多,尤其是,对于较大的 values 值更是如此。这就是关键点,尽管做了更多工作,但是,这种复杂的方法,比直接使用' 高斯差分 '或' 拉普拉斯高斯 '内核要快得多。

离散拉普拉斯内核

   Laplacian:{ type }

在大量的科学研究论文中,人们发布了大量不同形式的小型"拉普拉斯内核"。下面,我提供的是,我能够在学术期刊中找到的那些较常见的已内置的内核。

这些内核,基本上都是使用一个 ' 拉普拉斯高斯 '内核生成 的,但是经过了缩放,以便在一个小的内核数组中使用离散的整数值。 这就使得,妳可以使用生成的专用的快速图片滤镜,它们只使用整数计算来对图片数据进行处理。然而 ImageMagick 是一个比较通用的图片处理程序,因此,并不提供这种超快的专用滤镜。人们能够理解这一点。

这里提供的内核,都不可旋转,并且大部分都是 '各向异性 ' ,即,它们不是完美的圆,尤其是在对角 线方向会有较大差异。但是, 妳可以参考前一小节 (' 高斯差分 '内核) ,以了解一种生成真正的"各向同性的3x3拉普拉斯内核"的方法。

最前面的两个, ' Laplacian:0 ' ' Laplacian:1 '内核 ,是最常用的 "离散拉普拉斯内核" 。它们 狠小,意味着,它们能够精确地定位到边缘,同时,也倾向于会将图片中的噪声增强。

注意,并非所有'类型'数字都是已定义的,这就保留了一些空间,以便在日后定义更多的离散内核。已经使用的那些数字呢,是经过精心选择的,可以较好地匹配由那个数字定义的内核。

Laplacian:0  (默认)

8个邻居的拉普拉斯内核。可能是最常用的离散拉普拉斯边缘检测内核。

以下,我使用 显示内核 来显示出所用的“离散”“未归一化”内核,然后才使用 输出扭曲 来显示出归一化内核的结果。

  convert xc: -define showkernel=1 -precision 2 \

          -morphology Convolve:0 Laplacian:0 null:

  convert face.png -define convolve:scale='!' -bias 50% \

          -morphology Convolve Laplacian:0   face_laplacian_0.png

有些时候,某个拉普拉斯内核,不管它是上面示例中那种离散拉普拉斯内核,还是一个生成的 ' 拉普拉斯 高斯 ' ' 高斯差分 '内核 ,都会产生一个比预期结果要复杂的结果。在这种情况下,生成一个未扭曲的(不携带 输出扭曲 )内核会得到更好的结果。

那么,让我们来重复上面的操作,但不带扭曲选项,这样,就只会保留那些较亮的'正数'边缘

  convert face.png -define convolve:scale='!' \

          -morphology Convolve Laplacian:0 \

          -auto-level face_laplacian_positives.png

在这个例子中,我们将暗色(黑色)线条叠加中亮色(白色)颜色上。这反过来使得这个滤镜将边缘“变成双份”,这一点可以在结果中看到。

对于这张图片,使用一个负数的缩放因子(以保留负数的边缘,而不是正数的边缘)会有更好的效果。

  convert face.png -define convolve:scale='-1!' \

          -morphology Convolve Laplacian:0 \

          -auto-level face_laplacian_negatives.png

如妳所见,对于这张图片,使用负数的尺寸的话,会产生更强的边缘提取效果,而不会向正数尺寸中的结果那样,产生“双份”效果。这是因为,在所使用的图片中,是在白色背景上存在着“黑色”的边缘线条。

发散 :在黄色星星周围,妳会看到有蓝色的边缘,这是因为,“黄色”星星和“白色”背景之间的差分值,是蓝色。假如背景是黑色的,那么,会产生黄色的边缘颜色。

Laplacian:1

4个邻居的拉普拉斯内核。也是狠常用的。

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:1 null:

  convert face.png -define convolve:scale='!' -bias 50% \

                   -morphology Convolve Laplacian:1   face_laplacian_1.png

结果不是特别强的效果,但是,通常会比8邻居的拉普拉斯内核要清晰。

Laplacian:2

3x3的拉普拉斯内核,其中,中心:4,边缘:1,角落:-2

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:2 null:

  convert face.png -define convolve:scale='!' -bias 50% \

                   -morphology Convolve Laplacian:2   face_laplacian_2.png

Laplacian:3

3x3的拉普拉斯内核,其中,中心:4,边缘:-2,角落:1

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:3 null:

  convert face.png -define convolve:scale='400%!' -bias 50% \

          -morphology Convolve Laplacian:3    face_laplacian_3.png

这个内核,会将对角线方向的边缘高亮显示,但是,倾向于让竖直和水平方向的边缘消失。不过,妳可能需要对结果进行缩放(就像我做的这样),以让任何结果变得可见。

Laplacian:5

5x5的拉普拉斯内核。

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:5 null:

  convert face.png -define convolve:scale='!' -bias 50% \

                   -morphology Convolve Laplacian:5   face_laplacian_5.png

对于拉普拉斯内核,一个普遍规律是,内核越大,结果越清晰,尤其是当牵涉到错误时更是如此。不过呢,细节就会发生一些丢失。

Laplacian:7

7x7的拉普拉斯内核。

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:7 null:

  convert face.png -define convolve:scale='!' -bias 50% \

                   -morphology Convolve Laplacian:7   face_laplacian_7.png

Laplacian:15

一个离散的5x5的拉普拉斯高斯内核(Sigma值约为1.4)

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:15 null:

  convert face.png -define convolve:scale='!' -bias 50% \

                   -morphology Convolve Laplacian:15   face_laplacian_15.png

Laplacian:19

一个离散的9x9的拉普拉斯高斯内核(Sigma值约为1.4)

  convert xc: -define showkernel=1 -precision 2 \

              -morphology Convolve:0 Laplacian:19 null:

  convert face.png -define convolve:scale='!' -bias 50% \

                   -morphology Convolve Laplacian:19   face_laplacian_19.png

利用边缘检测来对图片进行锐化 (增强原始图片 中的边缘 )

利用模糊内核进行图片反锐化 相反的是, ' 拉普拉斯高斯 ' ' 高斯 差分 '内核 也可用来对图片进行锐化。

基本上,妳所需要做的,就是,将内核结果(包括负数结果)相加到原始图片上。

要做到这一点,狠容易,只需要向缩放因子中加上一个 100%权重 ' 统一 ' "唯一"内核 。这就是为什么要提供那个内核的原因。

例如……

  convert face.png -define convolve:scale='100,100%' \

          -morphology Convolve 'Log:0x2' face_sharpen.png

反锐化技巧 生成的结果(显示在右侧)相比,这个方法生成的结果,更光滑。这是因为,它是针对图片的真正的锐化,而不是通过对模糊值做减法而得到的假的锐化结果。

跟之前说的一样,如果只进行单次卷积的话,那么,妳可以直接使用一个 混合内核

例如,较少的锐化……

  convert face.png -define convolve:scale='50,100%' \

          -morphology Convolve 'Log:0x2' face_sharpen_50.png

或者较强的锐化……

  convert face.png -define convolve:scale='150,100%' \

          -morphology Convolve 'Log:0x2' face_sharpen_150.png

妳可以使用 2 遍的 高斯差分 方法,以达到一个较快的多步骤锐化操作效果,但是,上面已经展示过,这种模式,要求,使用 4 次卷积和一次单独的混合操作,才能产生相同的结果。

这是因为复杂度这个原因,所以,通常更倾向于使用 反锐化 来对图片进行锐化。

如妳所见,对于重度的锐化过程,使用一个适当的锐化内核,相对于 反锐化 来说更合适。然而,对于小尺度的锐化,例如 对改变了尺寸的图片进行锐化 ,则,使用反锐化是没有任何问题的。

带有方向性的卷积 ( 坡和盘 )

与上面的那些内核类似,这里所说的这些内核,会沿着图片中颜色的强度变化方向来寻找坡度,它们不是寻找任意的坡度,而是寻找特定方向的坡度。数学上来讲,这是一个“派生词”,实际意义就是“坡度”。

但是,能够得知不同方向上的坡度信息的话,也狠有用,利用这种手段,可以确定某个坡度或图片边缘的角度或“罗盘”方向。它指的是,在图片中某个特定点的坡度的2维方向。

坡度也在图片处理技巧中被利用到,例如图片的'凹凸处理''阴影处理'

目前 ,没有可用的“生成的”内核,只有那些“命名的”预先定义的内核可用,例如 索贝尔 罗伯茨 。但是,我确信,在日后的某个日子里,凹凸处理和阴影处理内核生成函数会被移动到形态学/卷积内核集合中。

现在,让我们来看看其中一些“命令的”带方向内核。

方向性内核

Sobel

   Sobel:{ angle }  

我们已经在前面 零和内核 的说明中见识到了' 索贝尔 '内核了。

这个内核,是一个原始的方向性 (第一方向 派生 )内核 ,被设计于用来返回某个特定的正交方向上的边缘 的坡度。默认情况下,它被设计为,利用一个“卷积”操作,进行从左向右的坡度的检测。其结果,是该图片的 X方向 派生 (坡度)

  convert -size 60x60 xc:black xc:white +append slope_positive.gif

  convert slope_positive.gif -morphology Convolve Sobel slope_sobel.gif

注意, 这个内核也可以产生一个 '负数坡度'指示 ,但是,必须同时在这个卷积操作中使用一个' 50% '的 扭曲 ,否则看不到效果。尽管在前一个示例中没有负数坡度,但是,下一个示例中是有的,所以,我也添加了一个 扭曲选项 ,以便让妳看到效果。

  convert -size 40x60 xc:black xc:white xc:black +append slope_both.gif

  convert slope_both.gif -define convolve:scale='50%!' -bias 50% \

                         -morphology Convolve Sobel slope_sobel_bias.gif

现在,妳可以看到,当我们沿着从黑到白的方向上坡时,会产生一条白线(正数坡),当我们沿着从白到黑的方向下坡时,会产生一条黑线(负数)

以下是就针对脸图片使用默认的“索贝尔”内核的效果。

  convert face.png -define convolve:scale='50%!' -bias 50% \

                   -morphology Convolve Sobel  face_sobel.png

注意,sobel,以及大部分其它的边缘检测内核,都倾向于,针对超强的边缘产生出一个2像素宽的响应,而针对只有单个像素宽的线条产生出一个3像素宽的响应。这比拉普拉斯边缘检测器要强大。

妳可以使用' angle '参数来对这个内核进行旋转,以90 度为单位。不过,妳也可以以45度为单位,即便它不是如此设计的也可以。这在以下场景下狠有用:获取45度角度度量的方向性的派生;或者,所有以45 度为单位进行旋转的派生结果中最大响应的渐变范围。

以下是旋转90度之后的效果(上到下)

  convert face.png -define convolve:scale='50%!' -bias 50% \

          -morphology Convolve Sobel:90   face_sobel_90.png


利用'
索贝尔 '内核 来收集图片中所有边缘的一种手段是,累积 4 次在所有方向上应用该内核,并且收集所遇到的最大值(利用一个 轻数学合成 来实现这一点 )。这是对于渐变范围的一个近似。

   convert 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 形态学操作中的 多内核处理 特性来简化上面的命令。也就是说,妳可以针对该' 索贝尔 '内核创建一个以90 度旋转的内核列表。

   convert face.png   -define convolve:scale='!' \

           -define morphology:compose=Lighten \

           -morphology Convolve  'Sobel:>'   face_sobel_maximum_2.png

如果 妳想观察上面的命令具体做了什么,那么,加上 显示内核 选项和 话痨 选项。

还有一种更高效的生成渐变范围的技巧,会利用到这个事实:180度的旋转,其产生的效果,与将内核取反的效果相同,也就是说,相当于将结果取反。因此,对于XY派生(90度旋转的卷积),利用某些小技巧,可以获取到卷积的绝对值,于是利用较少的处理步骤实现相同的结果。

   convert 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

对于大部分目标,这已经足够好了。

将两个X和Y派生做一次向量相加(参考 毕达哥拉斯定理 ),可得到所有坡度的一个更精确的范围。

   convert 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

除了范围之外,妳还可以通过这两个边缘检测结果来提取出坡度的方向。

  convert -size 30x600 xc:'#0F0' -colorspace HSB \

          gradient: -compose CopyRed -composite \

          -colorspace RGB -rotate 90  rainbow.jpg

  convert 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

第一 " -fx "表达 式,使用了一个 'atan()'函数 来将一个 X,Y向量转换 成角度。然后,使用一个 彩虹渐变图片 作为 颜色查找表 ,对它进行上色。第二个 " -fx "表达式,创建 了一个带阈值的透明度掩码,使得,没有坡度的任何区域都是透明的。

但是,上面的技巧,倾向于在真实图片中产生巨量的信息,因为,它不会考虑到坡度的范围。

下面是另一个更复杂的版本。这个例子中,所有的计算都是在绿色'G'分量中进行的,这样,相对于3个分量的计算,减少了计算量。然后,它使用HSB颜色空间来创建方向值(hue)和范围值(brightness)

  convert 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

Roberts

   Roberts:{ angle }  

' Roberts '内核 比前面说的 ' 索贝尔 '内核 要简单得多,并且会产生一个更紧缩的边缘定位效果 (2像素 ) 。当然了,这使得它对噪声更敏感。

一般情况下,这个内核是由一个小得多的2x12x2内核来表示的,但是,在实际实现中,它是一个3x3的内核,这样,我就可以'循环'按照45 度角来旋转该内核了。

例如,这是一个45度的结果,通常被称作'Roberts-Cross'内核

  convert face.png -define convolve:scale='50%!' -bias 50% \

                   -morphology Convolve Roberts:45  face_roberts.png

对于' 索贝尔 '内核 ,妳也可以使用 多内核处理 来生成针对所有方向的一个最大坡度。但是,这次,我们会得到 8 x 45 的角度方向变化,而不是 4 个。

   convert face.png -define morphology:compose=Lighten \

           -morphology Convolve  'Roberts:@'   face_roberts_maximum.png

注意, 将这个内核旋转 180 度的话,并不会产生一个反转结果 (因为 有一个偏移值 ) 。因此 ,妳不能简单地将其中半数的卷积结果融合起来,这跟' 索贝尔 '不一样。

一般情况下,由单次 ' 罗伯茨 '卷积生成 的坡度,会相对于实际图片有半个像素的偏移。 也就是说,假如某个点是位于 ' +1 ' ' -1 ' 值之间,则,它会被存储在 ' -1 '像素 的中心处。

然而,这也意味着,将所有的坡度结果都保存下来,然后将它们相加,就会得到一个小得多且锐利得多的边缘检测结果,其宽度仅为2像素(而不是4像素)

Prewitt

   Prewitt:{ angle }  

'Prewitt'内核' 索贝尔 '非常类似,不过,在特定边缘检测的精确方向上,要宽松得多。因此,其结果相对来说会模糊一点。

  convert face.png -define convolve:scale='50%!' -bias 50% \

                   -morphology Convolve Prewitt  face_prewitt.png

Compass

   Compass:{ angle }  

这是'Prewitt Compass'内核,它比' 索贝尔 '的方向性要强一点。

  convert face.png -define convolve:scale='50%!' -bias 50% \

                   -morphology Convolve Compass  face_compass.png

Kirsch

   Kirsch:{ angle }  

这是另一个方向性狠强的边缘检测器。

  convert face.png -define convolve:scale='50%!' -bias 50% \

                   -morphology Convolve Kirsch  face_kirsch.png

Frei-Chen

这个内置内核,包含3组不同的内核。

第一 个内核是' 索贝尔 '的一个“各向同性的” (统一方向)变种 ,其中,那些 ' 2 ' 值被 2 的平方根替代。

   Frei-Chen:[{ type },][{ angle }]  

上面的,是默认的未加权值的内核,它是' Frei-Chen '内核的核心。

  convert face.png -define convolve:scale='50%!' -bias 50% \

                   -morphology Convolve Frei-Chen  face_freichen.png

' 索贝尔 '类似 ,这个内核,应当按照 90 度的单位来旋转。

为了让事情变得简单,我们提供了两个内核(它们具有相同的权值),一个用于正交方向的使用,一个用于斜线方向的使用。

Frei-Chen:1

Frei-Chen:2

第三组类型,由9个专门设计及赋予权重的内核组成,它们不仅用于特定方向的边缘检测,还能够确定一个锐利边缘的实际角度。

这个集合中的内核,其 ' type ' 的值可取 ' 11 ' ' 19 ' ,使得妳能够指定使用9个内核中的任意一个。

然而 ,如果妳将 ' type ' 的值指定为 ' 10 ' ,那么,神奇的事情发生了,妳会得到一个多内核列表,其中包含所有 9 个预先设定权重的内核。

这些内核会逐个应用到原始图片中,然后,它们的结果会被相加到一起,以产生最终的边缘检测结果。

最好 是使用一个 HDRI 版本的ImageMagick来做这件事情。

convert 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

如果指定类型10,那么,会生成一个多内核列表,其中包含所有的带权值内核。这样,妳就可以利用多内核合成来实现上面的功能,不过代码更简单……

convert image.png  -define morphology:compose=Plus \

-morphology Convolve FreiChen:10 \

result.pfm

相关

 )

前面 所说的 ' 卷积 '方法 ,主要是用于图片处理,而 '相关 '方法 呢,被设计于用来进行模式匹配。也就是说,它在图片与内核之间进行一个 '交叉相关 '计算 ,在图片中寻找指定的形状。

实际 上, ' 卷积 ' '相关 ' 是相同的操作。唯一的差别是一个狠小的差别,就是,内核中 x y 的反射 (等价 180 度旋转 )

我能找到的最好的讲述相关度计算与卷积之间区别的文章是: CMSC 426 的课程笔记, 2005 年秋, David Jacobs

卷积与相关度的比较 ( 非对称内核效果 )

我在前面已经说过, ' 卷积 ' ' 相关 度计算 ' 这两个操作在本质上是一样的。实际上,用户经常会在想要指代相关度计算的时候却说成了卷积。同时,相关度计算实际上也更容易理解。

对于那些关于某个中心点对称的内核来说,这两个方法实际上就是相同的。只有当妳使用一个非对称或者奇数的内核的时候,才会体现出差别。

下面的例子中,我用了一个'L'形状的平坦内核,来处理一个'单像素'图片

  convert pixel.gif  \

          -morphology Convolve '3: 1,0,0

                                   1,0,0

                                   1,1,0'   convolve_shape.gif

妳可以看到, ' 卷积 '操作, 将中心点的单个像素展开了,在它的周围形成了一个 'L'形状 。甚至,原始点本身也没有出现在结果的“周围”圈子中。

现在 ,我们再用 ' 相关 度计算 ' 来做试验。

  convert pixel.gif  \

          -morphology Correlate '3: 1,0,0

                                    1,0,0

                                    1,1,0'  correlate_shape.gif

可以看到, ' 相关 度计算 ' 也将单个像素展开了,也形成了一个 'L'形状 ,但是,它的形状是“旋转过”的。

这就是这两个方法的唯一本质区别。 ' 相关度计算 '方法 会‘原样’地应用该内核,其结果是,单个像素被展开为一个 '旋转 ' 的形式。而 ' 卷积 ' 呢,实际上会使用该内核的 180 '旋转' 的形式,因此,每个像素被展开为相同的未旋转的形状。

相关度及形状搜索

'相关 度计算 '方法 的真正用途, ( “原样”应用一个内核,而不施加旋转 ) ,是,一种古老而简单的方法,用于定位到某些形状,这些形状大体上与所提供的内核的形状相匹配。

例如,假设 ,我们要以一个'L'形状的内核来进行'相关度计算',并且尝试寻找到我们之前使用卷积方 法产生的那个图片中的形状,那么,结果是……待续

  convert convolve_shape.gif  -define convolve:scale='1!' \

          -morphology Correlate '3: 1,0,0

                                    1,0,0

                                    1,1,0' correlate.gif

注意,我利用了 IM 内核归一 功能 ,来阻止最终结果发生以下不幸的事情:变得太亮;或者,“波峰”在白色点的海洋中被淹没。

可以看到, ' 相关 度计算 '方法, 当内核“原点”与图片中相同的形状完全重合时,产生了一个最亮的点。但是,在该形状部分匹配的地方,也产生了不那么亮的结果。形状匹配 得越多,对应的像素就越亮。

然而 ,我得警告妳,尽管 ' 相关 度计算 '方法 在这个例子中是有用的,但是,它实际上并不是做这个事情的好方法。例如,在那些本身就有狠高亮度的区域,它会产生出大量的假的匹配。

这个问题,可通过以下手段来缓和:对于那些应当匹配到图片中的暗色背景的区域,使用负数值。也就是说,不与背景匹配的那些区域,应当让结果像素变得不那么亮。

  convert 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

可以看到,匹配的波峰更明显了,因为,现在,妳不仅仅是在对前景像素进行匹配,也对背景像素进行了匹配。

注意, 上面的例子中使用了特殊的归一化标志 ' ^ ' 。这一点狠重要,因为它会分别对内核中的正数值和负数值进行归一化。也就是说,妳希望按照与前景像素相同的权重来搜索背景像素。

这也意味着,妳可以使用 HDRI版本的IM 或适当的 输出扭曲 (参考前文)来同时搜索指定形状的正数和负数匹配。

例如,下面的例子中,我针对一个同时包含正数和负数'L'形状的测试图片搜索'L'形状。(此处图片 过了放大)

  convert test.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

输出扭曲 ,使得,普通的搜索输出变成了一半的灰度,而匹配的形状呢,会变得更亮或更暗,具体取决于实际与该 '形状内核'匹配 的像素个数。如果 妳逐个检查一下输出图片中的实际值,那么,会发现产生了一个纯白和一个纯黑的像素,它们表示完美的匹配。然而,也有狠多近似匹配。

如果 我对负数或 '黑色'匹配 不感兴趣,那么,我可以去掉 输出扭曲 ,以及 ' 50% ' 的缩放因子,这样的话,不匹配的像素会是黑色的,而完美匹配的像素会是白色的。

有了 ' 相关 度计算 ' 的匹配图片之后, 妳需要尝试着找到匹配的 '波峰' 。这一点,可利用另一个 相关 度计算 操作 来完成,但是,并不总是产生好的效果。

更好 的方法是,使用更精确的模式匹配方法, ' 命中 还是不命中 '形态 学方法,并且使用专门为这个目的而创建的特殊的 ' 波峰 ' 。这种方法,会找到满足以下条件的单个像素:它只被较暗的像素包围。还有其它类型的 ' 波峰 '内核可用 ,它们可用来寻找 ' 较宽松 ' 的匹配。

  convert correlate_bias.gif  -morphology hitandmiss peaks:1.9 \

          -auto-level  correlate_peaks.gif

这样,妳可以轻易地看到,对于该形状的最佳匹配位于哪里,不过,匹配的程度已经丢失了。

妳可以参考一下 比较及子图片搜索 中的 '波峰寻找'小节

相关度计算与命中还是不命中形态学操作的比较

妳可以将我展示的内核图片和在 命中 还是不命中形态学方法 中使用的 内核的图片做个比较,妳会发现,它们表示的是同一个东西。

' 命中 还是不命中 '

' 相关 度计算 '

前景

值为'1.0'

值为'1.0'(归一化之前)

不关心

值为'Nan''0.5'

值为'Nan''0.0'

背景

值为'0.0'

值为'-1.0'(归一化之前)

结果

从背景的最大值中减去前景的最小值。因此,只有精确的匹配会产生出正数的结果,并且,阈值化操作会产生出一个纯黑白的匹配图片。

产生一个范围,表示该图片与某个形状的匹配程度。只要整体的模式是存在的,那么,就可能出现,某些背景像素的值比某些前景像素的值要大。可能会难以定位到特定的'匹配'波峰。妳还会发现存在负数的匹配。

妳可以看到,它们是互相对应的。因此,针对某个功能的内核,可以被转换为针对另一个功能的内核。

然而 ' 命中 还是不命中 ' ,只会针对固定的前景与背景差来找到完美的精确匹配。因此,与' 相关度计算 '相比,它对于噪声更敏感,并且更可能不命中。

另一方面, ' 相关 度计算 ' 可使用线性图片处理技术来实现,甚至可使用 快速傅立叶变换 这样的话,使得,对于较大的模式和内核,进行模式匹配会快得多,尤其是当妳牵涉到多个模式的时候,它会节省将图片和模式变换到频率域的开销。它也对实际图片有效,不过,需要一些预处理,并且必要的时候需要使用 HDRI

究竟选择哪种方式,取决于妳,结果也会有对应的不同。考虑因素包括:是否只允许完美匹配;是否允许近似匹配,并且带有大量的错误;是否能够使用较快的算法。

注意,如果 妳的目的是在较大的图片中找到小的彩色图片的精确匹配,那么," compare "程序中的 子图片定位功能 会提供比' 命中还是不命中 '和' 相关度计算 '方法好得多的方法。 这是因为,它使用一种 '最少矩形 的颜色向量差 ' 来进行子图片匹配,这种方法,对于匹配结果来说,可以产生一个更好的度量结果。然而,它狠慢,尤其是对于大图片。

邻居计

卷积的另一个鲜为人知的用途就是,进行邻居计数。也就是,计算出,一张图片中,每个像素点周围,特定区域中,有多少个像素。

计算邻居

简单来说,使用一个狠简单的卷积内核,就可以创建一个图片,它能够计算出,在一张纯黑白图片中,某个特定点周围的邻居个数。

用一个尺寸为' 1.5 '的 环内核 进行卷积,就能够进行邻居计数。

下面例子 中,计算出了一个小区域中每个像素附近的邻居个数,并且显示 出了计算之前和之后的的单个像素的放大效果 (使用 放大图片脚本 来生成 )……

  convert area.gif  -define convolve:scale=\! \

          -morphology Convolve Ring:1.5     neighbour.gif

可以看到,各个像素的灰度级别,显示了它们拥有多少个邻居,包括边缘处的 虚像素 邻居。

如果妳想把当前像素计算在内,那么,可以使用一个 方形内核

使用适当 的转换 (包括级别调整) 以及 Pbm +文件格式 ,妳就可以将上面的灰度级别转换成实际的数字。

  convert neighbour.gif +depth +level 0,8 pgm: | pnmnoraw | tail -n +4

如果 妳想要排除掉位于实际形状内部的像素,那么,妳可以使用一个带有强负数值中心像素的内核,然后, 去掉 任何负数结果(假设 妳在使用 HDRI版本的IM 的话 )

要想生成这种由正数的1围绕在一个巨大的负数中心周围的内核的话,有一种简单的方法,就是,以负数因子来缩放一个标准的 离散拉普拉斯内核

  convert area.gif  -define convolve:scale=-1\! \

          -morphology Convolve Laplacian:0 -clamp  neigh_edge.gif

当然,我们也可以,将原始图片用作一个掩码,以去除不感兴趣的像素。

生命游戏

1970 年,一个英国数学家, John Horton Conway ,在科学美国人上发表了一种特殊的模拟方法,后来大为流行。现在,它被称作 Conway 的生命游戏

它是基于一个由点组成的网格,其中,每个点,或者是'存活状态',或者是'死亡状态'。之后,在下“一代”中,各个'细胞'是处于'存活状态'还是'死亡状态',则,取决于一组狠简单的规则,这些规则只基于各个细胞周围处于存活状态的细胞的个数。

  • •.邻居范围,指的是,每个“细胞”周围的8个像素。

  • •. 一个处于'存活状态'的细胞,如果它有2个或3邻居,则,它继续存活。

  • •. 一个处于'死亡状态'的细胞,如果它正好有3个邻居,则,它进入'存活状态'(出生)

  • •.否则,该细胞保持或进入'死亡状态'

将这些规则应用到纯黑白的模式上,所产生的结果是非常奇妙的,妳会发现一堆堆的'细胞',它们似乎:在扩张;在缩小;在彷徨;或者,甚至在缓慢地在网格中移动。它成为了理论研究中的一个主要热点,以研究,能否产生出以'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 ' 这个灰度级别,妳将得到一个白色,并非是内核能够产生这个值。

现在 ,我们可以将 ' 生命游戏 ' 的规则编码进入一个 颜色查找 表图片 中,这样,就能够,根据“生命规则”,将内核生成的结果中的邻居计数 '灰度 ',转换 成,适当的 '存 活还是死亡 '结果

  convert -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

这个图片本身是狠小的,所以,我使用了一个 图片放大脚本 来生成一个较大的版本,以便显示,这样,每个像素都是清晰地分开的。

简单来说,前面10个像素,是针对'死亡细胞'的,后面10个像素,是针对'存活细胞'的。

左边的第一个白色像素(表示一个死亡细胞周围的邻居个数 = 3)表示一个'出生规则',而右边的两个白色像素呢(表示一个存活细胞周围的邻居个数为23)使得一个已有的'存活细胞'继续存活。其它的任何结果,产生的最终结果都是黑色(死亡)

这个 颜色查找 的长度为 21 个像素,因为,我会以 20作为因子 来进行除法计算,这就意味着,我们会生成一个范围为 0 20 的值,或者说是 21 个唯一的灰度级别。我们也可以使用别的值而不是10来表示中心点 (细胞 在之前的状态 ) ,使用别的值而不是 20 来表示缩放因子,但是,这两个值较容易理解。

总结一下,我们将卷积内核除以 20 ,并且使用一个长度为 21像素 的颜色查找表 (利用 整数插 ) 来将卷积结果(灰度值)匹配为正确的输出颜色值。

好,让我们来对一张包含着“生命”模式的图片用一用这种操作,并且进行多次,以观察,该模式如何一代代地变化,并且检查以确认,它是否按照预期的形式来工作。

  convert -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

  convert 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

  convert 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

  convert 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

  convert 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

可以看到,这些'生命'模式按照预期的方式表现出了它们的行为(假如妳对这些模式熟悉的话,妳可以看出来)。左上角的'闪烁',就是不停的翻转,而底部的'滑翔'呢,每隔4“代”,就向它移动1个对角线格子的距离。

这是一个较大的示例,被称作 Gosper 的滑翔机发射器 我使用一个特殊的生命模式生成了一个 60 帧的动画。实际使用的图片的尺寸,显示在上方,但是,我将结果动画图片放大了,以便更好的观摩。

  convert glider_gun.gif life_pattern.gif

  for i in `seq 59`; do

    convert 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 | convert - -scale 500% \

                 -set delay 10 -layers Optimize -loop 0  glider_gun_anim.gif

  convert glider_gun.gif -scale 500% life_pattern.gif

注意,滑翔 者在底部边界处会 '爆炸' ,这是因为,卷积操作中会进行默认的 ' 虚像素 '处理 ,这样导致,当滑翔者超出图片的边界时,其生命信息会丢失。

这只是IM 能够处理的整个 ' 元胞自动 '范围 中的一个示例。当然 ,有狠多更快的专门用来处理 '生命' '元胞自动 ' 的程序,它们能够完成相同的工作,但是,我想说明的是, IM足够灵活 ,也能够做这件事。

由于 这些结果是简单的二值化图片,所以,妳还可以使用 IM 形态 方法 (例如 命中还是不命中的模式寻找 交叉相关度计算 )来寻找特定的生命模式,这样,用 IM 来进行生命模式寻找就更实用了,只是可能会速度慢。

高阳公主和 辩机

Your opinions
Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands