ImageMagick 示例 --
图像映射效果

索引
ImageMagick 示例前言和索引
图像映射简介
使用图像映射扭曲图像
绝对扭曲查找表
相对位移查找表
可变模糊映射
使用某种辅助“映射”图像来扭曲或修改图像,该图像控制着处理过程。无论是替换颜色、可变模糊图像,还是通过绝对或相对地指定源坐标来扭曲图像。

简介

正如您在前面有关合成简单扭曲扭曲的部分中看到的那样,您可以通过多种不同的方式修改图像。但是,它们都局限于已内置于 ImageMagick 中的特定方法。您甚至可以使用'FX' DIY 运算符“自行”创建图像扭曲,或使用诸如评估函数之类的运算符,甚至各种级别运算符来直接修改图像的值。但是,扭曲需要大量的计算(和时间)来完成其任务,如果您计划对多个图像执行相同的任务,让 IM 重复所有这些计算将是真正的浪费时间。另一个方面是很难以自由形式的方式限制扭曲的效果。您不能简单地编辑或修改您想要应用的扭曲。您的控制有限。**图像映射**则有所不同。您使用额外的“映射”图像来控制图像的哪些部分以及如何修改,或者以何种方式修改。它不必修改整个图像,也不必以某种预定义或预编程的方式修改图像。您可以创建一个可以以任何可能的方式修改图像的“地图”,不受限制。您还可以编辑或进一步修改映射,以调整或限制其效果,使其更复杂,通过将不同的地图合并在一起,或者只是平滑或模糊效果。最后,您可以保存映射以便以后再次使用。是“地图”图像控制结果。由于修改是由“地图”控制的,因此 ImageMagick 通常需要执行很少的计算,因此“图像映射”通常非常快。它也是可重复的,因为您可以对任意数量的图像应用相同的非常复杂的地图,以获得完全相同的修改。也就是说,您可以非常快速地将其应用于整个图像目录。本质上,图像映射所做的是将特定效果的缓慢且复杂的数学运算从特定图像转移到更通用的“地图”图像。一旦生成该“地图”,它就可以非常快速地应用于许多实际图像。

什么是图像地图

映射图像基本上是“查找表”或 LUT,用于定义应如何逐像素地将特定效果应用于图像。也就是说,是否应用效果以及应用程度完全由图像地图控制。从本质上讲,图像是一个值的数组,这些值的含义取决于正在应用的映射过程。它们可能表示…
  • 直接替换值(颜色查找),
  • 颜色应来自哪个图像(图像蒙版),
  • 像素应被照亮或变暗多少(高光),
  • 指定源坐标(扭曲),
  • 或相对于当前位置的位置(位移)。
  • 在此位置模糊像素的程度
我们已经在图像合成中看到了很多这些内容,从某种意义上说,图像映射只是将多个图像合并在一起的另一种方式。事实上,许多图像映射技术只是作为专门的合成方法实现的!请记住,真正的图像合成实际上只是以各种方式叠加两个真实的彩色图像(特别是Duff-Porter Alpha 合成方法)。图像映射更一般地涉及使用专门的图像以特殊方式修改一个图像。图像映射最困难的部分是为特定效果生成特定的“地图”。这正是本页上存在的大量工作、努力和技术的来源。但是,一旦您拥有了地图,就可以使用它多次,并快速应用于许多不同的图像。

使用图像映射扭曲图像

虽然 IM 示例的前面部分描述了各种扭曲运算符(例如简单图像扭曲通用图像扭曲),但您仅限于已编程到 IM 图形库中的各种扭曲类型,通常使用特定的数学方程和公式。但是,有时您希望以更自由和更少数学的方式设计自己的扭曲。例如,为了生成更复杂的扭曲,例如将图像映射到特定形状,或具有特定的复杂镜头效果,这比用数学方式定义更容易绘制。有时您只想能够对大量图像重复您的扭曲,并避免一遍又一遍地重新计算扭曲。解决方案是预先计算您的扭曲,并将其以灰度图像的形式保存为特殊的查找表 (LUT)。也就是说,对于每个输出像素,我们查找 LUT,然后使用该值从源图像中查找颜色。也就是说,需要三个步骤。
  1. 在 LUT 中查找每个目标像素
  2. 将 LUT 值映射到源图像位置(两种方法)
  3. 从源图像中查找颜色
由于图像用于扭曲的“查找表”,因此您可以使用图像编辑器(例如“Gimp”或“PhotoShop”)创建或修改扭曲图,让您自由地进行一些真正奇特和复杂的扭曲。但是,您必须记住,就像我们看到的其他所有扭曲方法一样,查找被应用为反向像素映射。也就是说,对于目标图像中的每个像素,我们使用正在应用的扭曲方法从源图像中查找像素的颜色。在这种情况下,该方法是从提供的查找表图像中查找源坐标。
现在,有两种方法可以使用图像地图来确定应在源图像的哪个位置查找颜色……**绝对**或**相对**。使用**绝对**坐标查找,扭曲映射将 LUT 颜色值直接转换为源图像中的坐标,从中查找要使用的颜色。LUT 中颜色的位置无关紧要,每个颜色都指的是要使用的精确查找点。扭曲 LUT 图像将具有颜色渐变,但该渐变的任何扭曲或变形在应用地图时都会达到相同的效果。使用**相对**坐标查找,位移映射使用颜色值来偏移当前坐标,以确定源图像中查找颜色的位置。这意味着使用纯灰色 LUT 图像,较亮和较暗的区域定义了像素如何根据地图进行移动或位移,而不管它们在地图中的位置如何。正如您将看到的,这两种方法各有优缺点。

绝对扭曲查找表

创建绝对扭曲 LUT 地图是这两种方法中更容易理解、创建扭曲 LUT 地图和应用的方法。但是,正如您将看到的,它有一个非常严重的缺点,使其不如相对位移地图实用。它是“扭曲地图”中任何特定点的颜色直接指的是源图像中的位置。也就是说,跨“地图”的灰度渐变定义了要放置在该位置的“纹理”。现在考虑一下,地图图像实际上是复杂物体(如 T 恤)的图像,具有复杂的褶皱和波纹。如果该衬衫上有渐变,则可以将任何平面图像映射到该衬衫上。也就是说,绝对扭曲地图的力量。LUT 图像中的任何“黑色”像素(颜色值 0)将被认为是源图像的最左侧像素或“0”X 坐标,而 LUT 中任何“白色”像素(值 1)将被认为是最右侧的像素(源图像的宽度)。请注意,此 LUT 仅查找源图像中颜色的 X 或水平位置。它不会更改颜色的高度或 Y 位置。因此,让我们使用简单的纯灰度水平渐变作为 LUT 来尝试一下。

  magick koala.gif \( -size 75x75 gradient: -rotate 90 \) \
          -fx 'p{v*w,j}'      distort_noop.gif
[IM Output]  + [IM Output] ==> [IM Output]
请注意,这并没有对将源图像映射到目标图像进行任何真正的更改。也就是说,因为我们从扭曲地图中查找的 X 坐标与我们查找颜色的位置相同。通过简单地翻转渐变,查找像素的操作也会翻转,从而创建镜像图像。也就是说,白色在左侧,“黑色”在右侧,以及跨图像的水平渐变。

    magick koala.gif \( -size 75x75 gradient: -rotate -90 \) \
          -fx 'p{v*w,j}'      distort_mirror_x.gif
[IM Output]  + [IM Output] ==> [IM Output]
如果我们采用原始渐变并使用对比度增强运算符对其进行压缩,我们可以获得更有用的扭曲。

  magick -size 75x75 gradient: -rotate 90 \
          -sigmoidal-contrast 8,50%      map_compress.gif
  magick koala.gif  map_compress.gif -fx 'p{v*w,j}'  distort_compress.gif
[IM Output]  + [IM Output] ==> [IM Output]
请注意,扭曲的两侧被拉伸,而中心被压缩。我们可以通过使用 2 个扭曲地图将其扩展到两个维度,一个用于调整 X 坐标,另一个用于调整 Y 坐标。

  magick map_compress.gif -rotate 90 map_compress_y.gif
  magick koala.gif  map_compress.gif map_compress_y.gif \
          -fx 'p{u[1]*w,u[2]*h}'   distort_compress_2D.gif
[IM Output]  + [IM Output] [IM Output] ==> [IM Output]
如您所见,以上重新创建了内爆方法的一个变体,但仅通过沿 X 和 Y 轴(同时)压缩图像,而不是像内爆运算符那样径向压缩。这里的关键是,无论您对绝对扭曲地图执行什么操作,您都将对应用它的任何图像的最终图像执行该操作。也就是说,扭曲地图的力量。

复合“扭曲”方法

到目前为止,我们一直在使用FX,通用 DIY 运算符来应用绝对扭曲地图。这提供了一种精确调整和微调您正在执行的操作的确切方法,但速度也非常慢。合成运算符Distort”编码了我们上面一直在使用的非常相似的公式。尽管它以一种使其也与我们将在相对位移地图中稍后看到的“Displace”合成运算符更兼容的方式实现。

因此,让我们使用“Distort”合成重复最后一个“内爆”示例。


  magick koala.gif  map_compress.gif map_compress_y.gif \
          -compose Distort  -define compose:args=37.5x37.5 -composite \
          distort_compose.gif
[IM Output]  + [IM Output] [IM Output] ==> [IM Output]
请注意上面使用了 “定义设置” 中的 “compose:args”。此值是用于乘以 LUT 渐变的倍数(以完美的灰色为中心)。使用值 '37.5' 是图像宽度和高度的一半(75 像素)。您可以更改该乘数以扩展或收缩失真的整体比例。如果未定义 “compose:args” 值,则将默认为正确的值。如果将值设置为零,则不会在该方向上应用失真。如果您想自动设置合成参数,可以使用以下等效的 设置“option:” 方法来计算它……

  magick koala.gif  map_compress.gif map_compress_y.gif \
          -set option:compose:args '%[fx:w/2]x%[fx:h/2]' \
          -compose Distort  -composite \
          distort_compose_set.gif
[IM Output]
或者取消定义它,以便让 IM 计算正确的值(对于 2D 失真)……

  magick koala.gif  map_compress.gif map_compress_y.gif \
          -compose Distort -define compose:args='' -composite \
          distort_compose_default.gif
[IM Output]

无操作失真映射

在我们继续之前,我想先退一步,再仔细看看上面提到的 'noop' 示例。这实际上会稍微模糊图像,因为我概述的公式实际上并不完全正确。获取原始图像的 'no-op' 副本是一个很好的测试,可以验证您的失真数学是否正确。

也就是说,当给定一个完美的渐变时,您可以将源图像中的每个像素映射到目标图像。也就是说,LUT 的 '白色'(或 1.0)值将精确映射到目标图像中最右侧(或最下方)的像素。

为了测试无操作失真,我们使用 '像素检查图像'(例如:“pattern:gray50”),因为它会显示任何失真,从而显示应用数学中存在的任何问题。因此,让我们尝试将无操作失真应用于我们迄今为止使用的方法……

  magick -size 75x75 pattern:gray50 \
          \( gradient: -rotate 90 \) \( gradient: -flip \) \
          -fx 'p{u[1]*w,u[2]*h}'    distort_fx_check.gif
[IM Output]

  magick -size 75x75 pattern:gray50 \
          \( gradient: -rotate 90 \) \( gradient: -flip \) \
          -set option:compose:args '%[fx:w/2] x %[fx:h/2]' \
          -compose Distort  -composite  distort_compose_check.gif
[IM Output]
如您所见,这两种方法都未能重现 '像素检查' 图像。尽管由于坐标的计算方式不同,它们以略微不同的方式做到了这一点。发生的情况是从颜色查找到像素坐标的缩放因子偏差了 1 个像素。有关发生这种情况的原因的详细信息,请参阅 失真、图像坐标与像素坐标
FX 失真以右上角(像素位置 0,0)为中心,并在底部和右侧边缘生成重复的虚拟像素。也就是说,因为它没有尝试更改从查找颜色到用于实际查找的图像坐标的缩放中心。因此,即使缩放比例不正确,黑色像素仍以像素 0,0 为中心。

compose “Distort” 操作符在应用缩放之前将坐标转换为图像中心为零。它在 '位移映射' 的缩放过程中执行此操作(请参阅后面)。因此,不准确的缩放会将图像的边缘沿每个边缘向内拉 1/2 个像素,同时保持图像中心正确。
以下是针对绝对失真映射的已更正的“完美无操作”版本,在计算颜色和坐标之间的缩放因子时,本质上使用了图像坐标(宽度和高度减少了一个)。

  magick -size 75x75 pattern:gray50 \
          \( gradient: -rotate 90 \) \( gradient: -flip \) \
          -fx 'p{u[1]*(w-1),u[2]*(h-1)}'    distort_fx_check_correct.gif
[IM Output]

  magick -size 75x75 pattern:gray50 \
          \( gradient: -rotate 90 \) \( gradient: -flip \) \
          -set option:compose:args '%[fx:(w-1)/2] x %[fx:(h-1)/2]' \
          -compose Distort  -composite  distort_compose_check_correct.gif
[IM Output]
实际上,如果未定义 “compose:args” 的默认值,则使用正确的缩放值。

  magick -size 75x75 pattern:gray50 \
          \( gradient: -rotate 90 \) \( gradient: -flip \) \
          -compose Distort -define compose:args='' -composite \
          distort_compose_default_check.gif
[IM Output]
但是,应该指出的是,在使用失真时,这些细微的误差通常并不重要,因此通常会忽略任何细微的差异。请记住这一点,以便在它确实很重要时了解它。

扭曲映射的问题

让我们继续通过尝试旋转来扭曲图像。为此,生成旋转映射可能有点棘手,但可以做到……

  magick -size 75x75 gradient: -background black -rotate 45 \
          -gravity center -crop 75x75+0+0 +repage  map_rot45_x.png
  magick map_rot45_x.png  -rotate 90              map_rot45_y.png
  magick koala.gif  map_rot45_x.png   map_rot45_y.png \
          -compose Distort  -composite    distort_rot45.gif
[IM Output]  + [IM Output]  + [IM Output] ==> [IM Output]
现在我们有了另一种旋转任何图像的方法。这种技术的最大问题是,通过使用旋转创建失真映射,我们在对角线边缘的侧面引入了一些颜色奇怪的像素。在最后一个示例中,这导致一些随机像素沿图像的右下角添加成一行。这些 '随机' 颜色是由旋转引入的反锯齿值,用于生成 '更好' 的图像。但是对于失真映射,反锯齿边缘像素会导致真正的问题。现在我们可以尝试更好地定义旋转 LUT 图像边缘的颜色。在这种情况下,我们可以生成一个更大的渐变图像,然后将旋转裁剪到正确的尺寸。

  magick -size 100x20 xc:white xc:black -size 115x75 gradient: \
          +swap -append   -rotate 45 \
          -gravity center -crop 75x75+0+0 +repage   map_rot45b_x.png
  magick map_rot45b_x.png  -rotate 90              map_rot45b_y.png
  magick koala.gif  map_rot45b_x.png   map_rot45b_y.png \
          -compose Distort  -composite     distort_rot45_better.png
[IM Output]  + [IM Output]  + [IM Output] ==> [IM Output]
通过这种方式,LUT 中的所有像素现在都已正确定义,没有抗锯齿。这现在显示了一个稍微不同的问题。最终图像中的所有像素都已正确定义,但某些像素不应成为最终图像的一部分。它们在结果图像中没有实际意义。这代表了使用 LUT 指定从源图像获取绝对坐标的最大问题。您无法指定 IM 在这些未定义区域中应该做什么。

使用蒙版设置未定义像素

解决 '未定义像素' 问题的更通用方法是定义一个映射,指示哪些像素实际上是失真中有效的定义结果。换句话说,一个蒙版图像。例如……

  magick -size 75x75 xc:white -background black -rotate 45 \
          -gravity center -crop 75x75+0+0 +repage  map_rot45b_m.png
  magick distort_rot45_better.png map_rot45b_m.png \
          -alpha off -compose CopyOpacity -composite   distort_rot45_masked.png
[IM Output]  + [IM Output] ==> [IM Output]
现在我们有三个与失真映射相关的图像,结果确实变得复杂了。当然,在典型情况下,您可能不需要走那么远,但对于一般情况,您确实需要。

统一扭曲图像

但是,您可能已经注意到所有三个映射都是灰度图像。这意味着将所有映射合并到单个失真映射图像中是相当合理的。例如,我们将 'X 失真映射' 映射到 'red' 通道,将 'Y 映射' 映射到 'green',并将蒙版映射到 'alpha' 或透明度通道,这使得处理起来更容易。

  magick map_rot45b_x.png map_rot45b_y.png \( map_rot45b_m.png -negate \) \
          -alpha off -channel RGA -background black -combine  map_rot45u.png
[IM Output] [IM Output] [IM Output] ==> [IM Output]
组合通道图像 中,'blue' 通道未定义,因此其值取自当前的 “-background” 颜色,在上面我将其预设为 'black' 或值为零。
现在让我们将此统一的失真映射应用于我们的考拉图像。不幸的是,这需要两个图像处理步骤,一个用于扭曲图像,另一个用于蒙版结果。

  magick koala.gif -alpha set   map_rot45u.png \
          \( -clone 0,1  -fx 'p{v.r*w,v.g*h}' \
             +clone -compose Dst_In -composite \) -delete 0,1 \
          distort_rot45_unified.png
[IM Output] [IM Output] ==> [IM Output]
您还可以直接使用统一的失真映射图像使用 “Distort” 组合方法……

  magick koala.gif -alpha set   map_rot45u.png \
          -compose Distort -define compose:args='' -composite \
          distort_rot45_compose.gif
[IM Output]
在 '统一失真映射' 图像中仍然有一个未使用的通道(blue)。其一个逻辑用途是作为向失真图像添加高光和阴影的方法。(请参阅 叠加高光)。您可以在下面的 球形失真映射 示例中看到此技术的进一步应用。

沙漏扭曲映射

现在我想要一个一维失真映射,根据该行的高度以不同的方式缩放图像的每一行。有点像产生一个真正的嘉年华游乐场哈哈镜失真,使胖的人看起来非常瘦。换句话说,一种沙漏失真。这是一个非常复杂的 LUT 图像,在经过大量调整后,我想出了以下表达式来生成高度变量,但水平线性渐变映射。

  magick -size 100x100 xc:  -channel G \
          -fx 'sc=.15; (i/w-.5)/(1+sc*cos(j*pi*2/h)-sc)+.5' \
          -separate  map_hourglass.png
[IM Output]
在生成灰度渐变时,您可以使 -fx 运算符快 3 倍,只需要求它仅生成一个颜色通道,例如上面示例中的 'G' 或绿色通道。然后可以 分离 此通道以形成所需的灰度图像。这可以代表非常大的速度提升,尤其是在使用非常复杂的 “-fx” 公式时。
sc” 是沙漏的缩放因子(值范围为 0 到 0.5),并允许您调整失真的幅度。现在让我们将此映射应用于内置的 “rose:” 图像。请注意,100x100 像素的映射与 70x46 像素的图像不匹配。这使事情变得复杂,因为我们需要按适当的量缩放源图像中的当前像素以匹配我们提供的失真映射,以查找该像素颜色的位置。

  magick rose:  map_hourglass.png \
          -fx 'p{ v.p{i*v.w/w,j*v.h/h}*w,  j}'  distort_hourglass.png
[IM Output]
如果您仔细观察,像素 X 坐标 'i' 乘以失真映射图像的宽度 'v.w',然后除以原始图像的宽度 'w',得到 'i*v.w/w。像素 Y 坐标 'j*v.h/h' 也发生同样的事情。这将目标图像中的像素坐标重新缩放以匹配失真 LUT 图像。然后通过将 LUT 值乘以源图像的宽度来缩放查找的坐标,以成为颜色查找的 X 坐标。如果您同时拥有 X 和 Y 失真映射,则必须对 Y 映射重复缩放查找。当然,我们之前看到了相同的 '边缘' 失真,因此让我们将 虚拟像素设置 更改为透明。

  magick rose: -alpha set  -virtual-pixel transparent -channel RGBA \
          map_hourglass.png  -fx 'p{ v.p{i*v.w/w,j*v.h/h}.g*w, j}' \
          distort_hourglass2.png
[IM Output]
请注意使用 “-channel” 设置以确保 “-fx” 将使用源图像的 alpha 通道(透明)值并返回这些值。特别是透明虚拟像素。另请注意,在查找失真映射时,我们仅从绿色通道(使用 'v.p{}.g')查找。如果不这样做,将使用与源图像中正在处理的通道相同的通道,并且对于映射,'alpha' 未定义。可以通过使用非线性渐变来改进此失真映射,以便图像保持矩形,边缘的失真比中间的失真更大,以使其更“圆润”或“圆柱形”。有谁想尝试一下吗?给我发邮件

球形扭曲映射

在之前的 沙漏失真映射 示例中,我生成了一个由余弦曲线水平缩放的渐变。稍微多做一些工作,就可以生成球形形状……

  magick -size 100x100 xc:  -channel R \
          -fx 'yy=(j+.5)/h-.5; (i/w-.5)/(sqrt(1-4*yy^2))+.5' \
          -separate  +channel     sphere_lut.png
[IM Output]
但是请注意,以上并不完全准确。压缩的渐变仍然是线性渐变,只是压缩以适合圆圈内。更准确的表示可能需要创建非线性渐变。这在绝对位置方面将是 'arccos()' 函数。现在,此映射也有一些会被归类为无效的大面积,因此需要某种蒙版来定义最终图像中哪些像素有效,哪些像素无效。在这种情况下,一个简单的圆圈就可以了。

  magick -size 100x100 xc:black -fill white \
          -draw 'circle 49.5,49.5 49.5,0'    sphere_mask.png
[IM Output]
最后,我们还需要一个阴影高光,例如在 叠加高光 中开发的,供 叠加强光 合成使用……

  magick sphere_mask.png \
          \( +clone -blur 0x20 -shade 110x21.7 -contrast-stretch 0% \
             +sigmoidal-contrast 6x50% -fill grey50 -colorize 10%  \) \
          -composite sphere_overlay.png
[IM Output]
请记住,以上阴影仅在球体对象的范围内才重要,因此阴影超出这些范围这一事实并不重要。实际上,如果您想尝试提出一个更好的球形阴影,从而产生一个更好的球状图像,我很乐意看到它。因此,让我们将这三张图像:X 坐标 LUT、叠加阴影和透明度蒙版应用到大小合适的实际图像上(为简单起见)。

  magick lena_orig.png -resize 100x100   sphere_lut.png   -fx 'p{ v*w, j }' \
          sphere_overlay.png   -compose HardLight  -composite \
          sphere_mask.png -alpha off -compose CopyOpacity -composite \
          sphere_lena.png
[IM Output] ==> [IM Output]
此特定示例展示了 绝对失真映射 最强大的方面。您可以在任何自由形式的对象上定义渐变(不一定在数学上),以便可以将任何图像映射到该对象上,无论是曲线、皱纹、褶皱等。简而言之,一旦您确定了对象映射,就可以将任何图像映射到其表面上。然后,为了使其看起来更逼真,您可以叠加第二个映射,以添加高光、阴影、边缘和其他特征。当然,由于所有三个图像都是灰度的,因此您可以将它们组合成单个 统一失真映射 图像,以方便存储。在这种情况下,我将通过重复使用 X 坐标失真 LUT 作为 Y 坐标来使其成为更球形的失真。


  magick sphere_lut.png   \( +clone -transpose \) \
          sphere_overlay.png   \( sphere_mask.png -negate \) \
          -channel RGBA  -combine    spherical_unified.png
[IM Output]
这是一张相当漂亮的图片。但在尝试解读它时,请记住:'红色'和'绿色'通道是 X 和 Y 坐标查找表,'蓝色'是高光和阴影效果叠加,透明度通道保存最终图像的无效像素掩码。因此,让我们使用“扭曲”合成方法来应用它。

  magick mandrill_grid_sm.jpg   spherical_unified.png  \
          \( -clone 0,1 -alpha set -compose Distort -composite \) \
          \( -clone 1   -channel B -separate +channel \) \
          \( -clone 2,3 -compose HardLight -composite \) \
          \( -clone 4,1 -compose DstIn -composite \) \
          -delete 0--2  spherical_mandrill.png
[IM Output] ==> [IM Output]
按顺序...
  • 我们应用扭曲映射(包括掩码)
  • 从统一的图像映射中提取阴影映射
  • 将阴影映射应用于扭曲后的图像
  • 恢复阴影操作中丢失的掩码
  • 删除除最终图像外的所有内容并保存它
这其中的复杂性纯粹是由于需要提取阴影掩码,以及恢复阴影去除的 Alpha 掩码。

圆弧扭曲映射

仅仅为了展示使用位置扭曲映射真正能够实现什么,这里有一个绝对扭曲 LUT,类似于上面'弧形扭曲方法'提供的。基本上,我们不是为每个被扭曲图像中的每个像素计算坐标映射,而是将这些计算出的坐标保存到两个 X 和 Y 坐标灰度 LUT 图像中。也就是说,我们将整个扭曲预先计算到一个更简单的查找表图像中,允许它被重复应用,而无需进一步的平方根或三角函数。

  magick -pointsize 30 -font Candice label:Anthony -trim +repage \
          -gravity center -resize 95x95 -crop 100x100+0+0\! \
          -flatten text_image.jpg
  magick -size 100x100 xc: -channel G  -fx 'atan(j/(i+.5))*2/pi' \
          -separate   -flip -flop       map_p_angle.png
  magick -size 100x100 xc: -channel G  -fx '1-hypot(i,j)/(w*1.6)' \
          -separate   -transverse       map_p_radius.png
  magick text_image.jpg   map_p_angle.png map_p_radius.png \
              -fx 'p{u[1]*w,u[2]*h}'    distort_p_curved.jpg
[IM Output]
颜色源
 +
 
[IM Output]
角度 - X 映射
 +
 
[IM Output]
半径 - Y 映射
==>
 
[IM Output]
弯曲文本
当然,生成那个扭曲映射是困难的,但是一旦它被生成一次,使用任何你喜欢的方式(甚至使用像“Gimp”这样的图像编辑器进行艺术化处理),你就可以在大量的图像上重复使用它。

极坐标扭曲映射

有时你可能需要目标图像由扭曲映射定义,而不是源图像,仅仅是为了使事情正常工作。例如,如果我们想将一些文本映射到一个圆圈中(也称为极坐标变换),你确实需要能够使用一个高度大约是宽度 3 到 4 倍的图像(高纵横比),否则结果将难以阅读。为此,我们将扭曲映射图像放在颜色源图像之前,以便第一个(X 映射)图像将用于设置最终结果的大小,而不是输入源图像。

  magick -size 100x100 xc:  -channel G \
          -fx 'atan2(i-w/2,h/2-j)/pi/2 + .5' \
          -separate  map_p_angular.png
  magick -size 100x100 xc:  -channel G \
          -fx 'rr=hypot(i-w/2,j-h/2); (.5-rr/70)*1.2+.5' \
          -separate  map_p_radial.png
  magick -font Candice -gravity center -size 200x50 \
                                label:'Around  the  World'    text.jpg
  magick map_p_angular.png map_p_radial.png text.jpg \
                 -fx 'u[2].p{ u*u[2].w, v*u[2].h }' distort_p_circle.jpg
[IM Output]
角度 - X 映射
 +
 
[IM Output]
径向 - Y 映射
 +
 
[IM Output]
颜色源
==>
 
[IM Output]
圆形文本
本质上,颜色源图像现在可以是任何大小或纵横比,并且事情将被正确处理,但是你可能需要调整扭曲映射的生成以正确处理源图像的纵横比。在生成上面的映射时,值“70”控制圆的最终大小,中线放置在该圆上。“1.2”的值另一方面控制图像垂直缩放成圆圈,允许你调整扭曲文本的高度。
请记住,此“-fx”表达式要求先给出扭曲映射,并将颜色源作为第三个(索引 2)图像给出。但是,这也意味着存储在源图像中的任何元数据也将丢失。
此扭曲映射的问题在于,“X 映射”中存在非常明显的颜色不连续(由数学中的渐近线引起)。当你进行任何颜色查找或映射大小调整以生成更大的图像时,此线必须保持清晰。也就是说,你需要确保此映射的任何大小调整或插值查找不会在此渐近线上产生灰色查找颜色。

如果你确实在此线上生成灰色查找,那么你将在最终结果中得到一行彩色像素(从图像中间查找)。

因此,建议你始终以最终图像所需的大小生成此扭曲映射,并且永远不要使用之前显示的任何缩放技术。
你也可以将其用于其他效果,例如圆形棋盘...

  magick map_p_angular.png map_p_radial.png \
          -size 150x90 pattern:checkerboard \
          -fx 'u[2].p{ u*u[2].w, v*u[2].h }'   distort_check_circle.gif
[IM Output]
尝试 IM 提供的其他一些内置图案,以获得其他有趣的效果。
上面清楚地显示了使用“-fx”进行图像扭曲的限制。在图像的中心附近,径向线变得锯齿状,因为大面积像素合并成单个像素并没有发生。另一方面,图像的边缘,特别是角部,显示出径向线适当的模糊。

原因是“-fx”(以及大多数旧的扭曲方法)只对源图像中的颜色进行简单的未缩放插值查找。这意味着随着图像缩小,源图像像素不会合并在一起以产生目标像素的正确颜色。

对于放大区域(如角部)这不是问题,只有在极度压缩(中心)时才会出现。因此,一个解决方案是使用超级采样,但这只是将问题推迟到更高的压缩级别。
扭曲映射中相同的渐近线(突然变化)(从图像中心到底部)也在上面的示例中产生沿该线的颜色急剧变化。将该线与其他径向线(例如从中心到图像顶部)进行比较,由于之前提到的插值查找,这些线在接近图像边缘时变得非常模糊。

当使用可平铺图像(如上所示)生成圆形图案时,这可能是一个问题,并且可能需要一些特殊处理以避免图像该部分的可见差异。

为了避免这种情况,最好将图像的上半部分与下半部分分开扭曲,以避免渐近区域。

随机打乱行

在这个例子中,我们做了一些更不寻常的事情……随机打乱图像的行。首先,我们创建一个映射,该映射对于 X(红色通道)具有渐变,对于 Y(绿色通道)具有随机噪声图像。

  magick rose: \
          \( -size 46x70 gradient: -rotate -90 \) \
          \( -size 1x46 gradient: -spread 23 -scale 70x46\! \) \
          -compose Distort -define compose:args='' -composite \
          rose_row_shuffle.png
[IM Output] ==> [IM Output]
不幸的是,“-spread”似乎在其选择的要交换的像素中包含虚拟像素,这意味着某些行变成了重复,而其他行则完全丢失了。换句话说,“shuffle”图像映射并不完全正确。你有没有更好的像素随机打乱解决方案?

相对查找位移映射

如你所见,创建绝对扭曲映射相当容易创建和使用。但是,当扭曲具有“未定义”区域或扭曲超出源图像正常范围的区域时,它存在严重问题。更严重的问题是,你始终在处理渐变,这些渐变定义了颜色查找的绝对坐标。映射图像的任何部分都不是简单、干净或易于手动修改或编辑的。它们的创建和使用需要特殊的技术和数学。这意味着通常在“艺术”开发方面几乎没有可行性。但是,还有另一种使用查找表的方法来指定获取最终颜色的坐标。通过使用**相对位移映射**。映射不是定义从源图像查找每个像素颜色的确切坐标,而是定义相对于当前位置的偏移量或*位移*。现在,偏移量可以是正值或负值,负值需要一些技巧才能编码成颜色值。因此,他们所做的是定义“纯灰色”作为坐标的 0 位移(无变化)。然后,他们使“黑色”表示最大负位移,“白色”表示最大正位移。这可能难以描述,所以让我们看一个例子。首先,我们创建一个要“位移”的测试图像。

  magick -font Candice -gravity center -size 150x50 \
                                           label:'Anthony'    label.jpg
[IM Output]
现在,我将使用一些“魔法”来创建一个带有“纯白色”和“纯黑色”区域的“纯灰色”图像。

  echo "P2 5 1 255\n 127 0 127 255 127" |\
                magick - -scale 150x50\! -alpha off   displace_map.jpg
[IM Output]
现在,要使用此图像作为“位移映射”,我们从位移映射中获取“灰色值”,并将其添加到 X 和 Y 坐标(或两者)。也就是说,我们根据位移映射的“灰色程度”相对于当前位置位移查找。以特殊方式处理“值”,因此“纯灰色”表示查找点的零位移(在这种情况下仅为 Y 坐标),但“最大位移”用于“白色”(正)或“黑色”(负)值。例如,让我们将位移映射应用于我们的“标签”图像。

  magick label.jpg  displace_map.jpg  -virtual-pixel Gray \
          -fx 'dy=10*(2*v-1); p{i,j+dy}'   displaced.jpg
[IM Output]
如你所见,图像的各个部分看起来像根据位移映射的颜色“移动”。“白色”区域会将给定的“位移值”添加到查找点,因此在该区域中,每个像素都查找源图像“10”像素“向南”(正 Y 方向)。结果,它看起来好像源图像向上移动了。请记住,是查找被位移了,而不是实际的图像本身,这就是为什么它看起来向上或白色方向为负方向移动的原因。在具有“黑色”位移的区域也观察到了类似的效果。源图像看起来向下移动了,因为查找位移是在负方向完成的。仔细思考一下。你还会注意到“位移查找”实际上可以超出正常的图像边界,允许你使用虚拟像素设置来控制这些超出边界的像素。在上面,我只是请求返回一个灰色像素。上面示例中的“最大位移”值“10”非常重要,它是源图像的任何部分看起来移动的最大相对距离,用于映射图像中的“纯白色”或“纯黑色”位移值。你不能将查找以及输入图像位移到超过此值的任何位置。最大白色或黑色值与中心无位移 50% 灰色值之间的其他灰色阴影将以适当的量位移查找。因此,25% 灰色值将使查找在负方向上位移 1/2 位移值,而 75% 灰色值将使查找在正方向上位移 1/2 该值。此值是绝对扭曲映射相对位移映射之间的关键区别。你可以增加或减少相对位移,使图像或多或少地扭曲,只需更改位移值即可,而无需更改位移映射本身。此外,由于“零位移”映射只是一个纯灰色或 50% 灰色,而不是复杂的渐变,因此你可以从一个简单的灰色图像开始,并通过艺术方式使区域变亮或变暗以生成所需的位移。你可以通过简单地绘制形状或区域来做到这一点,而无需复杂的精确数学公式。最后,由于所有位移都是相对的,因此边缘效应产生的疯狂值不会产生疯狂或随机的像素颜色。事实上,正如你将看到的,平滑或模糊位移映射实际上是一件好事,因为它消除了你可以在上面示例中看到的脱节或不连续的“切割”效果。**总之**位移映射更易于控制和艺术化,提供局部位移,无需复杂且精确的数学运算,并且对错误、边缘效应甚至位移映射模糊非常宽容。它非常适合简单的“位移”类型扭曲,例如在生成水、波浪、扭曲镜子、光的弯曲、透镜状效果或磨砂或气泡玻璃效果等效果时。另一方面,高度数学的扭曲(如“极坐标”、“旋转”和“透视”扭曲,或其他真实世界的 3D 类型映射)不容易实现。也就是说,这并不是说它不可能,因为稍后我们将展示你实际上可以在两种映射样式之间进行“魔法”,只是更困难。

合成位移方法

我们使用了DIY FX 运算符来进行置换映射,这样您就可以看到实际执行的操作。但这是一种缓慢的技术。但是,有一个等效的内置合成运算符,“Displace”。以下是使用方法...

    magick {image} {displacement_map} \
            -compose Displace   -define compose:args={X}x{Y} \
            -composite   {result+}

    magick {image} {displacement_map} \
            -compose Displace   -set option:compose:args {X}x{Y} \
            -composite   {result+}

    magick composite {displacement_map} {image} \
              -displace {X}x{Y}    {result+}

注意顺序,尤其是在“magick composite”命令中。

使用“-set”而不是 define 也允许您在参数中使用百分比转义符。'X' 和 'Y' 值定义了将用于给定置换图中“白色”和“黑色”颜色的方向和“最大位移”。您可以定义一个或两个值,以便允许您沿任何特定方向进行位移。也就是说,通常置换图在某个随机方向上提供线性位移,最大强度由'X' 和 'Y' 值控制。然后,“地图图像”设置从负最大值(黑色)到正最大值(白色)应用该最大值的多少,完美的灰色表示该像素的查找没有位移。例如,以下是我们上面提到的相同的 Y 位移示例...

  magick label.jpg  displace_map.jpg  -virtual-pixel Gray \
          -compose Displace -define compose:args=0x10 -composite \
          displaced_y.jpg
[IM Output]
您还可以使用其他设置,如“-geometry”和“-gravity”设置,来调整置换图叠加在图像上的区域。置换图产生的像素查找仍然可以引用图像叠加部分之外的区域,并将它们复制到叠加区域中。

简单的位移示例

一个原始颜色区域的置换图,没有任何平滑过渡,通常会在生成的图像中不同区域之间产生不连续(不连续)的位移,就像您上面看到的。事实上,您可以使用这种技术生成一个像在看裂纹镜子一样“断裂”的置换图。例如,请参见下面的破碎的镜子。如果颜色从一个区域平滑地流向另一个区域,您可以产生更美观和更平滑的结果。例如,通过模糊生成的置换图,您可以在位移区域之间生成波浪状过渡...

  magick displace_map.jpg  -blur 0x10   dismap_wave.jpg
  magick label.jpg  dismap_wave.jpg  -virtual-pixel Gray \
          -compose Displace -define compose:args=0x10 -composite \
          displaced_wave_y.jpg
[IM Output]
[IM Output]
除了沿 Y 方向位移图像之外,您还可以使用地图沿 X 方向位移图像,从而产生一种压缩波。

  magick label.jpg  dismap_wave.jpg  -virtual-pixel Gray \
          -compose Displace -define compose:args=10x0 -composite \
          displaced_wave_x.jpg
[IM Output]
通过对 X 和 Y 方向都使用相同的置换图,我们可以同时添加压缩波和振幅波。

  magick label.jpg  dismap_wave.jpg  -virtual-pixel Gray \
          -compose Displace -define compose:args=10x10 -composite \
          displaced_wave_xy.jpg
[IM Output]
请注意,图像仍然沿单个线性方向位移,导致上面图像在向下斜坡上拉伸,在向上斜坡上挤压在一起。也就是说,失真是在一个角度或“向量”上执行的,具有水平和垂直分量。您可以看到这种效果非常像在水下,图像被水面上的轻微波纹扭曲。但是,失真图可以包含原始图像的多个副本,就像在反射或折射图像中一样...

  echo "P2 3 1 255\n 255 127 0 " | magick - -scale 150x50\! dismap_copy.jpg
  magick label.jpg  dismap_copy.jpg \
          -compose Displace -define compose:args=66x0 -composite \
          displaced_copy.jpg
[IM Output]
[IM Output]
您还可以通过使用渐变创建图像部分的镜像翻转或翻转。例如,在这里,您可以使用线性置换图将像素从图像的一侧复制到另一侧。

  magick -size 50x150 gradient: -rotate -90  -alpha off  dismap_mirror.png
  magick label.jpg  dismap_mirror.png \
          -compose Displace -define compose:args=150x0 -composite \
          displaced_mirror.jpg
[IM Output]
[IM Output]
你能弄清楚这个置换图是如何工作的吗?提示:找出最左边和最右边边缘的位移,然后查看图像的其余部分如何适应它。但是,由于您再次使用渐变图像,因此您失去了置换图的简单性。因此,镜子最好使用图像上的直接翻转操作或使用绝对失真图来完成。请注意,通过翻转渐变,您会缩小图像。

  magick -size 50x150 gradient: -rotate 90  -alpha off  dismap_shrink.png
  magick label.jpg  dismap_shrink.png \
          -compose Displace -define compose:args=150x0 -composite \
          displaced_shrink.jpg
[IM Output]
[IM Output]
上面也演示了置换图的一个特定问题。当图像的一个区域(或全部)压缩超过 50% 时,您将开始生成混叠伪像。这在清晰可见的阶梯状“混叠”边缘中尤其明显。如前所述,解决此问题的一个方法是超采样用于生成每个输出像素的像素数量。为此,我们将图像和置换图都放大,然后将生成的图像重新调整为更正常的尺寸。这将允许更多像素参与特定像素在结果中的设置,从而产生更好的图像。例如...

  magick label.jpg  dismap_shrink.png  -resize 200% \
          -compose Displace -define compose:args=400x0 -composite \
          -resize 50%    displaced_resize.jpg
[IM Output]
一个更好、更平滑的结果,尽管可能有点模糊。 绘制渐变图直接源于上述示例的想法是,通过使用简单线条的 Y 位移,您可以生成置换图颜色的图形。例如,这里我生成一个数学sinc()函数(定义为'sin(x)/x'),并通过将其用作置换图来绘制该渐变...

  magick -size 121x100 xc: -fx 'sin(i*24/w-12)/(i*24/w-12)/1.3+.2' \
                                                      gradient_sinc.gif
  magick -size 121x100 xc: -draw 'line 0,50 120,50'     graph_source.gif
  magick graph_source.gif gradient_sinc.gif \
          -compose Displace -define compose:args=0x49 -composite \
          displace_graph.gif
[IM Output]  + [IM Output] ==> [IM Output]
正如您所看到的,它确实有效,尽管我不想将其用于数学绘图。最好使用合适的绘图软件包。但是,此技术可用作绘制图像中一行或一列像素强度的简陋方法。它所做的是显示位移的巨大差异如何容易产生不连续或不平滑的结果。“图形源”中的每个单独像素仅被逐一查看,没有进行平均,从一个像素到下一个像素的位移查找的巨大差异可能会导致结果发生较大的颜色变化。寓意是置换不仅最适合平滑的置换图,而且最适合位移包含大面积或色调的图像。它不适用于锐利的细线。当然,您可以通过再次超采样失真图来改进...

  magick graph_source.gif gradient_sinc.gif  -resize 400% \
          -compose Displace -define compose:args=0x196 -composite \
          -resize 25%   displace_graph_2.gif
[IM Output]
结果好多了,虽然不如使用绘图软件包所能达到的效果好。仍然只使用了 ImageMagick 来创建它。这是同一图形的另一个版本,但这次使用纯色,这比位移细线效果好得多。

  magick -size 121x50 xc:white xc:black -append \
          gradient_sinc.gif  -resize 400% \
          -compose Displace -define compose:args=0x196 -composite \
          -resize 25%   displace_graph_3.gif
[IM Output]

区域位移(线性)

让我们尝试一个更合乎逻辑的位移问题。将图像的一个区域从一个位置直线移动到另一个位置。正如我们所看到的,“纯灰色”图像不会导致任何位移,而“白色”颜色会导致从源图像进行正向查找位移。例如,让我们创建一个这样的图像....

  magick -size 75x75 xc:gray50 -fill white \
          -draw 'circle 37,37 37,20'  dismap_spot.jpg
[IM Output]
现在,当我们应用此图像时,标记区域的内容应该具有出现在给定位移值方向上的任何内容的副本。因此,让我们尝试一个X+10 和Y+10 或'10x10'的位移值...

  magick koala.gif dismap_spot.jpg \
          -compose Displace -define compose:args=10x10 -composite \
          displace_spot.png
[IM Output]  + [IM Output] ==> [IM Output]
正如您所看到的,标记区域的内容现在包含了位于东南方向+10,+10像素处的图像副本。基本上是考拉“尾巴”的图像。换句话说,在圆圈内,图像向东北位移,或-10,-10像素。请记住,位移是查找的位移,因此由于反向像素映射,源图像会向负方向移动。图像向反方向位移!另请注意,移动的是标记区域内的图像。您不是在位移标记的图像,而是在将图像移入标记区域。最后,请注意圆形边缘处的急剧不连续性。标记区域内的区域被移动,而外部区域保持不变。这些是事实,因此值得重复。
位移使图像向与值相反的方向移动。
只有标记的非灰色区域才会被位移。
颜色急剧变化会导致图像急剧不连续。
因此,让我们尝试一些更实用的东西。让我们将考拉鼻子和眼睛之间的中心(位于'32,22')移动到白色(完全正位移)圆圈的中心(位于'37,37')。这需要一个'-5,-15'的位移值(记住它是反方向的)...

  magick koala.gif dismap_spot.jpg \
          -compose Displace -define compose:args=-5x-15 -composite \
          displace_head.png
[IM Output]
在那里,我们有一个考拉头部中央部分的精美居中副本。但是图像仍然“不连续”,并且使用负值不是很好。解决方案是使用黑点,但也模糊黑点的边缘。另外,让我们将其放大以包含考拉头部的更多部分。所以这是我们的“正运动点”图像...

  magick -size 75x75 xc:gray50 -fill black \
          -draw 'circle 37,37 37,17'  -blur 0x5  dismap_area.jpg
[IM Output]
您不希望过度模糊图像,否则点的中心将不再是纯黑色。或者,您可以简单地规范化反向色阶调整图像以确保绘制区域为黑色,周围部分为完美的灰色。您将在后面的示例中看到很多这样的操作。现在让我们使用我们的黑色“模糊点”置换图重复上一个“头部”位移。

  magick koala.gif dismap_area.jpg \
          -compose Displace -define compose:args=5x15 -composite \
          displace_area.png
[IM Output]
正如您所看到的,我们将图像+5,+15移动到“模糊”区域,但这次区域的边界更平滑并连接到图像的其余部分。当然,圆圈边缘的耳朵因模糊边缘而变形,考拉的身体也受到压缩,但它仍然比我们之前看到的要好得多。为了防止在尾随侧看到的图像“撕裂”或留下位移部分的副本,您需要扩展该点,或制作更复杂的渐变类型的置换图像。例如,假设您想将考拉的头部从其起始位置'32,22'移动到图像的中心'37,37',或移动+5,+15像素,但您想调整整个图像以适应此更改,以获得更平滑的效果。为此,您希望在'37,37'处具有黑色(正图像位移)的最大位移并位移+5,+15的值。但您还需要确保图像的其余部分保持完整,方法是将角固定在 50% 灰色。也就是说,非常适合谢泼德插值的稀疏渐变

  magick -size 75x75 xc:  -sparse-color  Shepards \
          '37,37 black   0,0 gray50  74,74 gray50  0,74 gray50  74,0 gray50' \
          dismap_move.jpg
  magick koala.gif dismap_move.jpg \
          -compose Displace -define compose:args=5x15 -composite \
          displace_move.png
[IM Output]  + [IM Output] ==> [IM Output]
正如您所看到的,您获得了更大的位移区域,该区域分布在整个图像上。结果比之前使用的更紧凑的“点”方法产生的图像变化更加平滑。这实际上与谢泼德失真完全相同,但仅适用于一个移动控制点。这也是 Fred Weinhaus 脚本'shapemorph'中使用的方法,但带有一些动画效果。总之:对于小的局部位移,可以使用“模糊点”位移。但对于较长的距离上的较大位移,应使用较大的平滑渐变置换图来防止撕裂或复制源图像。
正在建设中

简单的置换变形

Modifying the Size of Displacement Vectors
Two Image Morphing
Random 1D Displacements

波纹水反射

如前所述,置换图尤其适用于生成水和玻璃状失真。[IM Output]在此示例中,我通过裁剪花朵图像生成了一个小图像。现在我想让它看起来像放在一些波光粼粼的水面上。为了生成波纹,我需要相同大小的正弦波渐变,我可以使用评估正弦函数来生成它。“8”表示将添加到渐变中的“波”的数量。

  magick -size 150x80 gradient:  -evaluate sin 8  wave_gradient.png
[IM Output]
现在让我们使用有角度的位移向量来扭曲该图像,而不仅仅是简单的垂直或水平扭曲,以便使其更加突出。

  magick composite wave_gradient.png  flower.jpg -displace 5x5 flower_waves.png
[IM Output]
现在这看起来不是很有趣,但是如果您翻转该图像、垂直压缩它并将其附加到原始图像会怎样...


  magick flower_waves.png -flip \
          flower.jpg  +swap -append  flower_waves_2.png
[IM Output]
不幸的是,它看起来仍然有点人工。原因是图像顶部和底部的反射看起来相同。它没有“深度”感。反射的亮度也与原始图像相同,这种情况很少见。为了使其更逼真,您需要使用强度变化的波纹图案。下面使用一些花哨的渐变数学来“衰减”我们上面使用的波浪渐变。也就是说,我们使波浪图案从上到下线性变小。这种技巧确保波浪在图像底部(稍后翻转)以纯灰色或“无位移”颜色结束。

  magick -size 150x80 gradient: \
          \( wave_gradient.png \
             +clone -compose multiply -composite \) \
          \( -clone 0 -negate -evaluate divide 2 \
             -clone 1 -compose plus -composite \) \
          -delete 0-1      waves_decreasing.png
[IM Output]
所以让我们应用这个渐变,形成一个新的花朵反射。我还稍微加深了反射图像的亮度,以表示一些光线损失到水中本身,使其看起来更像水反射。

  magick flower.jpg  waves_decreasing.png  \
          -compose Displace -define compose:args=8x8 -composite \
          -flip   +level 0,80% \
          flower.jpg  +swap -append   flower_in_water.png
[IM Output]
请注意,由于扭曲的图像被翻转以形成反射。此外,图像在靠近与原始图像连接的“水面”顶部的“波纹”将比底部少。这使得扭曲产生了一种与观察者距离的感觉。您可以通过稍微旋转、弧线或只是使用“随机”位移来扭曲波浪位移图,使其更逼真。这将使波浪看起来更自然。虽然最好在“衰减”之前进行,以便之后添加“深度”。试试看,做些实验,并告诉我你的想法。
Future Animated Ripples -
  Using -function Sinusoid with phase changing

二维位移映射

到目前为止,所有相关的位移图都只在一个方向上移动图像。尽管可以通过设置相应的“XxY”位移值或“向量”将该方向设置为任何所需的角度。但是,您可以通过使用两个单独的位移来产生更复杂的位移,其中图像可以在任何方向上以任何数量进行位移。为此,我们需要为 X 和 Y 方向分别创建两个位移图。以下是您可以使用的命令……

magick {image} {X displacement} {Y displacement} \
        -compose Displace   -define compose:args={X}x{Y} \
        -composite   {result+}

magick {image} {X displacement} {Y displacement} \
        -compose Displace   -set option:compose:args {X}x{Y} \
        -composite   {result+}

composite {X displacement} {image} {Y displacement} \
          -displace {X}x{Y}    {result+}
请注意“magick composite”命令中的输入图像顺序。奇怪的排序是由需要滥用“magick composite”选项处理以及历史原因造成的。您务必正确设置它。因此,我建议您使用“magick”命令,而不是“magick composite”。
在 IM v6.4.4 之前,使用 2 个单独的位移图进行单独的 X 和 Y 位移是一个碰运气的事情。它有时有效,有时无效。不建议尝试在 IM 的旧版本上使用它。
此外,就像使用统一位移图一样,您可以使用单个“统一位移图”。如果只提供了一个位移图像,则 X 位移将从“红色”通道中查找,Y 位移将从“绿色”通道中查找,并且任何 alpha 遮罩也将从位移图传输到最终图像。“蓝色”通道被忽略。
在内部,“magick”和“magick composite”实际上都合并了两个图像(如果提供),以便生成一个“统一位移图”,然后将其传递给内部 API。

这不会影响我们之前查看和先前给出的位移图是灰度图像,因此“红色”和“绿色”通道都相同。

圆柱形位移

在 IM 论坛中多次出现的一个问题是如何将图像映射到圆柱体上,例如将其叠加在咖啡杯或软饮料罐上。这是解决方案……

  magick rose: -background black -gravity south -splice 0x8 \
          \( +clone -sparse-color barycentric '0,0 black 69,0 white' \) \
          \( -clone 1 -function arcsin 0.5 \) \
          \( -clone 1 -level 25%,75% \
                 -function polynomial -4,4,0 -gamma 2 \
                 +level 50%,0 \) \
          -delete 1 \
          -virtual-pixel black  -define compose:args=17x7 \
          -compose Displace  -composite   rose_cylinder.png
[IM Output]
以上内容非常复杂,但本质上是同时使用了两个单独的位移。X 方向上的反正弦()压缩和 Y 方向上的圆弧位移。以下是命令的作用……
  • 加载“rose”图像并为垂直位移添加一些空间
  • 为以后的数学函数创建一个水平数学渐变
  • magick 渐变的副本以生成压缩位移图
  • magick 另一个副本到垂直椭圆弧位移
  • 移除线性渐变
  • 准备并进行位移
结果...一朵玫瑰正确地包裹在圆柱体的 30 度等轴测视图中。分解上述命令以保存和查看各个位移图。需要记住的关键是,两个地图位移执行 X 和 Y 值的查找,以计算哪个像素应该出现在查找位置。请记住,位移实际上不是源图像的位移,而是源图像中查找的位移。这种位移扭曲方法已内置于 Fred Wienhaus 的“cylinderize”脚本中。

碎镜

您可以通过生成 X 和 Y 位移的随机区域,为图像创建“碎镜”外观。

  magick dragon_sm.gif -sparse-color voronoi '  \
                  %[fx:rand()*w],%[fx:rand()*h]  red
                  %[fx:rand()*w],%[fx:rand()*h]  lime
                  %[fx:rand()*w],%[fx:rand()*h]  black
                  %[fx:rand()*w],%[fx:rand()*h]  yellow
               ' -interpolate integer -implode 1     mirror_areas.gif
  magick  mirror_areas.gif -channel R  -separate   mirror_dismap_x.gif
  magick  mirror_areas.gif -channel G  -separate   mirror_dismap_y.gif

  magick composite mirror_dismap_x.gif  dragon_sm.gif  mirror_dismap_y.gif -alpha off \
            -background white -virtual-pixel background -displace 7 \
                                                        mirror_displaced.gif

  magick  mirror_areas.gif -edge 1 -threshold 20% \
            -evaluate multiply .7 -negate               mirror_cracks.gif
  magick composite mirror_displaced.gif  mirror_cracks.gif -compose multiply \
                                                        mirror_cracked.gif
[IM Output] ==> [IM Output] [IM Output] [IM Output] ==>  
  [IM Output] [IM Output] ==> [IM Output]
使用随机化的Voronoi 稀疏颜色图像生成四个随机位移区域。然后对其进行内爆扭曲以将这些区域扭曲到图像的中心。由于四个彩色区域保持为纯色,因此每个区域将包含原始图像的未失真但已位移的副本。但是每个区域都以不同的方式位移了图像,就像碎镜的每一块碎片一样。为了完成镜子,使用边缘检测来勾勒区域的边缘,从而勾勒出所得图像的碎裂性质。也就是说,裂缝也变得可见。
从技术上讲,我不必分离生成的彩色随机位移图中的“红色”和“绿色”通道。我可以直接使用它们,因为 X 位移是从“红色”通道中查找的,而 Y 位移是从“绿色”通道中查找的。也就是说,我可以直接使用“mirror_areas.gif”图像作为“统一位移图”。
正在建设中

谢泼德位移

随机位移

透镜效果

磨砂玻璃效果

色散效果(旋转位移)

具有随机位移的色散效果

未来:其他可能的扭曲/位移映射示例
  • 将渐变光线跟踪到 3D 对象上,以便稍后可以将任何图像映射到这些对象上。
    • X 和 Y 渐变映射图像
    • 用于颜色、高光和阴影的纯灰色图像

可变模糊映射

添加到 ImageMagick 版本 6.5.4-0 中,“-compose”方法“Blur”为您提供了一种根据映射图像替换每个像素的椭圆高斯平均值(模糊)的方法。

    magick composite -blur {Xscale}[x{Yscale}[+{angle}]]          blur_map  image   result

    magick image  blur_map \
        -define compose:args='{Xscale}[x{Yscale}[+{angle}]]' \
        -compose blur -composite   result

    magick image  blur_map \
        -set option:compose:args '{Xscale}[x{Yscale}[+{angle}]]' \
        -compose blur  -composite   result

请注意,此图像合成需要使用操作参数,该参数可以通过多种方式设置。有关更多详细信息,请参阅全局定义的工件。通过使用可变映射来控制模糊,您可以模糊图像的一部分,同时完全保留另一部分,或者您可以产生诸如倾斜移位效果之类的效果,其中真实世界图像看起来更像是一个小型人工模型。

例如,这里我模糊了考拉图像的一半,同时完全保留了另一半未模糊……

  magick -size 37x75 xc:black -size 38x75 xc:white +append  blur_map_bool.gif
  magick koala.gif blur_map_bool.gif \
          -compose blur -define compose:args=3 -composite \
          blur_koala_bool.gif
[IM Output]  + [IM Output] ==> [IM Output]
如您所见,“blur_map”图像上为“白色”的任何像素都使用给定的最大“sigma”值进行了模糊,而“黑色”的任何像素都没有进行模糊。换句话说,您有一个非常简单的遮罩模糊。当然,这可以通过许多其他方式实现,但这并不能解释模糊映射的强大之处。使这种模糊映射多功能的是它在图像中是可变的。也就是说,如果模糊映射颜色为灰色,那么您将获得相应的较小的模糊结果,使用较小的“邻域”来获取该像素。但是黑色不会模糊,而白色则根据给定的值进行最大模糊。需要注意的一件事是,只有模糊的区域需要额外的处理时间。未模糊的像素不需要这种额外的处理。这使得上述方法比使用遮罩合成(与模糊整个图像并合并结果相同)快得多。当处理图像非常小区域的大模糊时,这种节省时间可能变得更加重要。例如,让我们使考拉逐渐向他的脚变得更模糊……

  magick -size 75x75 gradient:black-white blur_map_gradient.gif
  magick koala.gif blur_map_gradient.gif \
          -compose blur -define compose:args=3 -composite \
          blur_koala_gradient.gif
[IM Output]  + [IM Output] ==> [IM Output]
这是相同的模糊,但显示了模糊如何随高度变化。

  magick blur_map_bool.gif blur_map_gradient.gif \
          -compose blur -define compose:args=15 -composite \
         blur_edge_gradient.gif
[IM Output]  + [IM Output] ==> [IM Output]
有关可变映射模糊的实际示例,请查看照片倾斜移位效果距离模糊阴影字体。请注意,它是围绕每个像素的邻域用于生成该像素的“模糊颜色”。这意味着,即使您可能指定图像的某些部分不应模糊,来自该未模糊区域的颜色也可能用作周围像素模糊的一部分。也就是说,仅仅因为某个区域没有模糊并不意味着来自该区域的颜色不用作其他模糊像素结果的一部分。也就是说,来自未模糊区域的颜色可能会“泄漏”到周围的模糊区域。要模糊背景而不包括前景像素,您需要使用读取遮罩技术来防止它们被读取作为模糊操作的一部分。

椭圆形模糊

Blur”合成设置使用与正常的模糊或高斯模糊运算符不同的技术,因为它通过使用高斯椭圆区域重采样算法实现,该算法是为缩放图像重采样而开发的,作为广义扭曲运算符的一部分。用于邻域重采样的椭圆区域也使这种模糊方法比运算符“-blur”和“-gaussian-blur”提供的普通均匀“圆形”模糊更加多功能。椭圆本身由模糊区域的 sigma 的“宽度”和“高度”定义。椭圆还可以通过给定的“角度”(顺时针方向)从正交对齐旋转。例如,在下图中,我们显示了单个像素的模糊颜色如何从旋转的椭圆区域获取其颜色,这基于给定的 sigma 值。然后根据高斯滤波器(使用椭圆距离公式,以生成模糊颜色)对该区域中的像素进行加权平均。

  magick koala.gif -compose blur -define compose:args=5x1-30 -composite \) \
             elliptical_blur.gif
  # ... other commands to create diagram of blur effect ...
[IM Output]
如前所述,这与广义扭曲运算符用于生成其扭曲图像的颜色完全相同的颜色查找方法,因为它允许将源图像区域的缩放(和过滤)合并到一个像素中,尤其是在极端扭曲中,例如查看远方地平线中所示的示例。有关此过程的更多详细信息,请参阅区域重采样重采样滤波器

作为可变模糊映射可用的椭圆控制的示例,让我们使用与之前使用的相同渐变模糊映射的黑点。但这次我们将缩放一个细长的水平椭圆“30x0”,而不是一个圆形。“x0”可能看起来很奇怪,但基本上意味着不应该看到垂直模糊,只是生成良好结果所需的最小高度的椭圆。


  magick -size 75x75 xc: -draw 'circle 36,36 36,8'  black_circle.gif
  magick black_circle.gif blur_map_gradient.gif \
          -compose blur -define compose:args=15x0 -composite \
          blur_horizontal.gif
[IM Output]  + [IM Output] ==> [IM Output]
正如你所看到的,模糊的程度仍然会随着提供的映射图像而变化,在图像顶部产生非常少的模糊,而在底部产生大量的模糊。但也要注意,底部边缘在水平方向上向两个方向模糊的程度相同,但在垂直方向上则不相同,从而在垂直方向上产生了清晰的切断。通过给出一个第三个角度参数来旋转细长的椭圆,或者通过直接定义一个垂直椭圆,你就可以只在垂直方向上模糊图像…

  magick black_circle.gif blur_map_gradient.gif \
          -compose blur -define compose:args=0x15 -composite \
          blur_vertical.gif
[IM Output]
但是请注意,模糊并没有均匀应用!上半部分看起来比下半部分模糊程度低,因为这就是“映射图像”告诉它要做的。这反过来又扭曲了图像,使其看起来因模糊效果而略微截断。最后,让我们再做一次,但这次使用一个旋转 45 度固定角度的水平椭圆。

  magick black_circle.gif blur_map_gradient.gif \
          -compose blur -define compose:args=15x0+45 -composite \
          blur_angle.gif
[IM Output]
图像可能看起来很奇怪,这是因为可变模糊映射是垂直的,而模糊本身是倾斜的,由于椭圆角度和模糊映射的角度不一致,从而产生了奇怪的效果。
请注意,使用这种细长的椭圆实际上比使用单个大圆圈要快得多。“-blur” 运算符通过使用两个独立的水平和垂直模糊来提高速度,而 “-gaussian” 模糊运算符则以一种比刚刚描述的“Blur”合成方法更简单的方式对二维 卷积 进行完整处理。

具有可变纵横比的模糊

到目前为止,我们已经使用“模糊映射”改变了用于模糊的椭圆区域的大小。然而,虽然椭圆的大小甚至其角度都可以旋转,但其形状和角度保持不变。现在,“模糊映射”是一个由三个颜色通道组成的图像:红色、绿色和蓝色。当我们使用灰度图像时,所有三个颜色通道的值都相同。但是,在内部,椭圆的宽度仅按红色通道值缩放,而高度则按绿色通道值缩放。蓝色通道值的任何影响通常都会被忽略,除非在稍后我们将要讨论的特殊情况下。这意味着可以通过对单个红色和绿色通道使用不同的映射来改变椭圆的形状或其“纵横比”。与普通的模糊映射一样,零(或仅在该通道中为“黑色”)值将导致最小宽度或高度,而最大值(或“白色”)将导致给定的模糊量。例如,在这里我可以将图像划分为四个部分,使图像的两个四分之一部分水平模糊(红色通道最大),同时使其他区域垂直模糊(绿色通道最大)。对于此示例,我在 组合 它们成一个现在五彩缤纷的“模糊映射”之前,分别生成了宽度和高度映射。在正常情况下,你可以通过任何你想要的方式创建映射,甚至可以使用预先准备好的映射来实现特定的模糊效果。

  magick -size 2x2 pattern:gray50 -sample 75x75! blur_map_r.gif
  magick blur_map_r.gif -negate blur_map_g.gif
  magick blur_map_r.gif blur_map_g.gif -background black \
          -channel RG -combine blur_map_aspect.gif
  magick black_circle.gif blur_map_aspect.gif \
          -compose blur -define compose:args=10x10 -composite \
          blur_aspect.gif
[IM Output]  + [IM Output] ==> [IM Output]
[IM Output]  + [IM Output] ==> [IM Output]
当然,你仍然可以为椭圆设置一个固定角度。

  magick black_circle.gif blur_map_aspect.gif \
          -compose blur -define compose:args=15x15+45 -composite \
          blur_aspect_angle.gif
[IM Output]
在 IM 6.5.8-8 版本之前,发现了一个处理倾斜垂直椭圆模糊的错误。

具有可变角度的模糊

到目前为止,用于模糊图像的椭圆的角度一直是整个图像上的一个恒定角度。也就是说,用于模糊的椭圆始终处于相同的角度,即使可以通过修改模糊映射的红色和绿色通道来改变椭圆的纵横比。从 IM v6.5.8-8 版本开始,你可以使用模糊映射图像的蓝色通道为模糊提供可变的角度。这是通过给模糊参数提供两个角度来实现的。第一个角度参数用于定义蓝色通道中零值(“0”或“黑色”)的角度,而给出的第二个角度用于定义蓝色通道的最大值(“QuantumRange”或“白色”)。如果只给出一个角度值,则该角度用于设置零和最大“蓝色”通道值的角度,这基本上意味着角度变得固定,无论“模糊映射”图像的蓝色通道中存在什么值。这就是为什么在前面的示例中,角度一直是恒定的。例如,这里我使用一个水平模糊的椭圆,但随后使用蓝色通道在图像中心的 +0 到 +360 度范围内改变椭圆的角度。映射生成使用极坐标渐变,其详细信息可以在 扭曲渐变 中找到。请注意,当将该渐变置于蓝色通道时,我如何使用 -background 颜色设置与 组合运算符 一起使用,以确保红色和绿色通道都设置为最大值(“白色”),因此它不会缩放倾斜的椭圆。当然,这意味着在最终的映射图像中,白色表示使用最大角度,而黄色(或零蓝色通道值)表示最小角度。

  magick -size 100x300 gradient: -rotate 90 \
          +distort Polar '36.5,0,.5,.5' +repage -flop gradient_polar.jpg
  magick gradient_polar.jpg -background white \
          -channel B -combine blur_map_angle.jpg
  magick koala.gif blur_map_angle.jpg \
          -compose blur -define compose:args=5x0+0+360 -composite \
          blur_rotated.jpg
[IM Output] ==> [IM Output]
[IM Output]  + [IM Output] ==> [IM Output]
结果如你所见,是一个旋转模糊的图像。将结果与使用的模糊映射进行比较。在图像顶部,渐变为白色或黑色,根据所使用的参数,这意味着椭圆的角度为 0 或 360,因此椭圆保持水平。在底部,渐变为纯灰色,因此使用了给定范围之间的中间角度,即 180 度。这意味着椭圆再次保持水平。但在图像尺寸处,渐变为 25% 或 75% 灰色。因此,角度为 90 或 270 度,使椭圆垂直旋转。所有其他角度也随之变化,导致椭圆围绕图像平滑旋转。但是,结果图像的中心模糊得非常奇怪!这是因为椭圆的大小保持不变,并且不会向图像中间适当地缩小。解决方案是也使用红色和绿色通道设置椭圆的大小。例如。

  magick -size 106x106 radial-gradient: -negate \
          -gravity center -crop 75x75+0+0 +repage gradient_radial.jpg
  magick gradient_radial.jpg gradient_radial.jpg gradient_polar.jpg \
          -channel RGB -combine blur_map_polar.jpg
  magick koala.gif blur_map_polar.jpg \
          -compose blur -define compose:args=10x0+0+360 -composite \
          blur_polar.jpg
[IM Output]  + [IM Output]  + [IM Output] ==> [IM Output]
[IM Output]  + [IM Output] ==> [IM Output]
一个更好的结果。但是请注意,虽然结果看起来不错,但模糊椭圆没有像真正的旋转模糊图像那样正确地弯曲成弧形。因此,以上只是对真实旋转模糊的近似。但对于较小的模糊距离(相当于模糊角度),它相当不错。执行旋转模糊的更好方法是使用特殊的 极坐标-去极坐标扭曲技术,或当前错误命名的 径向模糊运算符。通过更改用于椭圆角度(蓝色通道)的角度范围,你可以轻松地将上述内容变成径向模糊,随着距中心的距离增加而变得更加模糊。

  magick koala.gif blur_map_polar.jpg \
          -compose blur -define compose:args=5x0+90+450 -composite \
          blur_radial.jpg
[IM Output]
但是,你还可以做更多的事情,而不仅仅是这些径向/旋转模糊,因为你可以通过整个图像上的任意数量来旋转和缩放任何位置的模糊。你拥有完全的控制权。例如,你可以通过使用不同的角度范围使模糊椭圆的角度与图像中心周围的角度不匹配,从而创建两种模糊的非常奇怪的混合。

  magick koala.gif blur_map_polar.jpg \
          -compose blur -define compose:args=10x0+0+180 -composite \
          blur_weird.jpg
[IM Output]
基本上,你现在可以完全控制图像的哪些部分以及如何模糊。并且通过使用模板,你可以创建整个模糊效果库。