ImageMagick 示例 --
扭曲图像

索引
ImageMagick 示例前言和索引
通用扭曲技术
 
仿射矩阵变换(单独的子目录)
 
广义扭曲运算符
 
扭曲方法简介
 
仿射(三点)扭曲方法
四点扭曲方法
多项式 扭曲
圆形和径向扭曲方法
投影扭曲
多点和自由变形扭曲
在研究了 IM 自早期就提供的简单内置图像包裹和扭曲运算符后,我们现在深入研究图像内部机制和更复杂的数学扭曲。从更深入的理解出发,我们接下来探讨更通用的图像扭曲运算符。这包括从复杂的旋转、缩放和剪切到透视或 3D 扭曲,再到向圆形弧形、相机镜头扭曲以及最终到更通用的变形扭曲进行扭曲。

通用扭曲技术

现在我们已经了解了 IM 提供的简单扭曲运算符,让我们退一步,深入研究细节,看看图像扭曲是如何实现的,以及如何改进使用它们的方式。之后,我们将继续探讨更复杂的扭曲图像方法,包括那些没有直接内置在 ImageMagick 中的方法。图像处理器扭曲图像只有几种基本方法。例如,简单扭曲 运算符是通过像素交换实现的。也就是说,单个像素甚至整个像素行和列只是被互换以进行图像的翻转滚动转置 甚至矩形旋转。不会进行颜色更改,像素数量保持不变。扭曲图像的下一种方法是通过水平或垂直移动或剪切像素列和行,例如 IM 在图像剪切 和上面的波浪扭曲 中所做的那样。剪切反过来提供了一种以任何给定角度旋转图像 的方法,这种方法应该非常快。但是,像素移动方法仅限于这些基本扭曲。例如,它无法将图像缩放为不同的尺寸。你对处理结果图像中未被原始源图像覆盖的区域几乎没有控制权。在上述函数中,IM 只是将缺失的区域设置为当前背景色。要能够以更通用的方式扭曲图像,你需要使用一种更通用的扭曲技术,称为反向像素映射。例如,这种方法被更复杂的圆形扭曲(例如内爆旋涡 图像)使用。

正向或直接像素映射

人们在尝试扭曲图像时首先想到的就是将源图像中的每个像素直接移到目标图像中的新位置。事实上,这有点像简单扭曲图像裁剪 甚至扭曲矢量图像时所发生的事情。每个像素(或坐标)只是被移动到最终图像中的新位置。不幸的是,当你尝试对除简单扭曲之外的任何操作进行此操作时,就会出现问题。例如,这里我使用一个小图像的枚举像素列表,只是更改每个像素的位置,以便将其旋转到新的位置。

  # Rotate by 17 degrees -- get the Sine and Cosine of this angle
  sin=`magick xc: -format "%[fx:sin( 17 *pi/180)]" info:`
  cos=`magick xc: -format "%[fx:cos( 17 *pi/180)]" info:`

  # For each Pixel, rotate that pixels coordinates
  magick koala.gif  txt:- |  sed '2,$ s/,/:/' |\
    gawk -F: 'NR == 1 { print }
              NR > 1 {  x = $1-32;    y = $2-32;
                        nx = int( c*x - s*y + 32 );
                        ny = int( s*x + c*y + 32 );
                        printf( "%d,%d: %s\n", nx, ny, $3 );
              }' s=$sin c=$cos - |\
      magick -background black  txt:-   koala_rotated_direct.gif
[IM Output]
扭曲是一个只有 17 度的简单旋转,但结果并不理想。首先,每个新的像素位置都是一个浮点数,但像素只能存在于整数网格中,因此上面的代码只是丢弃了结果的非整数部分。第二个问题是结果充满了没有像素着陆的孔洞。这带来了第三个问题。你可能看不到,但对于结果图像中的每个孔洞,你还会找到另一个有两个像素放置的位置。也就是说,你在同一个位置有多个像素。你应该使用哪个像素值?在上面的代码中,IM 只使用了为某个位置定义的最后一个像素。换句话说,结果图像是不完整的,其中目标图像中的每个像素并不完全位于它应该在的位置,并且可能有多个像素,也可能根本没有像素。这些都是严重的问题,在将源图像中的像素正向映射到目标图像时无法轻易解决。也就是说,这并不是说它不能起作用,许多研究论文谈论使用一种称为“散射”的技术。基本上,他们获取每个输入像素,变换其位置,然后在新的位置绘制它,并对像素颜色进行适当的扩散和混合。这种技术在处理真实世界物体的 3D 数字化时特别有用。在这里,你有一组已知的颜色表面点“云”。任何对用户可见的点都只需“散射”到屏幕上,以形成最终图像。有足够的点,图像看起来就会完整。使用交互式 3D 控件,它工作得很好,并且速度非常快。但是,散射三维点超出了 IM 处理二维光栅图像的范围。

反向像素映射

与其尝试将像素映射到最终图像中,不如将目标图像中每个像素的坐标映射到源图像中的相应位置,然后从源图像中查找该像素应该包含的颜色。这被称为反向像素映射,几乎所有图像扭曲程序都使用这种方法。由于会处理每个目标图像像素,我们可以确保目标图像中的每个像素都只获得一种颜色。因此,只要我们能确定每个目标像素的“源”位置,我们就可以使用你能想到的任何数学公式将源图像扭曲到目标图像。
[Diagram]
总之,扭曲映射(反向映射)执行以下操作。
For each pixel (I,J) in the destination or output image
   Map the I,J pixel position to a X,Y pixel position in the original image
   Look up the Color of the original image at position X,Y
       Using color interpolation, work out the appropriate color.
       Or the virtual-pixel setting, if it misses the actual source image.
   Set the destination images color for pixel I,J
请注意,我在上面使用了变量名“I,J”和“X,Y”,因为这些变量映射到你在FX DIY 运算符 中通常使用的变量名。例如,这里我模拟了之前尝试的相同 17 度旋转,但这次使用“-fx”运算符在源图像中查找最接近该位置的像素。

  # Rotate by 17 degrees -- get the Sine and Cosine of this angle
  sin=`magick xc: -format "%[fx:sin( 17 *pi/180)]" info:`
  cos=`magick xc: -format "%[fx:cos( 17 *pi/180)]" info:`
  cx=37; cy=37;   # center of rotation

  magick -size 75x75 xc:       koala.gif  \
          -virtual-pixel Black  -interpolate NearestNeighbor \
          -fx "ii = i - $cx;   jj = j - $cy;
               xx =  $cos*ii +$sin*jj + $cx;
               yy = -$sin*ii +$cos*jj + $cy;
               v.p{xx,yy}" \
          koala_rotated_fx.gif
[IM Output]
你可以在关于DIY 仿射扭曲映射 的子部分中获取有关上面 DIY 示例的更多详细信息。如你所见,我们的图像中不再有“孔洞”,因为为目标图像中的每个像素都查找了一种颜色。它仍然看起来不太好,但这是调整要放置到每个像素中的确切颜色问题。也就是说,反向像素映射不会生成孔洞或重叠像素。每个像素都有一个定义良好的颜色,从而产生完整的图像。
正向映射和反向映射之间的区别很重要,因为大多数数学变换被定义为正向映射,将单个源 (X,Y) 位置映射到目标 (I,J) 位置。事实上,“正向映射”非常适合矢量图形,以及绘制线条,因为您可以直接映射线条的端点并绘制它。对于任何线性变换(例如旋转,其中线条保持直线)来说,尤其如此。事实上,这就是所有基于矢量的语言(例如 PostScript 和 SVG)所采用的方式。但是对于一般的栅格图像,您必须使用“反向映射”来扭曲图像,这样才能确保您“填充”目标图像的所有像素。例如,如果您查看用于映射上述两种情况下坐标的数学公式,您会发现它们看起来几乎完全相同。“旋转”的反向映射是另一个“旋转”,只是方向相反。如果您仔细观察,您会发现“sin”常数与正向映射版本相反,这足以反转旋转方向。这个细节很重要且至关重要。问题在于并非所有正向映射变换都适用于反向变换。事实上,一些正向映射没有简单的直接反向映射。这不是说它不能实现,只是没有那么简单。另一方面,一些图像变换作为反向映射非常有效,但没有简单的正向映射。因此,从数学角度来看,使用反向映射方法既有优点也有缺点。
作为参考,以下是使用 通用失真,SRT 方法的更快等效方法,该方法执行与上面相同的图像旋转,并产生完全相同的结果,只是更快。同样,颜色查找仅限于使用“点”插值方法映射位置的最接近像素的颜色。这意味着图像中不会添加新颜色(除了我们“错过”源图像的情况),但您也会遇到严重的混叠效应。

  magick koala.gif  -virtual-pixel Black  -interpolate NearestNeighbor \
          -filter point    -distort SRT 17    koala_rotated_srt.gif
[IM Output]
有关失真变换的替代讨论,请参阅 Leptonica,仿射实现,特别是它对“逐点”方法的讨论。另一种方法,“顺序”方法,本质上是 IM 用于实现其 旋转剪切 失真操作员的方式。
名称有什么意义? 在我的研究过程中,我发现这种图像处理方法并没有真正明确的命名。实际的算法过程被称为“反向像素映射”,而使用数学方程被称为“几何变换”。如果失真由各种控制点的移动控制,它通常被称为“图像扭曲”或“橡胶片”。定义特定点(通常是为了在两个或多个图像之间找到等效点)的过程被称为“图像配准”。图像也可以细分为更小的更简单的单元,这些单元使用称为“网格化”(四边形)和“三角形网格化”(三角形)的技术进行单独扭曲。通过使用小的增量失真和来自两个图像的颜色混合,您可以生成动画的“图像变形”,例如您在电影和音乐视频中看到的。如果使用预先准备好的映射图像而不是动态的数学查找,则如果查找是相对位移(50% 灰色表示没有位移或查找坐标更改),则得到“绝对失真映射”。如果映射仅稍微修改颜色(阴影)而不是查找失真,则得到相关的但不同的“凹凸表面映射”。在 3D 建模和 3D 电脑游戏中,相同的技术也被用来给平面和曲面提供某种类型的彩色图案,这种方法被称为“纹理映射”。这可能涉及将图像细分为网格和网格,这些网格接近单个像素。然后,您将查看由数百万个单个点定义的对象,使用一种称为“点喷溅”的技术,尽管这通常使用正向映射失真应用。以上所有内容都密切相关,最基本的是根据将最终目标坐标映射到源图像(或对象)来查找像素的颜色。换句话说,将目标映射到源。应该使用哪个术语... 随便您选择。

像素颜色查找

上述 反向像素映射 技术仍然存在一些问题。首先,当从目标中的固定整数位置映射像素时,您最终可能会在源图像中获得非整数位置。也就是说,一个位于源图像中各个像素之间的某个位置。为了确定应该返回什么颜色,使用称为 插值 的过程来通过混合周围像素的颜色来确定该实际位置的最终颜色。 插值 设置也将处理失真图像的一部分被“拉伸”的情况,因此单个源像素会涂抹在目标图像的很大区域上。但是,简单的插值方法对相反的情况处理得并不好。这需要我们将在下面介绍的其他技术。例如,这里我们再次旋转我们的考拉,但这次使用“-interpolate Mesh”设置来混合四个附近的像素,以便从查找中产生更好、更准确的颜色。

  magick koala.gif  -virtual-pixel Black  -interpolate Mesh \
          -filter point    -distort SRT 17    koala_rotated_mesh.gif
[IM Output]
如您所见,通过简单地合并非整数查找点周围最接近的相邻颜色,您可以大大改善失真图像的外观。但是,还有一些问题需要解决... 例如,当映射位置完全“错过”源图像时该怎么办?在这种情况下,将返回什么颜色由 虚拟像素 设置确定。此设置将选择一种颜色,例如源图像的最近边缘,假装源图像在平面上无限地平铺(或镜像平铺),或使用某种特定颜色,例如“白色”、“黑色”或“透明”或用户定义的背景颜色。还有一种可能性是,对于正在映射的特定目标位置,没有数学上有效的坐标。例如,像素朝透视“平面”的“天空”看去(见 观看远处的视野),因此甚至看不到源图像所在的“平面”。在这种情况下,虚拟像素 毫无用处,因为它在 N 维空间中没有“命中”源图像平面,因此目标像素完全无效!在这种情况下,IM 使用当前的“-alpha set”设置作为像素颜色。如果它是一个“接近错过”,IM 将使用图像平面的相邻颜色对该无效颜色进行抗锯齿处理,如果它知道如何处理的话。它对透视失真进行了处理。

超级采样

插值非常适合简单的图像失真。但是,如果源图像的一部分被压缩到更小的区域,每个目标像素实际上可能需要合并源图像的更大区域。请记住,像素实际上不是点,而是代表真实图像的矩形区域。这意味着在某些情况下,我们实际上应该尝试将源图像的很大区域压缩到单个目标像素中。当这种情况发生时,简单的 像素查找 将失败,因为它只查找源图像中单个“点”的颜色(使用周围的像素邻域),并且不会合并和组合所有可能必须压缩到该单个像素中的输入图像的颜色。这样做的结果是,目标像素最终可能从源图像中获得一种基本上是随机的颜色,而不是所有相关颜色的平均值。这本身并不糟糕,但是当一个区域中的所有像素都这样做时,您会得到看起来随机的、孤立的像素、莫尔图案和混叠的“阶梯状”效果。细线也开始看起来更像点线和虚线(参见 采样操作员 的示例),或者可能完全消失。所有这些效果统称为 混叠伪像。对此的一种解决方案是对源图像进行更多颜色查找,针对目标中的每个像素,以便尝试确定目标图像中每个像素的更准确的颜色。最简单的解决方案通常被称为超级采样过采样。参见 超级采样的维基百科词条。通过从源图像中获取更多样本,覆盖映射到每个目标像素的区域,该像素的最终颜色将成为该点处失真图像的更准确的表示。您进行的颜色样本越多,最终颜色越准确,生成的图像越平滑、越逼真,尽管失真速度越慢。请记住,这种技术实际上只提高了源图像压缩超过 50% 的区域中目标图像的整体外观。在失真放大源图像或保持其比例大致相同的区域中,单个 插值查找 源图像查找通常会产生一个良好的结果,只需一次查找即可。在 图像内爆 变形示例(以及 IM 示例中的许多其他示例)中,我简要地提到了最简单的“超级采样”方法。基本上,扩大输出图像的大小(或者在本例中只是扩大输入图像的大小),然后执行失真。失真完成后,我们将其重新调整回其正常大小,这将合并所有生成的额外“样本”。例如...

  magick -size 94x94 xc:red -bordercolor white -border 3 \
          -virtual-pixel tile                -implode 4 \
          implode_tiled_box.gif
  magick -size 94x94 xc:red -bordercolor white -border 3 \
          -virtual-pixel tile  -resize 400%  -implode 4 -resize 25% \
          implode_tiled_ss.gif
[IM Output]
盒形图像的普通内爆
[IM Output]
超级采样内爆
当然,与其扩大输入图像,不如从更高质量(更大)的源图像开始,或在某个之前的处理步骤中生成一个。如果可以的话。这在旋转文本时特别有用,文本通常有非常精细的细节,需要均匀地保留,以确保最终图像具有良好的高品质外观。有关此方面的示例,请参见 宝丽来变换
从 IM v6.4.2-6 开始, 通用失真操作员 可以直接生成一个放大的输出图像,您可以将其缩小(或调整大小),以便合并和超级采样生成的像素。参见 失真缩放设置,以及下一个示例。
这只是一个超采样方法(称为“网格”方法),还有许多其他变体。最终,这些方法可能在 ImageMagick 中得到更直接的实现,但目前,简单地放大和缩放图像效果很好,不需要任何额外的编码。最后提醒一下,超采样受到用于最终图像中每个像素的样本数量的限制,因此最终调整大小中使用的缩放量也受到限制。这决定了最终“质量”失真图像。但通过使用更大的缩放因子,失真图像的生成速度当然会慢得多。但具有更高的质量,有其局限性。在极端情况下,超采样将无法处理任何涉及无穷大的图像失真(例如,内爆图像的中心)。在这种情况下,需要完全不同的技术,例如由区域重采样提供的一种技术(见下文)。总之,超采样可以改善仅具有轻微失真(例如旋转、剪切、仿射和简单透视)的图像的外观。但它对可以改善的失真类型有限。自适应超采样超采样技术可以进一步扩展。与其只使用固定数量的颜色查找来查找每个像素,不如对源图像中查找之间的距离进行检查,或者检查从低级采样返回的颜色有多接近,以查看是否应该为该特定像素进行更多采样。也就是说,超采样的量可以根据失真需求进行响应,而无需了解失真本身的任何细节。这被称为自适应超采样。这种技术在射线追踪器中非常常见,在射线追踪器中,几乎不可能确定最终图像在任何特定点的复杂程度。在这种情况下,它通常仅限于使用特定位置周围的“颜色差异”来确定何时需要更多样本。如果一个像素与它的邻居非常不同,那么在该区域使用更多样本来细化可能是某个三维物体的边缘。IM 目前不支持自适应超采样。尽管将替代采样方法添加到通用失真操作符中(见下文)是完全有可能的。这将需要对代码进行一些功能重排,因此可能不会很快添加。超采样总结超采样的难点在于确定需要多少个“点样本”,以及如何将这些样本与子像素边界排列。以及应该应用什么类型的“加权”。参见维基百科上的超采样条目

区域重采样,用于更好的失真

超采样方法最好的替代方案之一是区域重采样。与其失真更大的图像,并通过调整大小来平均结果,这只是从图像中获取和平均更多样本,我们实际上确定了源图像中应该合并多少像素(基于该点的失真“比例”)来生成每个特定的输出像素。也就是说,找出源图像中每个输出像素代表的粗略“区域”,并根据重采样滤波器合并(过滤)该区域中的所有像素。事实上,这就是 ImageMagick 调整大小操作符(实际上是一种非常具体的图像失真类型)生成如此好的结果的原因。但是对于调整大小,你只需要为整个图像计算一次每个像素需要采样的区域的比例。它需要“采样”的区域是源图像中的固定大小的矩形(窗口),这使得重采样过程变得容易,并在失真过程中提供了捷径。当对失真图像进行区域重采样时,获取样本的像素(窗口)区域不仅会改变位置,还会改变大小。因此,目标中的一个像素可能只需要合并几个源图像颜色,甚至只需要一个单一的插值颜色查找(例如,在放大时)。而目标图像中其他地方的另一个像素,可能需要对大量像素进行采样以生成正确的最终颜色。接近无穷大时,它甚至可能必须将源图像中的所有像素作为采样过程的一部分。此外,目标像素在源图像中所代表的区域可能不是简单的正方形、圆形,甚至椭圆形,而可能实际上是一个高度失真的形状,这取决于所使用的失真。计算和处理这些尴尬的形状可能非常耗时,或者几乎不可能实现。[Diagram] 使用源图像的椭圆形区域来计算每个目标像素的颜色,是一种称为椭圆加权平均 (EWA) 重采样的方法,并在 PDF 研究论文 "纹理映射和图像扭曲基础" 中概述,作者为Paul Heckbert(他也编写了“zoom”程序,几乎所有图像调整大小算法都源于此程序)。然后,这被用来定义新的通用失真操作符(见下文)。对于仿射失真透视失真,椭圆形是完美的形状。它特别适合极端的比例缩减(见下面的示例)。虽然对于其他失真并不完美,但它通常对于许多其他失真来说是合理的拟合,例如弧形和极坐标失真(但不是它们的逆),以及径向失真,例如桶形失真。但它不适合非线性失真映射,例如去极化Shepards 失真,因此它不适用于这些失真。超采样没有这种形状问题,因为每个“样本”都反向映射到目标。因此,它在这些情况下成为更好的采样方法。但正如所提到的,它可能不会对所有需要的像素进行采样,甚至可能会对太多像素进行采样。

区域采样与超级采样

以下是 IM 目前提供的所有三种采样方法,当应用于极端的无限平铺透视图像时。有关此失真的详细信息,请参见下面的查看远处地平线

  # input image:  special checkerboard with a gold outline.
  magick -size 90x90 pattern:checkerboard -normalize -fill none \
          -stroke gold -strokewidth 3 -draw 'rectangle 0,0 89,89' \
          -fill red        -draw 'color 20,20 floodfill' \
          -fill lime       -draw 'color 40,70 floodfill' \
          -fill dodgerblue -draw 'color 70,40 floodfill' \
          checks.png

  # Using Interpolated Lookup
  magick checks.png -filter point \
          -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_tile_point.png

  # Using Grid Super Sampling
  magick checks.png  -filter point  -set option:distort:scale 10 \
          -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          -scale 10%    horizon_tile_super.png

  # Using Area Resampling (default)
  magick checks.png       -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_tile.png
[IM Output]
检查图像
==> [IM Output]
插值
查找
[IM Output]
超级采样
x10
[IM Output]
椭圆加权区域
(EWA) 重采样
所有图像的失真完全相同,只是使用了不同的“重采样”技术。上面的最后一张图像使用了通用失真操作符的默认 EWA 设置,正如你所看到的,它生成了非常高质量的结果。但是它花了 4.6 秒来生成这张图像,考虑到所涉及的极端情况,这并不算太糟糕(有点慢)。第一张图像通过使用“-filter point”设置关闭了默认的 EWA 重采样。这迫使它对每个像素使用直接插值查找。因此,与之相比,这张图像生成得非常快(0.51 秒),但正如你所看到的,它产生了糟糕的结果,因为“缩小”(下采样)随着“距离”的增加而增加。中间的图像与第一张图像相同,但失真输出图像被放大了 10 倍,然后被缩放到与其他图像匹配(网格重采样)。也就是说,为每个目标像素查找并平均了 100 多个像素,从而对结果进行超采样。它生成速度很快(1.2 秒),虽然它总体上提高了图像的质量,但这种提高是有限的。上面示例中使用的 ×10 非常重,远远超过了大多数超采样使用中更典型的 3 或 4 倍缩放。结果之间最大的区别在于超采样只是对整个图像的质量进行了一般性的改善。随着失真的加剧,它开始失效。结果是在中景中出现了高度可见的重采样伪影,更具体地说是在地平线之前出现了一系列严重的莫尔纹效果。莫尔纹效果是由当每个像素的 10 个样本几乎与图像的棋盘格图案相匹配时产生的,从而产生失真的颜色效果。另一方面,区域重采样更关注靠近地平线的问题像素(它几乎把所有时间都花在了这里),而不是前景像素,它实际上确实优于超采样。基本上,上面是一个非常极端的失真,EWA 查找所花费的时间与其相称。更常见的是,它会生成比单个插值查找更好的结果,因为它有效地查看了所有相关的像素,同时不会在不需要的区域使用太多样本,就像超采样那样。总之...使用简单的椭圆形(EWA 重采样)或矩形(调整大小)进行“区域重采样”确实会产生良好的结果,因为所有参与缩放、仿射或透视失真的源像素将被合并以生成单个目标像素的最终颜色。在非常非线性失真情况下,例如在去极化失真中,或在不确定的失真情况下,例如在Shepards 失真中,甚至在射线追踪中,找到重采样所有所需源像素的正确“区域”变得非常困难,超采样是改善结果的最佳方法。但是对于直线平铺、放大和未缩放旋转,非常快速的单个“点”插值查找可能就足够了,甚至可以推荐它以确保完美的无操作(无变化)失真(见下文)。但请记住,所有重采样技术只是确定每个单独像素颜色的方法。它实际上并不是图像失真方式的一部分,除了与目标和源(或反过来,如果可能的话)之间位置的映射有关。

通用失真操作符

通过生成这些示例,在IM 论坛中随之而来的讨论,以及用户对更轻松、更快地进行透视和其他失真的多次请求,在 IM v6.3.5-1 中添加了一个新操作符,使我们能够更轻松地添加许多不同类型的图像失真。这个通用失真操作符被称为“-distort”,你可以使用“-list Distort”查看你的 IM 版本中有哪些失真方法可用。

  magick -list distort
-distort”操作符采用两个参数,一个是如上所示的失真方法,另一个是包含逗号或空格分隔的浮点数列表的字符串参数,用于控制特定的失真方法。

  magick ... -distort  {method}  "{list_of_floating_point_values}" ...
但是,给定的浮点数数量高度依赖于所使用的失真方法,它们的含义也不仅取决于所选择的方法,还可能取决于特定方法所需的控制点或属性的准确数量。对于“缩放-旋转-平移”(或简称为“SRT”)失真来说,情况尤其如此,它实际上将三个独立的“仿射”失真组合成一个失真。许多失真方法都采用一个控制点列表(以图像坐标表示),通常这些控制点被给出为成对的坐标,这些坐标控制失真将如何修改图像。这些成对的坐标在后面的使用控制点的失真中更详细地描述。

失真选项、控件和设置

最佳拟合 +Distort 标志

默认情况下,"-distort" 通常会将源图像扭曲成与原始图像大小相同的图像。但也有一些例外,例如“Arc”扭曲(一种极坐标映射变体),其中输入源图像的大小在图像的扭曲形式中并没有太大意义(有关详细信息,请参见下面的Arc Distortion)。运算符的另一种形式“+distort”(添加至 IM v6.3.5-7),将尝试调整扭曲图像的大小,使其包含整个输入图像(如果可能),类似于较旧的旋转和剪切运算符。但是,这种特殊的运算模式会更进一步,还会设置结果图像的虚拟画布偏移(页面)。这样,您可以稍后使用适当的Alpha 混合将此图像合并到另一个图像上,根据您的控制点处于正确的位置(请参见使用仿射分层的三维立方体作为基本示例)。此外(取决于扭曲方法),“+distort” 将尝试考虑源图像中可能存在的任何现有虚拟画布偏移,并将其用作扭曲过程的一部分。请参阅有关各个扭曲方法的说明。因此,您可能需要明智地使用“+repage” 属性设置运算符来清除或调整该偏移,使用“最佳拟合” “+distort” 形式的通用扭曲运算符之前。如果不需要虚拟画布和偏移,您可能还需要之后使用它。另请参阅移除画布/页面几何。正常的“-distort” 将忽略源图像中存在的任何现有偏移(就扭曲本身而言),但会将该偏移不变地复制到扭曲图像中。总结一下……使用“-distort” 使结果映射到大小相同的图像中。使用“+distort” 尝试自动设置输出图像大小,但也要使用和生成虚拟画布偏移(页面属性)。另请参阅扭曲视窗(如下),如果您想覆盖此通用视窗选择,并精确控制要在结果中看到扭曲图像的大小和哪个部分。
注意……“+distort” 生成的最佳拟合视窗比用户通常预期的要大 2 个像素。原因是这些像素包含来自区域重采样滤波器的半透明像素,而这些像素对于正确地“边缘连接”和扭曲图像的叠加至关重要。从技术上讲,添加的像素数量应取决于重采样滤波器支持的输出缩放比例。也就是说,像素区域由于重采样滤波器而可能“扩散”多少。但是,由于每个像素的缩放比例都是可变的,因此计算绝对正确的附加像素数量是一件非常棘手的事情,而且通常不值得费力。因此,添加的 2 个像素是一个“模糊值”,因为扭曲很少会放大图像,这会导致像素“扩散”更多。此外,由于大多数标准重采样滤波器的支持度为 2 个单位,因此添加 2 个像素是一个合理的做法。同样,由于此添加是“固定的”,因此它允许用户选择简单地裁剪图像大小(以各种方式),如果这是他们的愿望。当对图像进行放大时,2 像素的“模糊值”变得明显太小。但这些扭曲相当罕见,用户可以定义自己的视窗(见下文),如果这是一个问题的话。扭曲图像在虚拟画布上的虚拟偏移将进行调整以考虑这 2 个额外的像素,因此扭曲图像对于叠加来说是正确的,但对于简单的合成来说则不正确。但请注意,虽然裁剪修剪 将保留分层图像的位置,但修剪切除 将相对于此偏移移动分层图像。

扭曲像素颜色确定

如上面反向像素映射中所述,结果图像中的每个点首先通过根据所选扭曲方法,将该像素在目标图像中的位置映射到源图像中等效的(反向扭曲)位置来确定。但是,像素的最终颜色并不那么容易确定,因为它受许多因素的影响。

虚拟像素和平铺

扭曲映射点可能不会击中实际的源图像,而是击中其旁边某个地方,甚至远离实际图像。解决此问题的方法是假装源图像被一个“无限”或“虚拟”表面包围,该表面由当前的“-virtual-pixel” 设置定义。有关此设置效果的详细信息和示例,请参见虚拟像素示例。这对于生成扭曲的甚至未扭曲的源图像平铺模式非常有用。虚拟像素部分本身(未扭曲)以及仿射平铺查看远处地平线中展示了此类技术的示例。

无效扭曲像素

有时,目标像素的扭曲甚至不会“击中”虚拟平铺图像!这通常发生在您使用某种类型的三维空间扭曲方法扭曲图像时,并且像素“向量”甚至没有击中图像所在的源平面。基本上,扭曲的结果在数学上变得“未定义”。在这种情况下,颜色将由“-mattecolor” 设置确定。例如,当您在透视扭曲中看到“天空”时(例如,参见查看远处地平线),确定源图像位置的数学变得“未定义”(实际上是定义的,但从用户的正面观看角度来说是无效的)。因此,将输出“-mattecolor” 作为“天空”。实际上,透视扭曲算法还设法为靠近地平线的像素包含一些“反锯齿”信息,尽管对于这种情况来说这并不常见。

EWA 重采样和滤镜

一旦您知道目标像素“击中”源图像的位置,您就需要使用源图像中“击中”点附近的像素来确定目标像素的颜色。通常,扭曲运算符 将使用区域重采样 方法 EWA(椭圆加权平均值)来平均源图像的更大区域,以计算出此像素的正确颜色。您可以通过 EWA 重采样使用“-filter” 设置更改滤波器。请参见重采样滤波器,以及更具体的圆柱形滤波器以了解更多详细信息。最初,圆柱形高斯 滤波器用于 EWA 重采样,因为这是 EWA 重采样原始研究论文中定义的内容。但这往往会产生非常模糊的结果,尽管它也不会产生混叠效果。这曾经是默认滤波器,以及一个在该版本之前导致极度模糊的实现错误(现在已修复)。从 IM v6.6.5-0 开始,在与劳伦森大学数学教授尼古拉斯·罗比杜克斯进行大量讨论后,图像扭曲的默认滤波器被替换为“Robidoux” 滤波器,这是一种非常“类米切尔”的立方滤波器,专门为 EWA 重采样而设计。请参见圆柱形滤波器,了解有关此滤波器和其他圆柱形滤波器的信息。但请注意,任何底层的窗口化 sinc 滤波器函数都被更圆的窗口化 jinc 滤波器函数替换。因此,选择“Lanczos”滤波器将返回“jinc 窗口化 jinc”滤波器,而不是“sinc 窗口化 sinc”滤波器。有关更多详细信息,请参见窗口化 jinc 圆柱形滤波器。另外,“Sinc” 函数实际上不可用作圆柱形函数,因为该函数与网格上的径向距离交互,导致滤波器权重形成趋于自我抵消(权重和为零)的形式,只要使用偶数个“叶”。反过来,这会导致它尝试在使用像素级棋盘格“散列”模式时生成接近无限的颜色。基本上,EWA 使用重采样滤波器,就像调整大小运算符 一样,因此您也可以使用特殊的专家滤波器选项 修改滤波器。例如,可以通过滤波器模糊设置 控制“高斯” 和类高斯滤波器的模糊度。类似地,您可以使用叶瓣支持设置 来控制窗口化 jinc 滤波器的大小和强度,例如“Lanczos” 滤波器。
有许多极端扭曲方法,它们会自动关闭 EWA 重采样,并且仅使用更直接的插值查找

例如,去极化 扭曲会在圆弧形状中生成重采样区域,这些区域不太适合“椭圆形”(EWA)重采样。其他扭曲(例如谢泼德)使“缩放因子”的计算变得极其困难,尽管扭曲运算符的未来改进可能会使其成为可能)。

对于这些扭曲方法,建议使用超采样 技术来防止在结果中生成严重的混叠伪影,这些伪影出现在图像压缩(降采样)区域。

重采样失败

在某些特殊情况下,EWA 重采样椭圆可能会无法实际“击中”任何真实像素以创建加权平均值。基本上,椭圆太小或太薄,以至于完全落在图像中每个像素之间。如果没有像素颜色,它就无法为该点的输出图像生成颜色。这是一种极端情况,通常无法实现,除非您正在使用专家滤波器设置。但在不太可能的情况下,没有像素被击中,或者滤波器权重加起来为零,重采样将失败。在这种情况下,IM 将退回到使用简单的直接插值查找,就像您关闭 EWA 滤波(见下文)时获得的一样。如果您想检查是否发生了这种情况,您可以使用特殊的背景插值,并使用不寻常的背景颜色(如“红色”),以便突出显示任何此类重采样失败。例如,这里我故意将盒式滤波器的支持设置得太小,从而使重采样椭圆变得太小。我还大大放大了图像,这样您就可以看到哪些部分“击中了像素”,哪些部分没有。

  magick \( xc:red   xc:white xc:black +append \) \
          \( xc:blue  xc:lime  xc:white +append \) \
          \( xc:black xc:red   xc:blue  +append \) -append \
          -filter Box -define filter:support=0.4 \
          +distort SRT 30,0  bad_box_distort.png
[IM Output]
在高度放大的图像中,重采样圆圈要么只击中一个像素(产生一个由纯色组成的混叠圆圈),要么无法匹配任何像素,因为圆形采样区域完全落在像素之间,因此滤波器将回退到一个插值的彩色渐变(默认情况下为双线性插值),以获得至少一些合理有效的颜色以用于生成的图像。下面是同一个例子,但是用特殊的(通常无用)背景插值(只返回背景色,它被设置为“灰色”)来替换插值方法。

  magick \( xc:red   xc:white xc:black +append \) \
          \( xc:blue  xc:lime  xc:white +append \) \
          \( xc:black xc:red   xc:blue  +append \) -append \
          -filter Box -define filter:support=0.4 \
          -interpolate background -background Gray \
          +distort SRT 30,0   bad_box_distort_gray.png
[IM Output]
为了实现完全覆盖(因此它始终能找到至少一个像素),圆柱形重采样滤波器需要一个至少约为 0.707(sqrt(2)/2)(框滤波器的默认值)的“支持”。所有滤波器通常都比这个最小支持尺寸大得多。有关此内容的示例,请参阅有关圆柱形滤波器的部分。
角落的小彩色点是由虚拟像素的重采样优化引起的(在采样纯色 VP 区域时中止昂贵的 EWA 重采样)。它们将随着“-virtual-pixel”设置的不同选择而消失或改变。

通常这不是问题,而且只在这里看到,因为扭曲使用了一个“最佳拟合视区”,它略大于原始图像,因此在边缘周围包含了一些额外的像素,在本例中,它会对虚拟像素进行采样。

插值或直接颜色查找

您可以使用“-filter point”来关闭滤波,从而关闭 EWA 重采样。执行此操作时,ImageMagick 将切换颜色查找以使用快速且简单的像素插值。也就是说,它将使用对源图像的“单点”参考来查找颜色,而无需任何“重采样区域”。生成的像素的颜色将使用仅基于点最近邻居的插值颜色。
插值通常会导致严重的混叠效应
当图像发生任何形式的缩小或降采样时。
但它对于包含最小失真(如旋转、平铺或图像放大(放大或上采样))的图像非常有效。可以将超采样技术与插值结合使用,以提高强压缩、缩小或降采样区域的结果。请参阅去极化-极化循环问题(一种不能使用 EWA 重采样的扭曲)以了解使用超采样来解决插值混叠的示例。

详细的扭曲摘要

通过在运行“-distort”之前设置“-verbose”(使用“+verbose”再次关闭),扭曲将在标准错误通道中输出有关算法和内部系数的信息,这些算法和内部系数是在扭曲给定图像时计算出来的,并且根据指定的方式使用。您可以使用此信息来查看和了解扭曲是如何工作的以及是如何应用的。它也是我们用来找出哪里出错的调试工具,以及作为新扭曲的实施过程的一部分。

  magick koala.gif -verbose -distort SRT 0 +verbose  koala_noop.gif
[IM Output] ==>
[IM Text]
==> [IM Output]
注意:生成的图像几乎与输入图像相同,但并不完全相同(请参阅下一节中的“无操作扭曲”)。
详细的输出描述了给定扭曲的两种替代扭曲技术。一种是“仿射投影”扭曲,而另一种显示了一个DIY FX 运算符替代方法,详细说明了它如何将输出图像(i,j)中的给定像素映射到输入图像(xx,yy)中的插值查找,以变换图像。它执行重采样椭圆计算,该计算使用复杂的数学(特征值)来计算出,它只计算源图像中未缩放的插值查找点,从中可以确定(i,j)像素的颜色。两者都提供了有关扭曲过程的信息,可以用来提取用于同一类型的其他扭曲的额外信息。有关使用此信息的更复杂示例,请参阅下面的透视内部双线性内部。有关使用 FX 命令进行图像扭曲的示例,请参阅FX 图像调整大小。上面额外的“0.5”加法和减法是将魔法的“像素坐标”转换为“图像坐标”所必需的,并且对于正确处理图像扭曲的数学运算也是必需的。请参阅下面的图像与像素坐标

无操作扭曲

上面的例子展示了执行无操作扭曲的结果。也就是说,将图像通过扭曲(用于某些次要效果)运行,但没有实际的扭曲(只是像素的 1 对 1 映射)。EWA 重采样滤波器不会完全复制原始图像的相同颜色,但会使用其邻居将各个像素稍微模糊。这是由于使用了二维滤波器,虽然颜色模糊很小,但永远无法消除。因此,为了进行真正的“无操作”,我们还必须关闭 EWA 滤波,并使用插值或直接颜色查找(请参阅上面)。

  magick koala.gif -filter point -distort SRT 0  koala_noop_perfect.gif
[IM Output]
几乎所有插值设置通常会在精确引用时提取源像素的精确副本。但是,作为预防措施,您还可以指定最近邻插值,以提高速度并确保仅返回精确的颜色匹配,而不管扭曲可能产生的任何浮点误差。

  magick koala.gif   -filter point  -interpolate nearest \
          -distort SRT 0  koala_noop_perfect_2.gif
[IM Output]
这似乎适得其反,但这是一种非常有用的方法,可以放大图像区域或平铺图像(使用虚拟像素方法),而无需实际调整原始图像数据的大小。请参阅使用扭曲通过虚拟像素进行平铺以了解此内容的示例。也就是说,使用扭曲运算符进行其次要效果,例如多图像虚拟像素平铺、图像大小放大或裁剪、添加边框,甚至平移(通过整数甚至亚像素量)。所有这些都不需要实际“扭曲”图像,而只是以某种“编程”方式对其进行“修改”。

视区,扭曲查看的位置

如上所述,使用“-distort”或“+distort”会改变“目标图像”的最终大小和位置,使其成为:与源图像相同(忽略任何虚拟画布设置),或者,是扭曲源图像的最佳拟合计算(如果可能)。这两件事基本上定义了目标图像所看到的最终“扭曲空间”的哪一部分。另一种思考方式是,目标图像是一个“窗口”,它可以查看最终的扭曲图像,或者,是一个通往扭曲空间的“视区”。“distort:viewport”设置会覆盖这两个默认设置,并允许您直接指定要查看的扭曲空间的哪一部分...
-define distort:viewport=WxH+X+Y
-set option:distort:viewport WxH+X+Y
这些是在 IM v6.3.6-1 中添加的。它不会放大或缩放扭曲的图像,只会指定扭曲图像空间中正在查看的位置和区域(视区)。这可以用来创建特定大小的目标图像,或者将视图移动到扭曲图像空间中的特定区域。它与使用无限大小(由虚拟像素定义)扭曲图像的“视区裁剪”非常相似。例如,这里我们将输出裁剪到仅考拉头部(使用无操作扭曲)。换句话说,只是对原始未扭曲图像的直接“视区裁剪”。

  magick koala.gif  -define distort:viewport=44x44+15+0 \
          -filter point -distort SRT 0  +repage koala_viewport.gif
[IM Output]
在这里,我们扩展了视图,以查看围绕扭曲图像的额外空间,并展示了虚拟像素设置对围绕原始源图像的无限空间的影响。

  magick koala.gif  -define distort:viewport=125x125-25-25 \
          -filter point -distort SRT 0  +repage koala_viewport_2.gif
[IM Output]
在本例中,它更像是使用扩展运算符来放大图像。但是,它不会简单地用背景色填充,而是用虚拟像素设置填充添加的区域。在本例中,使用默认的“边缘”虚拟像素设置,这会导致从原始图像边缘的像素复制的水平和垂直像素线。
您可能希望为虚拟像素设置做出更好的选择。例如,使用“背景”设置将使此无操作扭曲几乎完全像扩展运算符一样工作。对于此图像,“白色”虚拟像素设置可能是一个更好的选择。

  magick koala.gif  -define distort:viewport=125x125-25-25 \
          -virtual-pixel White -distort SRT 0  +repage koala_viewport_3.gif
[IM Output]
前面的示例中的最终“+repage”是用来去除“-distort”在使用视区设置时将在其位置留下的视区的虚拟画布偏移量。在本例中,只是不需要这些信息。在其他情况下,例如,当分层扭曲图像时,您会希望得到这些偏移信息。
视区选项在使用“平铺”甚至“镜像”虚拟像素设置时特别有用,它允许您生成任何大小和不同样式的平铺图像。您甚至可以使用扭曲来扭曲这些平铺图像,例如,在下面的仿射平铺中举例说明。

  magick koala.gif  -define distort:viewport=125x125-25-25 \
          -virtual-pixel Mirror -distort SRT 0  +repage koala_viewport_4.gif
[IM Output]

居中方形裁剪

如果您使用“-set”选项来设置生成的图像的“视区”,您可以在分配的值中包含百分比转义。更具体地说,您可以包含FX 百分比转义,它可以进行数学计算。这意味着“视区”可以被计算出来,同时利用当前内存中图像大小的属性来指定生成图像的最终大小。这意味着什么?嗯,这意味着“视区”可以用来生成特殊类型的裁剪,通常需要对图像进行一次或多次预读(或使用更高级的 API 编程接口)以及外部计算才能实现。例如,您可以裁剪出图像的“中心正方形”,而无需事先知道原始图像的大小或方向。这很复杂,因此我将视区表达式放在变量中,以便于阅读、编码和调试,尽管它实际上只是一个常量(固定)表达式。

  size='%[fx: w>h ? h : w ]'
  offset_x='%[fx: w>h ? (w-h)/2 : 0 ]'
  offset_y='%[fx: w>h ? 0 : (h-w)/2 ]'
  viewport="${size}x${size}+${offset_x}+${offset_y}"

  magick worldmap_sm.jpg  -set option:distort:viewport "$viewport" \
          -filter point -distort SRT 0  +repage   viewport_square.gif
[IM Output] ==> [IM Output]
生成的图像是从任何输入源图像中提取的最大中心正方形,无论该图像的大小如何。扭曲本身实际上不会扭曲图像,只会复制视区覆盖的区域。请注意,所有四个数字都需要计算才能产生“中心正方形裁剪”,因为所有值都取决于图像的方向。因此,每个表达式都使用“w>h ? ... : ...”形式的“图像方向”测试,因此最终的值取决于图像的方向。
这是一种使用“min()”和“max()”函数而不是图像方向测试的替代形式。

  magick worldmap_sm.jpg  -set option:distort:viewport \
    "%[fx:min(w,h)]x%[fx:min(w,h)]+%[fx:max((w-h)/2,0)]+%[fx:max((h-w)/2,0)]" \
    -filter point -distort SRT 0  +repage  viewport_square_2.gif
[IM Output]
感谢Fred Weinhaus 的 Tidbits 页面。在缩略图、正方形填充和裁剪中展示了使用多种图像处理技术来完成同一件事的方法。

纵横比裁剪

此技术可以扩展,以便您可以将图像居中裁剪以适合给定的纵横比。另请参阅论坛讨论裁剪到纵横比

其他视区示例

另请参阅下面的旋转图像的方法,以了解其他使用视区来控制扭曲空间中哪一部分在结果中可见的示例。

输出缩放和超级采样

-define distort:scale=N
-set option:distort:scale N
在 IM v6.4.2-6 中添加,作为通用输出图像缩放因子。这会将输出图像放大给定的倍数,因此“-distort”将需要生成 N2 个更多扭曲的查找“样本”。该数字通常是整数,但可以是浮点放大倍数。请注意,许多扭曲也允许您“缩放”结果扭曲图像的大小,但结果图像大小不受该缩放的影响(除非使用“最佳拟合”的“+distort”)。但是,此“缩放”设置不会更改结果图像的内容,只会放大或缩小结果输出图像。例如,可以将其与适当的“视口”一起使用,以生成可以轻松“-resize”到特定大小的图像,允许您生成对扭曲图像的受控“缩放”,而不会损失质量。
例如,我们在考拉的头上“放大”。

  magick koala.gif -set option:distort:scale 2.5 \
          -set option:distort:viewport 44x44+15+0 \
          -distort SRT 0  +repage koala_zoom.gif
[IM Output]
请注意,虽然请求视口为 44x44 像素,但实际输出图像已缩放至 110x110 像素。更常见的是,它用作对扭曲操作进行“超级采样”的简单方法(参见上文)。为此,使用整数“超级采样”缩放因子,并在扭曲图像后将其缩放到原始大小,以将额外的样本合并在一起,并产生更高质量的结果。

  magick koala.gif -filter point -set option:distort:scale 10 \
          -distort SRT 0  -scale 10%   koala_super.gif
[IM Output]
同样,在使用“超级采样”来提高图像质量时,不需要“区域重采样”(它只会减慢速度),因此通常通过使用“-filter point”选项(参见上一节)将其关闭。

扭曲方法简介

缩放-旋转-平移 (SRT) 扭曲

最简单的扭曲之一,但也可能是用途最广泛的之一,是“SRT”或“缩放-旋转-平移”扭曲。(SRT 只是一个快速简写)您已经在上面的示例中看到了这种扭曲的“无操作”示例,其中图像在没有任何实际扭曲的情况下被处理,但它仍然会经过滤波,这会导致一些非常轻微的模糊。以下是上述“无操作”扭曲结果的重复...

  magick koala.gif    -distort SRT 0    koala_noop.gif
[IM Output] ==> [IM Output]
请注意,由于使用 区域重采样,图像会略微模糊。但是,IM 重采样过滤器经过专门设计,可以最大程度地减少无操作扭曲的这种模糊,并且在正常使用时是必需的。

如果要出于特殊目的获得完美的“无操作”扭曲,则关闭 EWA 重采样。也就是说,在扭曲运算符之前,对上述运算符指定“无操作”过滤器“-filter Point”。

SRT”扭曲实际上是单一扭曲方法中的三个独立扭曲,这就是它被称为“缩放-旋转-平移”扭曲的原因。除了角度旋转之外,所有参数都是可选的,这使得参数高度可变,具体取决于您给出的逗号或空格分隔参数的数量,最多 7 个浮点数。
-distort SRT " 
                  Angle 
 "   -> 居中旋转
        Scale     Angle 
  -> 居中缩放和旋转
X,Y               Angle 
  -> 围绕给定坐标旋转
X,Y     Scale     Angle 
  -> 围绕坐标缩放和旋转
X,Y ScaleX,ScaleY Angle 
  -> 同上
X,Y     Scale     Angle  NewX,NewY
  -> 缩放、旋转和平移坐标
X,Y ScaleX,ScaleY Angle  NewX,NewY
  -> 同上
这是将图像进行选择,并可选地使用控制点。如果没有给出控制点,则使用输入源图像的中心。围绕该点,扭曲将按顺序进行... 缩放图像,旋转它,然后平移或将选定的控制点移动到新位置。因此得名。上面显示的参数顺序反映了实际应用于图像的操作顺序。X,Y 将转换的“中心”平移到原点,ScaleX,ScaleY 缩放图像,Angle 旋转图像,然后NewX,NewY 将“中心”平移到这些坐标。也就是说,运算符实际上表示 4 个内部扭曲运算,所有这些运算都作为单个扭曲同时应用。虽然对我们人类来说,只涉及 3 个不同的扭曲。因此,让我们以使用“考拉”图像为例... 一个参数只是围绕图像中心进行简单的旋转,基本上产生了与旧的 旋转运算符 相似的结果,但没有图像大小增加。

  magick koala.gif  -background skyblue  -virtual-pixel background \
          -distort ScaleRotateTranslate -110 koala_srt_rotate.png
[IM Output]
请注意,默认情况下,输入图像的大小也用于输出图像,因此旋转后的图像可能会被剪切。无论图像具有奇数还是偶数像素,它也都是完美的居中。使用“+distort”的“加号”形式,以及对结果虚拟画布偏移量的清理,我们可以生成与普通 旋转运算符 非常类似的结果。

  magick koala.gif  -background skyblue  -virtual-pixel background \
          +distort ScaleRotateTranslate -110 +repage koala_srt_rotate2.png
[IM Output]
从 IM 6.7.3-4 开始,旋转运算符 现在使用扭曲 SRT 扭曲。在此之前,它使用 剪切操作,这不会产生良好的结果。
让我们也将其缩小 30%,但使用透明背景。

  magick koala.gif  -alpha set -virtual-pixel transparent \
          +distort ScaleRotateTranslate '.7,-110' +repage koala_srt_scale.png
[IM Output]
下一组参数将指定图像旋转和缩放的“中心”。此点称为图像中的“控制点”或“句柄”,它是一个用于控制扭曲的位置。由于我们对这种扭曲使用了一个特定点,因此让我们不使用“最佳拟合”模式,以避免“虚拟偏移量”的复杂性。例如,让我们围绕考拉的“鼻子”旋转和缩放考拉,它位于源图像中的 28,24。趁我们现在在做,让我们扭曲 X 和 Y 缩放的不同。

  magick koala.gif  -background skyblue -virtual-pixel background \
          -distort ScaleRotateTranslate '28,24  .4,.8  -110' \
          koala_srt_center.png
[IM Output]
作为最后一个示例,让我们也将其“鼻子”移动到图像的底部附近,并将背景设置为匹配的白色背景。

  magick koala.gif  -virtual-pixel white \
          -distort ScaleRotateTranslate '28,24  .4,.8  -110  37.5,60' \
          koala_srt_trans.png
[IM Output]
请注意,最终位置也是浮点数。实际上,所有参数都可以是浮点数,扭曲将按预期进行。请记住,每个操作(缩放、旋转和平移)都是按顺序执行的。如您所见,这种扭曲非常通用,虽然您可以将其视为使用三种不同的方法按顺序扭曲图像,但实际上它是在同时应用所有三种扭曲以产生所示结果。这使得它比执行多个单独的运算符更快,并且通常会产生更好的最终结果。以上还演示了使用不同的 虚拟像素 设置来定义用于引用实际源图像之外区域的颜色。要查看 插值 对旋转的影响,请参见 旋转线和边缘的插值。这种扭曲专门设计用于接受图像并基于该对象的移动和旋转生成动画。例如,这里我创建了一艘风格化的宇宙飞船,然后以非常粗略的方式对其进行动画处理。这艘飞船位于其底部的 20,75(用于初始的“蹲下”缩放),而移动和旋转的正常“句柄”是飞船的中心,它位于原始图像中的 20,60。这些点表示控制点,通过这些控制点,可以简单地对对象进行动画处理。

  magick -size 80x80 xc:skyblue -fill yellow -stroke black \
          -draw 'path "M 15,75 20,45 25,75 Z  M 10,55 30,55" ' \
          spaceship.gif
  magick spaceship.gif \
          \( -clone 0  -distort SRT '20,75  1.0,0.6  0' \) \
          \( -clone 0  -distort SRT '20,60     1     0  20,49' \) \
          \( -clone 0  -distort SRT '20,60    0.9   20  27,35' \) \
          \( -clone 0  -distort SRT '20,60    0.8   45  40,23' \) \
          \( -clone 0  -distort SRT '20,60    0.5   70  55,15' \) \
          \( -clone 0  -distort SRT '20,60    0.3   75  72,11' \) \
          \( -clone 0  -distort SRT '20,60    0.1   80  100,8' \) \
          -set delay 50  -loop 0  spaceship_launch.gif
[IM Output] ==> [IM Output]
当然,这是一个关于如何使用“SRT”扭曲来为静态图像设置动画的非常粗略的示例,但您应该明白这个意思。您可以添加更多帧,以及一些火焰和烟雾来进一步改进它(欢迎投稿,最佳结果将与您的姓名一起添加在这里)。

旋转图像的方法

图像可以以多种方式旋转。但仅仅是简单的旋转可能不是您想要的。在不改变大小的情况下旋转图像...

  magick rose: -virtual-pixel black -distort SRT '20'  rotate_normal.png
[IM Output]
或者旋转以避免剪切旋转图像的任何部分...

  magick rose: -virtual-pixel black +distort SRT '20'  rotate_noclip.png
[IM Output]
但是,通常情况下,您不想看到围绕实际图像的“黑色”虚拟像素(或任何其他非图像颜色)。一个解决方案是裁剪图像(使用扭曲视口设置)到相同纵横比的最大矩形,以便它只包含旋转产生的真实图像像素。但是,计算此矩形相当棘手,并且在ImageMagick 论坛 上进行了大量讨论,使用了在数学帮助论坛 上找到的一些公式。在这里,我们旋转并在尽可能接近原始纵横比的情况下进行内部裁剪。

  angle=20
  ratio=`magick rose: -format \
     "%[fx:aa=$angle*pi/180; min(w,h)/(w*abs(sin(aa))+h*abs(cos(aa)))]" \
     info:`
  crop="%[fx:floor(w*$ratio)]x%[fx:floor(h*$ratio)]"
  crop="$crop+%[fx:ceil((w-w*$ratio)/2)]+%[fx:ceil((h-h*$ratio)/2)]"
  magick rose: -set option:distort:viewport "$crop" \
          +distort SRT $angle +repage   rotate_internal.png
[IM Output]
这看起来很复杂,但这是因为它实际上必须计算 4 个不同的值来定义 视口设置,即宽度、高度和原始图像中的偏移量。另一种选择是不仅旋转,还要稍微放大图像以“填充”原始图像的边界。

  angle=20
  magick rose: -distort SRT \
     "%[fx:aa=$angle*pi/180;(w*abs(sin(aa))+h*abs(cos(aa)))/min(w,h)], $angle" \
     rotate_correction.png
[IM Output]
最后一种方法非常适合对照片进行微小的旋转校正,以便保留图像的原始大小。这种方法之所以更简单,只是因为只需要计算一个“缩放”值,因此可以在“内联”中完成。

使用控制点进行扭曲

虽然“SRT”扭曲方法是通过指定旋转角度和缩放因子来定义的,但大多数扭曲都是通过移动源图像上的“点”并将它们移动到结果图像中的新位置来定义的。这有点像在定义“SRT”平移时移动“中心”点。这些点称为控制点,通常通过为每个控制点给出 4 个浮点数(2 对坐标)来定义。因此,扭曲通常是用多个 4 值集来定义的。例如...
X1,Y1 I1,J1     X2,Y2 I2,J2     X3,Y3 I3,J3     X4,Y4 I4,J4 . . . .
其中,源图像中的控制点 Xi,Xi(相对于其虚拟画布)映射到扭曲的目标图像上的 Ii,Ji
但是,由于扭曲运算符 实际上是将目标坐标映射到源坐标(参见反向像素映射),因此内部使用上述方法是将 I,J 坐标映射到 X,Y 坐标。然而,结果应该是一样的,只是思考方式不同。
在 IM 6.3.6-0 版本之前,扭曲运算符 运算符首次引入时,控制点的坐标排序定义为所有源坐标,然后是所有目标坐标。但是,这使得很难确定哪些源坐标和目标坐标彼此对应,并且不允许简单地附加更多控制点以进一步细化扭曲。
这样定义是为了将每个控制点的移动都保存在用逗号(或空格)分隔的浮点数列表中。它还允许将来使用外部“控制点文件”。使用控制点最简单的变形是“仿射”变形,虽然你将在后面看到,它通常用三个点定义,但你也可以只使用一个或两个控制点移动。实际上,'SRT' 仅仅是 '仿射' 变形的一个两个或一个点的子集。例如,这里我们将考拉图像的“鼻子”从 '28,24' 移动到新的位置 '45,40'(如红箭头所示),这导致了图像位置的简单“平移”。

  magick koala.gif  -virtual-pixel white \
          -distort Affine '28,24 45,40'   koala_one_point.png
[IM Output] ==> [IM Output]
使用两个点,“仿射”变形不仅可以平移图像,还可以缩放和旋转它('SRT' 变形的全部范围)。例如,这里我将考拉的“耳朵”(从 '30,11' 到 '48,29' 的红线)映射到一个更大的水平位置(从 '15,15' 到 '60,15' 的蓝线),需要缩放、旋转和平移图像,以便控制点移动到这个新位置。

  magick koala.gif  -virtual-pixel white \
          -distort Affine '30,11 15,15  48,29 60,15'   koala_two_point.png
当然,“SRT”变形可以再现上述两个点的“仿射”变形,只是我们在这里用不同的方式定义了变形。使用哪种形式由您决定,取决于您想要实现的目标。

图像坐标与像素坐标

在一般情况下使用控制点很简单,但是当您需要将扭曲的图像与另一个图像或绘制的结构对齐时,它会变得更加困难。原因是,虽然 IM 中大多数操作员以“像素位置”(例如当裁剪绘制等)处理坐标时,变形处理的是数学上的“图像坐标”。您需要记住的是,图像中的像素不是一个“点”,而实际上是一个“区域”,大小为 1 像素单位。也就是说,位于 10,10 的像素定义了一个颜色正方形区域,从 10 个单位向下/横向延伸到 11 个单位向下和横向。在图像坐标方面,“像素”的中心实际上位于 10.5,10.5。也就是说,当您扭曲图像以将“像素”的中心移动到特定位置时,需要添加 0.5。因此,要重新定位图像的角“像素”,您需要以位于 0.5,0,5Width-0.5,Height-0.5 的像素移动图像。另一方面,要根据图像的实际“边缘”重新定位图像,您只需使用坐标 0.0,0,0Width,Height。您只需要考虑您实际想要定位的是什么,是图像“像素”的中心还是图像的“边缘”。或者,对于您的特定问题,它是否真的重要。请记住,如果您想在扭曲的图像上绘制其他元素,您需要以“像素位置”给出绘制位置。是的,"-draw" 操作员可以使用浮点数绘制线、圆和其他形状。同样,对象的描边宽度和/或半径也可以作为浮点数给出。
[IM Output] ==> [IM Output]
小于 1.0 的描边宽度效果不好(参见 绘制线)。此外,区域填充会在填充区域的边缘添加额外的 0.5(与描边宽度加法匹配)(参见 绘制填充边界)。无论使用哪种实际的描边宽度,都会这样做。

有关更多信息,请参见 绘制填充边界。我认为这是一个错误。

使用百分比转义符的控制点

您也可以在变形参数中使用百分比转义。例如,您可以提取一个图像的图像属性,然后使用它们将另一个图像调整大小以匹配第一个图像。这里我获取内置图像 "rose:" 的大小,然后使用 '仿射' 变形将更大的 "logo:" 图像调整为相同大小(不保持纵横比)。

   magick rose: -set option:rw %w -set option:rh %h +delete \
           logo: -alpha set -virtual-pixel transparent \
           +distort Affine '0,0 0,0     %w,0 %[rw],0   0,%h  0,%[rh]' \
           +repage logo_sized_as_rose.png
[IM Output]
请注意,变形将在虚拟画布上生成略微更大的“分层图像”(包括负偏移),这就是为什么我需要在上述示例中包含 "+repage" 的原因。它还会具有模糊的边缘,因为变形正在生成图像的精确或真实变形,而不是经过清理的正交调整大小的图像。有关使用变形操作员以与调整大小操作员完全相同的方式调整图像大小的更高级示例,请参见变形与调整大小,以及下面的变形调整大小方法。您也可以使用百分比转义来计算基于图像在当前图像列表中位置的变形。在动画变形中给出了此类示例。

控制点最小二乘拟合

如果为“仿射”变形提供了超过 3 个控制点,或者为“透视”或“双线性”变形提供了超过 4 个点,ImageMagick 将对所有给定的点进行最小二乘平均,以找到这些变形的“平均”表示。这意味着,如果您试图将一个图像与另一个图像匹配(一种称为“图像配准”的技术),您可以定义超过最小所需点数,以便结果更精确的变形。当然,如果一个或多个点与其他点“不匹配”,那么结果将因“异常”点而扭曲,因为 IM 会尝试使用给定的所有控制点(包括不好的点)找到最佳拟合。在某些情况下,可能需要一些检查来查找和删除“不良坐标对”。

从文件中控制点

可以通过使用 '@filename' 语法从文件读取变形的数字(参数)列表,就像您为 "-annotate" 和 "label:" 输入文本一样(参见 文本参数中的转义字符)。例如,您可以这样指定一个变形...

  magick input.png  -distort Perspective '@file_of_coords.txt' output.png
文件名可以是 '@-',表示从标准输入读取文件。文件本身将作为字符串读取,并被视为变形所需的坐标(参数)列表。由于数字可以是逗号或空格分隔,这意味着坐标对可以整齐地按行排列,每行一个坐标对,形式为...
   X1   Y1   I1   J1
   X2   Y2   I2   J2
   X3   Y3   I3   J3
   X4   Y4   I4   J4
   ....
这使得使用 最小二乘拟合 使图像配准非常实用。由于文件只是一串每行四个数字,您可以使用其他文本处理脚本工具,如 "cut"、"paste"、"column",以及更高级的文本处理脚本工具,如 "sed"、"awk"、"perl" 等来操作坐标。坐标和变形参数文件的用途将在更高级的变形中变得更加重要,例如 'Shepards' 变形,以及计划中的“Grid”和 "Mesh' 的未来变形,其中可能涉及数百个坐标对。

仿射(三点)变形方法

仿射变形

'SRT' 变形以及上面显示的 'Affine' 变形的一点和两点形式实际上是 'Affine' 变形的完整三点形式的简化。事实上,如果您研究任何 'SRT' 变形的 "-verbose" 输出(参见 详细变形设置 以获取示例),您会发现它在内部实际上是 'AffineProjection' 变形(见下文)。上述方法唯一无法完全处理的变形效果是“剪切”,类似于 剪切操作员 所提供的剪切。为此,您需要使用三点仿射变形。您可以将其视为一个三点变形,通过想象第一个坐标映射为一个“原点”,另外两个坐标映射为从该原点出发的向量。例如,这里我绘制了一些文本,并叠加了一个红色和蓝色的“向量”来定义相对于该文本的三个控制点。现在,通过移动坐标(这两条线的 图像坐标),我们可以平移、旋转、缩放和剪切该文本图像,以适合这两条线的新的位置。

  magick -background lightblue -fill Gray -font Candice \
      -size 100x100 -gravity center label:Affine\! \
      -draw 'fill blue stroke blue path "M 3,60 32,60 M 27,58 27,62 32,60 Z"' \
      -draw 'fill red  stroke red  path "M 3,60  3,30 M  1,35  5,35  3,30 Z"' \
      label_axis.png
  magick label_axis.png \
          -distort Affine ' 3.5,60.5   3.5,60.5
                           32.5,60.5  32.5,60.5
                            3.5,30.5  33.5,20.5' label_axis_distort_shear.png
  magick label_axis.png \
          -distort Affine ' 3.5,60.5   3.5,60.5
                           32.5,60.5  27.5,85.5
                            3.5,30.5  27.5,35.5' label_axis_distort_rotate.png
  magick label_axis.png \
          -distort Affine ' 3.5,60.5  30.5,50.5
                           32.5,60.5  60.5,80.5
                            3.5,30.5  30.5,5.5' label_axis_distort_affine.png
[IM Output] ==> [IM Output] [IM Output] [IM Output]
在第一个示例中,只有第三个坐标(垂直的红线)被修改,导致图像被剪切,并沿 Y 轴拉伸。当然,它不必局限于 Y 轴。后面的示例对图像进行了更加激进的更改,包括旋转和平移。当然,注释文本 操作员也可以以相同的方式倾斜实际文本,但只能更改角度。该操作员不会以某些特定方向缩放或放大文本。也就是说,可以旋转一个“向量”,但不能将其拉长或缩短。有关示例表格,请参见 注释参数用法。仿射变形可以对任何图像执行这种类型的变形,而不仅仅是绘制的文本。使用少于或多于三个坐标对的仿射 如果只提供 1 个或 2 个控制点对,IM 将使用更有限形式的仿射变形来匹配这些较少点的移动。例如,如果只有一个坐标对,它将限制自己进行图像的无缩放平移。使用 2 个点,它将限制自己进行 '缩放-旋转-平移' 变形(无剪切)。有关示例,请参见前面有关 使用控制点的变形 的讨论。如果为 '仿射' 变形提供了超过 3 个控制点,那么 IM 将使用 最小二乘拟合 来找到最佳的“3 点”仿射变形,以匹配给定的所有坐标对。这意味着源图像控制点可能不会完全映射到目标图像控制点,但会根据给定的所有点找到一个最佳拟合的“平均值”。例如,如果您有一个文档扫描件,您可以找到并映射文档的所有 4 个角,以进行仿射变形,以校正文档的旋转和缩放。通过这种方式,您可以根据 4 个点而不是 3 个点获得更好的“平均”拟合。请注意,虽然更多的坐标可以产生更好更准确的变形,但如果一个坐标对非常差,那么最小二乘拟合可能根本无法产生非常好的拟合。可能需要进行一些检查来消除“不良坐标对”。未来:向 IM 添加一些代码来报告每个输入坐标对相对于其他坐标对的“准确性”,以帮助确定用户应该消除哪些“不良点”。

仿射投影变形

正如我之前提到的,'SRT' 畸变的各种参数和 '仿射' 畸变的控制点,在数学上被转换为 6 个特殊的数字,它们代表 '仿射投影' 的 '系数'。仿射投影中的这些数字是用于将源图像中的点映射到目标图像的 正向映射 的系数。也就是说,它们是用于将源图像 x,y 映射到目标图像 i,j 的数学值。6 个浮点参数(按给定的顺序)为...
sx, rx, ry, sy, tx, ty
它们反过来形成了畸变表达式。
Xd sx*Xs + ry*Ys + tx   ,       Yd rx*Xs + sy*Ys + ty
其中 "Xs,Ys" 是源图像坐标,"Xd,Yd" 是目标图像坐标。在内部,ImageMagick Distort 将反转上述等式,以便执行适当的 像素映射,将 "Xd,Yd" 坐标映射到源图像中 "Xs,Ys" 上的颜色。有关各种仿射投影矩阵值如何影响图像的更多信息,请参阅 仿射矩阵变换 子页面。如果你已经预先计算了这些系数(例如从 distort 的 详细输出 中提取,或者使用来自其他形式的输入参数的其他方法自己计算),那么你可以直接将它们提供给 IM 来扭曲图像。例如,这里我 '剪切' 图像,但使用角度来计算系数,而不是控制点的移动。

   angle=-20
   tan=`magick xc: -format "%[fx:tan( $angle *pi/180)]" info:`
   magick koala.gif -alpha set -virtual-pixel Transparent \
           +distort AffineProjection "1,$tan,0,1,0,0" +repage \
           koala_affine_proj.png
[IM Output]
在 ImageMagick 中执行此畸变的旧方法是使用 "-affine" 和 "-transform" 操作对。但是,从 IM v6.4.2-8 开始,这只是一个简单的 'AffineProjection' 调用,使用 Distort 运算符 的 'plus' 或 'bestfit' 形式。有关更多详细信息,请参阅 仿射矩阵变换 子页面。

仿射扭曲示例

仿射平铺

到目前为止,我们已经查看了所有三种上述仿射类畸变方法,它们还提供了一些有趣的方法来生成基于扭曲图像的各种平铺图案。

  magick checks.png    -alpha set    -virtual-pixel tile \
          -distort  ScaleRotateTranslate  '20,20  .5  30' \
          checks_srt_tile.png
  magick checks.png    -alpha set    -virtual-pixel tile \
          -distort  Affine  '0,0 10,10   0,89 10,50   89,0 50,0' \
          checks_affine_tile.png
  magick checks.png    -alpha set    -virtual-pixel tile \
          -distort  AffineProjection  '0.9,0.3,-0.2,0.7,20,15' \
          checks_amatrix_tile.png
[IM Output] ==> [IM Output] [IM Output] [IM Output]
以这种方式使用畸变映射实际上是 3D 图形库和游戏中 '纹理映射' 的工作原理。唯一的区别是它们将表面的 3 维坐标映射回二维图像。即使是 '无操作' 畸变("-distort SRT 0"),加上适当的 Distort 视窗 ,也提供了一种有用的方法来平铺整个图像序列,例如 动画闪光瓦片

  magick glitter_blue.gif -virtual-pixel tile \
          -filter point -set option:distort:viewport 100x100 -distort SRT 0 \
          glitter_blue_tiled.gif
[IM Output] ==> [IM Output]
请注意,我还使用了 "-filter point" 来关闭 EWA 重采样,以便加快操作速度,并确保源图像像素的完美(未采样)副本。 Distort 视窗 还可以指定偏移量,以便在结果图像上 '滚动' 平铺图像。

使用仿射分层的 3D 立方体

'仿射' 畸变及其控制点非常适合从三个图像生成正投影和等轴测立方体(请参阅维基百科,正投影等轴测投影 定义)。你只需要在目标图像上确定四个控制点。由于我们将使用 图像分层技术,因此这些点甚至可以具有负值,并允许 IM 相应地调整最终图像的大小以适应生成的扭曲图像。对于此示例,我将选择控制点 '0,0' 作为立方体的中心,以及三个点均匀地围绕该中心点,位于 '-87,-50'、'87,-50' 和 '0,100'。我唯一需要做的就是将三个(最好是正方形)图像的相应角点映射到这些控制点。

  magick \
     \( lena_orig.png -alpha set -virtual-pixel transparent \
        +distort Affine '0,512 0,0   0,0 -87,-50  512,512 87,-50' \) \
     \( mandrill_orig.png -alpha set -virtual-pixel transparent \
        +distort Affine '512,0 0,0   0,0 -87,-50  512,512 0,100' \) \
     \( pagoda_sm.jpg -alpha set -virtual-pixel transparent \
        +distort Affine '  0,0 0,0   0,320 0,100    320,0 87,-50' \) \
     \
     -background none -compose plus -layers merge +repage \
     -bordercolor black -compose over -border 5x2     isometric_cube.png
[IM Output]
请注意,我在扭曲图像时使用了图像实际边缘的 坐标。这意味着在数学意义上,这些图像应该完全吻合在一起。还要注意,我并没有简单地 '合成'(使用默认的 Over Alpha 合成)这些图像。如果你这样做,你会在图像之间获得轻微的透明 '间隙'。正确的方法(如所示)是使用 Plus Alpha 合成 来连接 '边缘连接' 的片段,这将导致完美的连接,没有透明的间隙。有关更多信息,请参阅 对齐两个蒙版图像。之后,我添加了一个额外的边框,并删除了所有透明度。这不是必需的,你可以轻松地使用任何背景(或 "none"),但这样做会突出显示图像中可能存在的任何 '间隙'。
[IM 输出] 右边显示的是该图像中一个这样的连接的放大图,显示了连接处没有任何 '黑色填充' 的间隙。有关创建等轴测立方体的另一种方法,无需使用 "-distort",请参阅 使用剪切的等轴测立方体。但是,此技术不允许你使用亚像素坐标(不是我在上面使用的任何坐标,但我可以使用),而是仅限于使用整个像素(整数)坐标来定位图像。

使用仿射剪切的 3D 阴影

上面使用的相同分层方法也可以用来生成奇怪形状的酷炫 3 维阴影。这将添加任何 '平坦' 形状的阴影,该形状是直立的。例如,让我们创建一个具有平坦底部的形状,这样它就可以直立起来。

  magick -background None -virtual-pixel Transparent -fill DodgerBlue \
          -pointsize 72 -font Ravie  label:A   -trim +repage \
          -gravity South -chop 0x5  standing_shape.png
[IM Output]
请注意,'形状' 具有一个平坦的底部,这也是图像的最后一行。这很重要,因为我们将在该行沿该行扭曲形状,以便阴影将单独连接到该行上的直立形状。以下是从此 '直立形状' 生成 3 维阴影的命令

  magick standing_shape.png   -flip +distort SRT '0,0 1,-1 0' \
          \( +clone -background Black -shadow 60x5+0+0 \
             -virtual-pixel Transparent \
             +distort Affine '0,0 0,0  100,0 100,0  0,100 100,50' \
          \) +swap -background white -layers merge \
          -fuzz 2% -trim +repage   standing_shadow.jpg
[IM Output]
上述命令执行了许多步骤来实现所示的结果。然而,最棘手的是第一行。它翻转图像,然后再次进行 '扭曲翻转'。这样做的结果是底部行现在位于虚拟画布上具有 Y=0 值的位置。也就是说,整个图像被赋予了负偏移量,以使其定位,以便底部行穿过虚拟画布的原点。通过执行此 '技巧',我们可以在提取的 '阴影' 上使用非常简单的 '仿射剪切' 来扭曲它。因此,我们不需要知道形状图像的大小来扭曲阴影,但仍然设法保持所有内容 '对齐',因为它们都沿着原始图像的底部(Y=0)行保持同步。你可以通过调整 '仿射剪切' 的最终坐标('100,50')来调整阴影的落入方向及其长度。前两个 '坐标对' 不应修改,因为这些 '锁定' 阴影到原始图像的底部行。但是,请注意,直到最后一步,所有图像都将包含负虚拟画布偏移量,因此如果你计划查看或保存中间处理图像,请注意。这种阴影效果的唯一问题是它是一种 '通用模糊'。也就是说,阴影不真实。实际上,阴影应该在它与 '直立形状' 连接的地方是清晰的,并且随着阴影离得更远而变得更加模糊。但是,这可以使用 可变模糊映射 来完成,例如在 距离模糊阴影字体 中使用的。

使用透视压缩的 3D 阴影

这是向阴影添加可变模糊的另一种方法,虽然我不推荐它,但它实现起来相当简单。这个示例是在 可变模糊映射 添加到 ImageMagick 之前开发的。基本上,你首先使用 透视畸变 (将在下面详细介绍)扭曲初始阴影形状,以便严重压缩阴影的 '远端部分',使其模糊,然后通过将其扭曲到我们上面使用的最终 '仿射剪切' 位置来扩展该压缩。

  magick standing_shape.png   -flip +distort SRT '0,0 1,-1 0' \
          \( +clone   -virtual-pixel Transparent -mattecolor None \
             +distort Perspective \
                '0,0 0,0  100,0 100,0   0,-100 45,-100   100,-100 60,-100' \
             -fuzz 2% -trim   -background Black -shadow 60x3+0+0 \
             +distort Perspective \
                '0,0 0,0  100,0 100,0   45,-100 -100,-50   60,-100 0,-50' \
          \) +swap -background white -layers merge \
          -fuzz 2% -trim +repage     standing_shadow_var.jpg
[IM Output]
这与原始的 3D 阴影示例 几乎完全相同,只是添加了一些额外的步骤。原始形状首先被扭曲成一个梯形,然后修剪任何多余的空间以加快下一步的速度。然后,我们从扭曲的形状中提取模糊的阴影。一旦从扭曲的图像创建了阴影图像,就使用相同的控制点来扭曲阴影图像,并将其移动到仿射剪切的位置。关键是阴影模糊发生在扭曲的图像上,然后被扭曲(在这种情况下是仿射剪切,同时进行)。结果,模糊也被扭曲和扩展,以便在阴影的顶部部分模糊更多,而在基线周围模糊更少。由于透视模糊的结果,我们得到了一个可变模糊,它应该在距地面基线约 100 像素的地方达到峰值。如初始透视模糊控制点所定义。

使用 Distort 调整图像大小

无论是 Distort 还是 Resize,在很多方面都非常相似。它们都是图像扭曲操作符,并且都使用 反向像素映射 来创建结果图像。它们还都使用 "-filter" 设置及其专家控制来确定颜色,尽管它们使用的方式大不相同。 Resize 是一种简化的(也是更常见的)图像扭曲操作,它允许你进行许多优化。它是正交对齐的,允许你在 resize 中使用 2 通道正交图像过滤方法。也就是说,它首先在一个维度上进行缩放,然后在另一个维度上进行缩放,使用一个中间临时图像。由于缩放因子在整个目标图像上是恒定的,并且边缘对齐到整个像素(整数)维度,该算法可以极大地简化其处理过程以及它使用的过滤器的缓存要求。所有这些限制都允许进行各种优化,使其与 distort 需要完成的工作相比速度非常快。Distort 也可以 Resize Images,但它是在一个通道内完成的,该通道直接从原始图像转换到新的结果图像。它不需要将边缘对齐到整数像素位置,并且可以旋转和缩放每个像素位置。换句话说,它是一个更通用的操作符,它需要对最终结果中的每个像素进行大量的额外处理,而优化的地方更少。为了让 Distort 生成与 Resize 等效的图像,它需要遵循完全相同的限制,并使用一些复杂的图像处理技巧。这在 IM 论坛上进行了讨论,在 Correct Resize (using distorts) 中,并导致了一种基于使用 仿射扭曲方法 的等效扭曲 resize 技术。
生成的 'Resize' 扭曲方法被添加到 ImageMagick 版本 6.6.9-2 中。该扭曲方法的命令行界面 (CLI) 版本将接受并魔术与 Resize 相同的 Geometry Argument,包括两个维度上的缩放因子的微小差异,使其成为一个直接的 resize 替代方案。

  magick logo:  -distort Resize 150x  logo_resized.png
[IM Output]
'Resize' 扭曲方法的其他 API 接口只接受两个数字作为参数,这两个数字被视为结果图像的最终整数大小。此时,它们不会接受包含各种 resize 控制标志的实际几何参数,这些标志会修改最终图像大小。也就是说,诸如百分比、只放大/缩小甚至保持纵横比之类的标志都不可用。

这将留待这些 API 的维护者为这种特殊的图像扭曲方法添加这种支持。
上述 Distort Resize 与正常 Resize Operator 之间的真正区别在于,扭曲版本使用了一个速度慢得多的单通道 圆柱形(椭圆形)过滤器 来确定每个像素的最终颜色。换句话说,它提供了 2 通道正交过滤器(resize)与一个通道但二维圆柱形过滤器(扭曲 resize)之间的直接比较。参见 Distort vs Resize 以了解一个这样的比较。

Distort Resize 内部

以下是上述 Distort Resize 在内部执行的等效操作。

  magick logo:  -alpha set -virtual-pixel transparent \
          +distort Affine '0,0 0,0   %w,0 150,0   0,%h 0,113' \
          -alpha off  -crop 150x113+0+0 +repage   distort_resize.png
值 '150' 和 '113'(在两个地方使用)是最终图像的所需大小,最接近整数。它被计算出来,试图尽可能地保持图像纵横比,同时保持最终整数大小限制。它们通常由 ImageMagick 从给定的 resize Geometry Argument 中计算出来,使用一个单独的 API 函数。然后它启用透明度和透明 Virtual Pixels,这样外部 'virtual pixels' 就不会参与最终像素颜色的计算。当扭曲完成后,透明度再次被移除(关闭),并且 distort 添加的 'buffer' 像素使用 Image Crop 被移除。由于使用了透明像素,上面的命令只适用于不包含任何透明度的图像,例如上面示例的 "logo:" 内置图像。这是更复杂版本,需要将 Virtual Pixels 的影响与图像中可能存在的任何透明度分离。

  magick logo: -alpha set -virtual-pixel transparent \
          \( +clone -alpha extract -alpha opaque \) \
          +distort Affine '0,0 0,0   %w,0 150,0   0,%h 0,113' \
          -alpha off -crop 150x113+0+0 +repage \
          -compose CopyOpacity -composite      distort_resize_trans.png
这执行了两个扭曲:首先扭曲图像,然后分别扭曲 alpha(透明度)通道,在每种情况下都使用 transparent 来去除虚拟像素的影响。因此,它的速度至少是原始图像中没有透明度的情况下的两倍。这两种技术都由 Distort Resize Method 在内部实现。因此,这种 '方法' 实际上是一个用户方便的 '宏',而不是一个真正的扭曲方法,它是一个 '仿射' 扭曲。

四点扭曲方法

透视扭曲

可能是最常见的扭曲类型请求,是为了进行快速的透视扭曲操作。这是一种 4 点扭曲,因此需要至少 4 组控制点对,或 16 个浮点数。例如,这里我有一张建筑物的图片。从这张图片中,我手动找到了 4 个点(红色)的位置。我还定义了最终图像中这些点变换到的最终位置(蓝色),以便 '拉直' 或 '校正' 建筑物的正面。

  magick building.jpg \
          -draw 'fill none stroke red polygon 7,40 4,124, 85,122, 85,2' \
          building_before.jpg
  magick building.jpg \
          -draw 'fill none stroke blue polygon 4,30 4,123, 100,123, 100,30' \
          building_after.jpg
[IM Output] ==> [IM Output] [IM Output]
要进行实际的图像扭曲,你只需要将这些坐标输入 "-distort" 的 'perspective' 方法中。

  magick building.jpg -alpha set -virtual-pixel transparent \
         -distort Perspective \
              '7,40 4,30   4,124 4,123   85,122 100,123   85,2 100,30' \
          building_pers.png
[IM Output] ==> [IM Output]
注意右上角的空白区域,那里扭曲 '错过了' 源图像中的像素数据。IM 在这种情况下所做的是由 "-virtual-pixel" 设置控制(参见 Virtual Pixel)。不那么明显的是,原始图像的最左边边缘也有一部分 '丢失',原因相同。有趣的是,让我们也反转扭曲,通过交换每个映射对的坐标。这让我们可以看到图像被扭曲降解了多少。

  magick building_pers.png  -alpha set -virtual-pixel transparent \
         -distort Perspective \
              '4,30 7,40   4,123 4,124   100,123 85,122   100,30 85,2' \
          building_pers_rev.png
[IM Output] ==> [IM Output] ==> [IM Output]
还不错。存在很多 '模糊',但这是不可避免的。请注意,'模糊' 在图像的右侧更严重,因为图像在那里被压缩得最多。所有扭曲都存在这种压缩问题,因此你应该始终尝试从原始图像扭曲,而不是扭曲已经扭曲的图像。这里还有另一个例子,使用这种变换,使用我们上面创建的特殊棋盘格测试图像,我们扭曲它然后反转扭曲。

  magick checks.png        -alpha set    -virtual-pixel transparent \
          -distort Perspective '0,0,0,0  0,90,0,90  90,0,90,25  90,90,90,65' \
          checks_pers.png
  magick checks_pers.png   -alpha set    -virtual-pixel transparent \
          -distort Perspective '0,0,0,0  0,90,0,90  90,25,90,0  90,65,90,90' \
          checks_pers_rev.png
[IM Output] ==> [IM Output] ==> [IM Output]
你可以看到图像压缩导致的轻微模糊,但图像基本上被恢复了。实际上发生的是,IM 使用所有给定的控制点对来计算 'Perspective Projection'(参见下一节)的适当系数。如果你包含一个 Verbose 设置,你可以看到 IM 用于执行这种扭曲的系数和 DIY FX Equivalent。如果只提供 3 个或更少的控制点对,IM 将自动回退到更简单的 'Affine' 扭曲。而超过 4 个点(用于 'Image Registration')将被 Least Squares Fitted 以找到所有给定控制点的最佳拟合扭曲。 FUTURE: Alternative. 这四个坐标也可以表示一个三角形和中心点。你可以固定三角形并移动中心点,或者固定中心点并移动其他三个坐标,以生成透视视图。如果你想更详细地了解扭曲的工作原理,请查看下面的 Perspective Internals。你还可以查看由 Gernot Hoffmann 在 PDF 论文 Perspective Rectification 中介绍的 Postscript 实现。还可以查看 Leptonica Affine and Perspective Transforms

查看远方地平线

如果你调整坐标以在图像边界内产生一个 '消失点',你可以使用 Perspective Distortions 产生一些非常不寻常的效果。

  magick checks.png -mattecolor DodgerBlue \
          -virtual-pixel background -background Green \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          checks_horizon.png
[IM Output]
好吧,我们使用 'Green' 作为 '围绕' 原始图像的虚拟像素,我们使用 Virtual Pixel Background Settings 启用了它。但更有趣的是 '蓝色' 的出现,它使用 "-mattecolor" 设置定义。这种 '蓝色' 代表扭曲生成的像素 无效 的区域,在这样的区域中,"-distort" 操作符将只输出 "-mattecolor" 设置。对于 Perspective Distortion,任何最终出现在结果图像 '天空' 中的像素都将被归类为无效。它还将 '天空' 定义为源图像不会出现的 '地平线' 的一侧。只有当结果图像被扭曲高度缩短时,'天空' 才会出现在透视扭曲的图像中。如果你不希望在最终图像结果中出现 '天空',那么最好的想法是将 "-background" 和 "-mattecolor" 设置为使用相同的颜色。当使用特殊的无限平铺 Virtual Pixel 设置时,Perspective Distortion 会变得更加有趣。例如,这里我们使用 'tile' 设置来生成一个无限平铺的平面。

  magick checks.png  -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_tile.png
[IM Output]
关于这张图像,需要提醒你一下。要求一个无限平铺的图像会 非常慢 地生成。图像越大,速度越慢。你可以使用 "-monitor" Operational Control Setting 来监控 "-distort"(或任何其他缓慢的图像处理任务)的进度。基本上,对于一个靠近地平线的单个像素,ImageMagick 需要对原始图像中的大量像素进行平均,以确定适当的颜色。这可能需要很长时间。ImageMagick 确实试图通过缓存信息并使用对各种 Virtual Pixel 设置的内置知识来限制它处理这些靠近地平线的像素所使用的时间,但它仍然可能需要很长时间。有关此方法的更多详细信息,请参见上面的 Area Resampling。另一个无限平铺的透视图像可以通过使用 Random Virtual Pixel Setting 来生成...

  magick checks.png  -virtual-pixel random -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_random.png
[IM Output]
发生的情况是,图像周围的所有虚拟像素都是图像本身内任何像素的随机选择。结果是地面由随机噪声组成,随着你向图像的视野看去,噪声变得更平滑和更模糊。它给了一种自然的深度感,没有任何特定的重复模式。在这里,我重复了上面的操作,但使用了一个纯黑白的源图像。但是,我对实际的扭曲图像不感兴趣,只对生成的 虚拟像素 'random' 模式感兴趣,因此我通过使用特殊的 '-set option:distort:viewport' 设置,改变了正在查看的“扭曲图像空间”的哪一部分。此设置会覆盖正在查看的扭曲空间区域的正常大小和位置。在这种情况下,该区域只包含虚拟像素,而不包含扭曲的图像。

  magick -size 90x90 pattern:gray50 -alpha set \
       -virtual-pixel random -mattecolor none \
       -set option:distort:viewport 120x120+100-15 \
       -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
       +repage -size 120x50 gradient:dodgerblue-tomato \
       -compose DstOver -composite    sunset_horizon.png
[IM Output]
为了完成图像,我删除了视窗偏移(使用 "+repage" ),并将 覆盖或 DstOver 渐变的日落颜色到透明的“天空”(使用 "alpha setmattecolor" 设置)。这是一张非常有趣的图像,可以用作其他一些图像处理工作的背景。你可以调整失真参数以调整地平线的高度和斜率。
这里是一个更传统的平铺透视失真的测试。

  magick pattern:checkerboard -scale 120x120 -normalize \
          -virtual-pixel tile  -distort Perspective \
             '0,0 10,61   119,0 60,60   0,119 5,114   119,119 125,110' \
          checkered_plain.gif
[IM Output]
在我的研究中,我发现上述测试具有误导性,因为它没有真正表明图像近似统一比例(前景区域,而不是远处区域)的区域重采样技术的质量。这是一个对重采样问题的仔细观察,例如在 重采样伪像 中所述。这张最后一张图像还显示了一个靠近地平线的“截止”点,在该点,ImageMagick 决定,它不值得尝试确定像素的适当颜色(考虑到当前的虚拟像素设置),而是短路 EWA 算法并使用整个图像的平均颜色。只有在图像中存在大尺度对角线颜色模式时,它才会在这张图像中可见。图像的平均颜色在每个失真操作中只计算一次,并且只在第一次需要时计算。通过使用它,ImageMagick 在计算接近地平线的颜色时节省了大量时间,而通常结果将是图像的平均颜色。当椭圆变得如此细长以至于超过浮点数限制,或者采样像素数(椭圆的边界平行四边形)变得比输入源图像大 4 倍时,就会发生这种情况。目前这不可用户设置。

3d 盒子,透视分层

"+distort" 的“加号”形式,它确保整个扭曲的图像保存在正确定位的图层(或“虚拟画布”)中,其设计是为了,如果使用相同的“控制点”来扭曲图像,那么这些点将在“虚拟空间”中对齐。这意味着如果图像被 图层合并 到一起,那么这些图像也会根据控制点对齐。例如,这里我们生成两个图像,一个“前面”图像和一个“脊柱”图像,这样两个边缘控制点就可以彼此对齐,形成一个盒子的脊柱。

  # Generate a Spine Image
  magick -size 200x40 xc:skyblue \
    -pointsize 20 -gravity north -annotate +5+0 'IM Examples' \
    -pointsize 10 -gravity south -annotate +0+0 'ImageMagick' \
    -stroke blue -strokewidth 2 -draw 'line 30,0 30,40' \
    -rotate -90 box_spine.jpg

  # generate the front cover
  magick -size 150x200 xc:skyblue \
    -fill black -pointsize 20 -gravity north -annotate +0+5 'IM Examples' \
    -fill blue -pointsize 15 -gravity northeast -annotate +5+28 'Box Set' \
    -fill black -pointsize 15 -gravity south -annotate +0+5 'ImageMagick' \
    -stroke blue -strokewidth 2 -draw 'line 0,169 150,169' \
    \( logo.gif -resize 100x100 \) \
    -gravity center -compose multiply -composite box_front.jpg

  # Distort both images and merge using common points.
  magick \
    \( box_spine.jpg -alpha set -virtual-pixel transparent \
       +distort Perspective \
           '0,0 -30,20  0,200 -30,179  40,200 0,200  40,0 0,0' \) \
    \( box_front.jpg -alpha set -virtual-pixel transparent \
       +distort Perspective \
           '0,0 0,0  0,200  0,200  150,200 100,156  150,0 100,30' \) \
    \
    -background black -compose plus -layers merge  +repage \
    -bordercolor black -compose over -border 15x2    box_set.jpg
[IM Output] [IM Output] ==> [IM Output]
还要注意使用 加号 Alpha 合成 来连接“边缘连接”的片段。这是为了防止在两个图像之间生成“半透明间隙”。有关更多信息,请参见上面的 3D 立方体 示例,以及 对齐两个蒙版图像。使用这种位置意味着几乎所有“脊柱”图像实际上都扭曲到一个负的“x”位置。因此,生成的图像在虚拟画布上具有负偏移。当使用操作符的分层 "+distort" 版本时,IM 在处理具有负偏移的分层图像方面没有任何问题。图层合并 操作符也旨在处理具有负偏移的分层图像,将两个图像干净地“缝合”在一起。我仍然需要使用最终的 "+repage" 从最终图像中删除该负偏移,在它们被“合并”在一起之后。如果我不这样做,其他程序(如 Web 浏览器)可能不理解这些负偏移,并会导致未定义的效果。上面的示例也被放置在 shell 脚本 "box_set_example" 中,以便你更方便地下载和使用它。你可以进一步将它扩展到添加“盒子”的镜像,这些镜像被它所放置的表面反射,尽管你可能还想以某种方式重新着色或使该图像变暗,使其看起来更逼真。有关此类镜像技术的详细信息,请参阅 反射
在 PHP 中的另一个示例是在关于在无边框画布框架上包裹“照片”的讨论中开发的。有关更多详细信息,请参阅 画布包裹转换
最后,这里有一个由 Jean-François Hren 为 www.animecoversfan.com 提供的精彩示例,它在 IM 讨论论坛 上得到了广泛讨论。
[Diagram]
此图像是通过获取一个艺术性的动画视频盒子封面图像,将该封面分成 3 个部分(“封面”、“脊柱”和“背面”),分别扭曲成分层图像,添加第四个“磁盘”图像,然后合并在一起而创建的。然后,通过添加高光和阴影效果(使用 强光 图像合成),以及添加边框和半透明阴影效果(使用 复制不透明度)来完成该图像。更令人惊叹的是,整个过程是由单个 "magick" 命令从输入图像完成的。它是一个很好的例子,展示了 IM 可以做什么,以及生成复杂命令脚本的过程。我建议阅读 论坛讨论,因为它包含许多提示、技巧和一般的调试技术。(欢迎提供更多贡献的示例)

透视投影失真

就像 '仿射' 失真可以直接处理给定 '仿射投影' 的数学系数一样,'透视' 也可以通过 '透视投影' 失真的 8 个系数来处理。与以前一样,这些数字代表用于源图像中点到目标图像的 正向映射 的系数。也就是说,它们是在源图像中将 x,y 映射到目标图像 i,j 的数学值。8 个浮点参数是(按给定的顺序)...
sx, ry, tx,
rx, sy, ty,
px, py
这些系数值反过来构成表达式。
Xd sx*Xs + ry*Ys + tx   ,       Yd rx*Xs + sy*Ys + ty


 px*Xs + py*Ys + 1.0   px*Xs + py*Ys + 1.0 
其中 "Xs,Ys" 是源图像坐标,"Xd,Yd" 是目标图像坐标。在内部,ImageMagick Distort 将反转上述等式,以便执行适当的 反向像素映射,将 "Xd,Yd" 坐标映射到源图像中 "Xs,Ys" 的颜色。'透视投影' 的前 6 个值实际上与 '仿射投影' 的系数相同,尽管它们略微重新排序以更符合逻辑(在“矩阵数学”术语中,前 6 个元素被对角线转置)。额外的两个参数 px,py 形成了对整个失真的缩放除数,这会导致图像根据给定的值在特定方向上看起来更小,从而为扭曲的图像提供透视“距离”效果。如果这两个值设置为零,则 '透视投影' 失真将等效于 '仿射投影'。例如...

  magick rose: -alpha set -virtual-pixel transparent \
          -distort Perspective-Projection \
             '1.40, 0.25, 3.0    0.15, 1.30, 0.0    0.007, 0.009' \
          perspective_projection_rose.png
[IM Output]
记住你给出的矩阵是正向投影矩阵,它将源图像坐标映射到目标图像坐标。在内部,ImageMagick 将反转矩阵,以便它可以将目标图像坐标映射到源图像坐标。如果你想查看这些值是什么,请使用 详细失真选项,以便 IM 输出其内部系数作为 FX 操作符表达式(见下文)。

透视内部

如果你在透视失真之前添加 "-verbose"(见上面的 详细失真摘要),IM 将输出两个操作符,它们应该是 "-distort" 操作符的近似等效替换。一个是 VERY SLOW "-fx" 版本(见 FX DIY 操作符)。另一个将是正向映射透视投影矩阵。例如...

  magick rose: -alpha set -virtual-pixel transparent -verbose \
          -distort Perspective "0,0,3,0 0,46,10,46 70,0,70,7 70,46,60,40" \
          +verbose perspective_rose.png
[IM Output]
[IM Text]
第一部分透视投影可用于将源坐标映射到目标坐标。公式如上。
  i = ( 1.430099*x +0.246650*y +3 )/( 0.006757*x + 0.009448*y +1 )  
  j = ( 0.147296*x +1.434591*y +0 )/( 0.006757*x + 0.009448*y +1 )  
定位扭曲的图层图像 中最后一组示例中,展示了提取和使用这些值的示例。另一方面,第二个 FX 等效部分使用另一组 8 个系数,它执行 反向像素映射,图像失真实际上需要应用。具体来说,是...
  x = ( 0.711858*i -0.108326*j -2.135575 )/(-0.004119*i -0.005877*j +1 )  
  y = (-0.073090*i +0.699571*j +0.219269 )/(-0.004119*i -0.005877*j +1 )  
注意在输出的FX 等效公式中,除数系数首先被使用,因为它们是 X 和 Y 坐标方程的共同部分。记住你给出的所有坐标都是图像坐标,而不是像素坐标,有关详细信息,请参阅 图像坐标与像素坐标。因此,任何像素位置都需要在应用上述公式之前将 0.5 添加到输入像素坐标中,然后从最终坐标中减去 0.5,将其变回像素(绘制)坐标。你可以在上面的FX 等效代码中看到这一点。FX 等效中的最终测试,在源图像查找之前,处理无效的“天空”像素,其中目标未能正确映射到源图像。但是,它只是用 'blue' 代替这些像素,而不是 "-mattecolor",并且不提供内部算法为透视失真提供的任何地平线反锯齿。
透视正向映射示例... 这些映射允许你将一个图像中的特定坐标映射到另一个图像中的位置(任一方向)。例如,源玫瑰图像中心的暗点位于像素坐标 '39,20'。通过添加 ½ 将其映射到图像坐标,得到 '39.5,20.5'。现在我们可以使用 x,y 到 i,j 方程将其映射到目标图像坐标 '44.2,24.1'。最后,通过减去 ½ 将其转换为“绘制”像素坐标,我们得到了 '43.7,23.6' 的最终位置。在这里,我在输入和输出图像上都使用圆圈标记了该坐标。

  magick rose: -fill none -stroke black \
          -draw 'circle 39,20 39,24'    rose_marked.png

  magick perspective_rose.png -fill none -stroke black \
          -draw 'circle 43.7,23.6 43.7,26.6'  perspective_rose_marked.png
[IM Output] ==> [IM Output]
如你所见,透视扭曲图像中的同一个点已在两个图像中被正确定位(甚至精确到亚像素级别)!

双线性失真

'双线性' 失真方法实现了另一种类型的 4 点失真。但是,它们不像上面我们看到的 '透视' 失真那样简单。但正如你将看到的,它是一个非常有用的替代失真。

正向双线性失真

例如,让我们取一张网格叠加在上面的山魈的特殊测试图像,并使用透视和双线性对其进行扭曲。

  magick mandrill_grid.jpg -alpha set -virtual-pixel black \
       -distort Perspective \
              '0,0 26,0   128,0 114,23   128,128 128,100   0,128 0,123' \
       mandrill_pers.jpg
  magick mandrill_grid.jpg -alpha set -virtual-pixel black -interpolate Spline \
       -distort BilinearForward \
              '0,0 26,0   128,0 114,23   128,128 128,100   0,128 0,123' \
       mandrill_blin.jpg
[IM Output]
原始图像
==> [IM Output]
透视
[IM Output]
双线性
首先,你应该注意到两种扭曲都正确地将图像从一组控制点映射到另一组点。另外,源图像中的所有水平线和垂直线在两种扭曲中都保持直线。然而,相似性到此为止。透视会减少线之间的间距,使即使对角线也保持直线。这导致正方形的面积变小,从而使右上角呈现出真实的“远处”外观。另一方面,双线性不会使图像的一侧看起来“更远”,也不会试图保持线直线。它试图做的是保持所有线之间的间距恒定,但这会导致对角线变弯曲。也就是说,它保留了沿任何给定线的距离比率。也就是说,每个线段的相对长度沿线的整个长度保持相同,即使线本身可能整体弯曲、曲线或缩短。这意味着以上示例中的网格间距在整个图像中保持恒定比例,并且右上角的扭曲正方形仍然与左下角的扭曲正方形大小大致相同。图像保持“扁平外观”,只是扭曲成不同的形状。请注意,(正向)双线性确实确保原始图像中的任何水平线或垂直线在最终图像中保持直线。也就是说,它将正交对齐的矩形转换为指定的四边形,使得原始矩形的每条边在整个线上保持直线并具有恒定的比例。正是扭曲的这个方面使“BilinearForward”扭曲在更复杂的“网格”扭曲中变得有用。也就是说,因为两个相邻的“四边形”即使它们可能被非常不同地扭曲,它们仍然会正确地边缘对边缘地对齐。以下是使用内置玫瑰图像的非常严重扭曲,对“透视”和“BilinearForward”的另一个比较...


magick rose: -alpha set -virtual-pixel transparent \ -distort Perspective "0,0,3,0 0,46,10,46 70,0,70,7 70,46,60,40" \ perspective_rose.png magick rose: -alpha set -virtual-pixel transparent -interpolate Spline \ -distort BilinearForward "0,0,3,0 0,46,10,46 70,0,70,7 70,46,60,40" \ bilinear_rose.png
[IM Output]
原始图像
==> [IM Output]
透视
[IM Output]
双线性
为了实现其目标(保留所有直线),透视扭曲似乎将几乎整个图像“吸入”到右侧的较小区域,而双线性扭曲将中心玫瑰保持在其结果的中心。它再次保留了距离比率,使玫瑰在左右边缘之间等距。它所做的全部是沿其长度简单地线性地垂直压缩图像的高度。这种“BilinearForward”扭曲的方面也使其被称为“梯形”扭曲。也就是说,当只有一个方向被缩放时,只是在一个方向上线性压缩图像。该压缩方向甚至可以是倾斜的,而不是沿着一个轴对齐。
请注意,由于执行“BilinearForward”扭曲所需的逆像素映射的复杂性,目前已关闭 区域重采样

因此,极度压缩的区域(大于 2 倍)可能会显示一些混叠效果(请参见上面示例中的线边缘。但是,使用 超采样 或“-interpolate Spline”可以用于提高最终图像的质量。
在 IM v6.5.7-0 之前,“BilinearForward”扭曲仍在开发中,并且在特定情况下存在特定“退化”情况的问题,这些情况可能会在特定情况下导致“黑色”错误图像。

反向双线性扭曲

因为只有水平线和垂直线保持直线,所以你不能使用“BilinearForward 扭曲来反转扭曲。由于变换图像中的网格线不再是水平或垂直的,因此它们在结果图像中将不再保持直线!例如,交换坐标对并重新应用“正向”扭曲(例如,我们上面使用“透视”扭曲所做的那样)将无法恢复原始图像。

  magick mandrill_blin.jpg -alpha set -virtual-pixel black \
       -distort BilinearForward \
              '26,0 0,0   114,23 128,0   128,100 128,128  0,123 0,128' \
       mandrill_blin_back.jpg
[IM Output] ==> [IM Output]
请注意,实际指定的坐标确实正确地定位了它们自己,但图像扭曲尚未反转。总之,“BilinearForward”扭曲不是它自己的反转。要还原图像,你需要使用稍微不同但密切相关的扭曲。几何变换的数学反转已实现为“BilinearReverse”扭曲。例如...

  magick mandrill_blin.jpg -alpha set -virtual-pixel black \
       -distort BilinearReverse \
              '26,0 0,0   114,23 128,0   128,100 128,128  0,123 0,128' \
       mandrill_blin_rev.jpg
[IM Output] ==> [IM Output]
如前所述,由于“BilinearForward”扭曲的复杂性,目前已关闭 区域重采样,这在上面会导致严重的混叠效果。
BilinearReverse”具有与“BilinearFoward”相同的距离比率保留功能,但会将任何四边形神奇地转换为正交对齐的矩形,确保四边形的边在映射到垂直和水平对齐时保持直线。如你在上面可以看到的那样。
在 IM v6.5.1-2 之前,“BilinearReverse”扭曲只是简单地实现为“Bilinear”。
双线性扭曲的一些实现(包括 IM 的旧版本和 Leptonica 库)只实现了上述更简单的(反向)双线性扭曲版本。但是,这种扭曲不太适合“正向映射”矩形图像。例如,我尝试在此使用“BilinearReverse”进行扭曲,该扭曲可能应该使用“BilinearForward”扭曲。

  magick mandrill_grid.jpg -alpha set -virtual-pixel black \
       -distort BilinearReverse \
              '0,0 26,0   128,0 114,23   128,128 128,100   0,128 0,123' \
       mandrill_blin_rev2.jpg
[IM Output] ==> [IM Output]
如你所见,由于目标四边形不是正交矩形,图像被严重扭曲,产生了大量向内弯曲的线。

平铺双线性扭曲

现在,虽然“BilinearReverse”从矩形图像中生成“曲线”图像。这种效果确实产生了有趣的平铺模式,这些模式似乎生成了弯曲的 3 维表面。例如,通过应用与上面用于 查看远方地平线 相同的变换,我们得到了这个有趣的结果。

  magick checks.png  -virtual-pixel tile  -mattecolor DodgerBlue \
          -distort BilinearReverse \
               '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          bilinear_rev_tile.png
[IM Output]
实际上,“BilinearReverse”永远不会生成“地平线”(无效像素)。另一方面,使用“BilinearForward”往往会经常生成“天空”或“无效像素”(用当前的“-mattecolor”填充)。实际上,平铺模式往往会变得相当疯狂...

  magick checks.png  -virtual-pixel tile  -mattecolor DodgerBlue \
          -interpolate Spline  -distort BilinearForward \
               '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          bilinear_fwd_tile.png
[IM Output]
如前所述,由于“BilinearForward”扭曲的复杂性,目前已关闭 区域重采样,这在上面会导致严重的混叠效果。
因此,我不建议使用“BilinearForward”的平铺形式。但是,我建议你在使用正向扭曲时定义一个适当的“-mattecolor”,以防止出现意外的灰色“天空”区域。

双线性内部

使用“正向映射双线性扭曲”将源图像中的坐标映射到目标图像中的坐标的实际公式是...
Xd C0*Xs + C1*Ys + C2*Xs*Ys + C3   ,       Yd C4*Xs + C5*Ys + C6*Xs*Ys + C7
但是,由于 IM 使用 反向像素映射 技术来实现扭曲,因此需要反转上述公式。一个复杂的过程,需要求解二次方程、平方根和一整页的代数。如果你要求 IM 详细 输出 FX 等效项,你将看到这种复杂性。例如,使用我们之前创建的检查图像...

  magick checks.png -alpha set -virtual-pixel transparent -mattecolor none \
      -interpolate Spline -verbose -distort BilinearForward \
                   '0,0,0,0  0,90,0,90  90,0,60,30  90,90,90,90' \
      +verbose bilinear_checks.png
[IM Output]
[IM Text]
(rt > 0 ) ? red :”检查是在“FX 等效项”的最后一行中的,目的是避免无效的负平方根。这是在之前示例中显示“天空”效果的检查。另一方面,由于 反向双线性扭曲 非常简单,因此你可以直接应用更简单的多项式方程来反转之前的扭曲...

  magick bilinear_checks.png  -virtual-pixel transparent \
      -verbose -distort BilinearReverse \
                   '0,0,0,0  0,90,0,90  60,30,90,0  90,90,90,90' \
      +verbose bilinear_checks_rev.png
[IM Output]
[IM Text]
如你所见,结果方程非常简单,因为我们现在正在应用它来进行目标坐标到源图像坐标的 反向像素映射
上面看到的混叠效果是由“BilinearForward”引起的,而不是由“BilinearReverse”扭曲引起的。这是因为目前 区域重采样 由于其复杂性而被关闭,用于“正向”映射的版本。
有关更多阅读内容,我建议你参考 Leptonica 仿射和透视变换

组合双线性扭曲

正在建设中
两种双线性扭曲方法结合在一起,可以让你直接将任何四边形扭曲成任何其他四边形,同时保持四边形的边直线。本质上,你可以首先“反转”扭曲一个四边形,使其变成矩形图像,然后你可以“正向”扭曲该矩形,使其变成最终的四边形。这种类型的扭曲还意味着你可以取任何矩形的坐标网格,并将其扭曲成另一个矩形的坐标网格。这被称为“网格”扭曲。这项技术是图像变形的主要基础,你可以在其中定义两个图像上的矩形网格线,并使用它们将图像合并成一个中间合成,甚至生成从一个图像正确变形到另一个图像的动画。但是,这还没有实现,但这是一个计划中的补充。

多项式扭曲(使用多项式拟合扭曲)

Polynomial”扭曲与大多数之前的扭曲方法一样,也映射控制点对,但使用标准多项式方程。这意味着在给出控制点之前需要一个额外的参数。
阶数     X1,Y1 I1,J1     X2,Y2 I2,J2     X3,Y3 I3,J3     X4,Y4 I4,J4 . . . .
阶数”参数通常是从“1”开始的整数,但也可以使用“1.5”的特殊值。这定义了将应用的二维数学方程(使用“x”和“y”)的“阶数”或复杂度。例如,阶数“1”多项式将拟合以下形式的方程...
Xd C2x*Xs + C1x*Ys + C0x   ,       Yd C2y*Xs + C1y*Ys + C0y
如果你将其与用于 仿射投影 的方程进行比较,你会发现它与之等效。由于每个 X 和 Y 公式都需要 3 个常数,因此你还需要提供至少 3 个 X,Y 坐标对。更多坐标将导致方程对给定的坐标进行最小二乘拟合。下一个“阶数”或“1.5”等效于“BilinearReverse”(记住该方程用于将目标坐标映射到源图像)。
Xd C3x*Xs*Ys + C2x*Xs + C1x*Ys + C0x   ,       Yd C3x*Xs*Ys + C2y*Xs + C1y*Ys + C0y
就像“BilinearReverse”扭曲一样,它需要至少 4 个坐标。例如... 基本上这与阶数“1”方程完全相同,只是在多项式方程中添加了 1 个额外项。也就是说,由于每个方程现在每个轴有 4 个项,4 个常数,因此你至少需要 4 个坐标对,以允许 IM 确定这些常数。

  magick mandrill_grid.jpg -alpha set -virtual-pixel black \
       -distort Polynomial \
              '1.5   0,0 26,0   128,0 114,23   128,128 128,100   0,128 0,123' \
       mandrill_poly_1.5.jpg
[IM Output] ==> [IM Output]
对于阶数“2”,多项式方程被进一步扩展,成为一个完整的二次拟合,需要至少 6 个坐标对。
Xd C5x*Xs2 + C4x*Xs*Ys + C3x*Ys2   + C2x*Xs + C1x*Ys + C0x
Yd C5y*Xs2 + C4y*Xs*Ys + C3y*Ys2   + C2y*Xs + C1y*Ys + C0y
基本上,这与“1”阶方程完全相同,只是在多项式方程前添加了 3 个额外的项(2 阶 + 1)。也就是说,由于每个方程现在有 6 个项,且有 6 个常数,因此您现在需要至少 6 个坐标才能让 IM 确定这些常数。在此之后,每个连续阶的多项式会在每个方程对中再添加 'order'+1 个项。因此,“3”阶三次拟合多项式至少需要 10 对坐标才能完全定义,而“4”阶五次拟合多项式需要 15 对坐标。您可以使用 详细的失真摘要 查看多项式失真拟合到指定坐标的所得方程。举一个更大的例子,我有一个网格图像。我还有一个大型坐标集(存储在文件 “grid16_control_points.txt” 中),说明我要如何扭曲该网格。然后我要求 IM 生成一个三次多项式来“最佳拟合”输入坐标。

  # warp image
  magick grid16.png -virtual-pixel gray \
          -distort polynomial "3 $(cat grid16_control_points.txt)" \
          grid16_polynomial.png

  # reverse image coordinate order
  awk '{print $3, $4, $1, $2}' grid16_control_points.txt \
                             > grid16_cp_inverse.txt

  # warp image back again
  magick grid16_polynomial.png -virtual-pixel gray \
          -distort polynomial "3 $(cat grid16_cp_inverse.txt)" \
          grid16_restored.png
[IM Output] ==> [IM Output] ==> [IM Output]
这个小型 “awk” 脚本将原始的 X,Y 控制点对集的顺序颠倒,以便我们可以使用新文件来尝试“撤消”失真。
控制点文件 “grid16_control_points.txt” 中的坐标是图像坐标,这意味着每个数字都指的是它所指的像素的中心。如果没有额外的 0.5,这些值将是整数“像素坐标”。请参见上面 图像坐标与像素坐标

这些值纯粹是通过使用图像查看器手动查找确定的,因此并不十分准确。这可能是某些反向失真伪影的来源,尽管多项式方程的功能性“最佳拟合”会减少整体失真效果。

这表明,虽然多项式失真有效,而且效果很好,但它不是一种精确或可逆的失真。本质上,这 81 个坐标是“平均”在一起的,以便生成输入坐标的数学“最佳拟合”。由于提供的控制点(81 个)比需要的最少数量(10 个)更多,因此不能保证所有控制点都与请求的坐标完全匹配。但是,对于这个特定示例,其中坐标接近预期的失真结果,它应该比较接近。多项式函数通常在图像边缘,特别是在图像角点处具有最大的误差。这不仅影响像素位置,还影响边缘处的采样区域(EWA)。这是所用近似方法的自然结果。可以使用更高阶的多项式,但在这种情况下,它不会产生任何重大改进。对于这个特定情况,多项式实际上试图将自身拟合到非多项式三角函数。由于这些函数的性质,第二次失真将比第一次失真更不准确。这个例子实际上与我们将在下面讨论的径向 桶形失真 方法非常接近。但是请注意,被映射的坐标不需要排列成网格,可以是任何一组坐标映射。因此,地理学家经常使用它来将航拍照片与地球物理地图对齐(并叠加),使用城镇、交叉路口、山峰和其他地标的已知位置作为控制点。
由于 多项式失真 通常不可逆,因此 IM 无法为给定源图像计算目标图像视窗的“最佳拟合”。因此,运算符的“+distort”形式不起作用,并且回退到正常的“-distort”操作。但是,您仍然可以使用 失真视窗 选项来定义目标图像的视窗。


圆形和径向失真方法

这些失真是指将径向向量作为失真过程的主要组成部分的失真。

弧形失真(将图像弯曲成圆形弧线)

Arc” 失真(截至 IM v6.3.5-5)是更复杂、 极坐标失真(见下文)的简单变体。默认情况下,它会将给定图像弯曲成一个完美的圆形弧线,弧线的角度由给定角度决定,并且在没有其他参数的情况下,它会尝试尽可能保留图像水平中心线的缩放比例以及图像的纵横比。为此,它最多需要四个参数。
arc_angle   rotate_angle   top_radius   bottom_radius
但是,只有“arc_angle”是必需的,其他参数是可选的,可以根据需要以给定的顺序添加。例如,在 60 度的角度范围内 “Arc” 图像…

  magick rose: -virtual-pixel White -distort Arc 60  arc_rose.jpg
[IM Output]
请注意,与其他图像失真运算符不同,“Arc” 失真总是会设置结果图像的大小,以便完整的源图像都存在。这包括任何抗锯齿边缘像素。因此,结果图像很少会与输入图像的大小匹配。

只有 视窗失真选项 才能让您更改特定失真的结果图像大小。
添加第二个参数 “rotate_agle” 允许您围绕圆圈旋转图像。例如,将其旋转 90 度。

  magick rose: -virtual-pixel White -distort Arc '60 90'  arc_rose_rot.jpg
[IM Output]
由于没有提到具体的半径参数,“Arc” 失真方法非常努力地尝试确保尽可能保留原始图像的比例。为此,图像的水平中心线设置为源图像的宽度和给定的“arc_angle”的“理想半径”。这意味着,如果您在更大的“arc_angle”范围内对图像进行弧形处理,所用的中心线半径也会以相同的系数缩小。因此,中心线的半径将更小、更紧密。

  magick rose: -virtual-pixel White -distort Arc 120  arc_rose_3.jpg
[IM Output]
请注意,图像现在将适合较小的圆圈,但图像的底部边缘仍然是更小的圆圈!如果您设置一个更大的角度来对图像进行弧形处理,底部边缘将与失真的中心线相撞,并超出中心线,这会导致源图像的下半部分消失在无形之中。

  magick rose: -virtual-pixel White -distort Arc 60   arc_rose_1.jpg
  magick rose: -virtual-pixel White -distort Arc 90   arc_rose_2.jpg
  magick rose: -virtual-pixel White -distort Arc 120  arc_rose_3.jpg
  magick rose: -virtual-pixel White -distort Arc 180  arc_rose_4.jpg
  magick rose: -virtual-pixel White -distort Arc 240  arc_rose_5.jpg
  magick rose: -virtual-pixel White -distort Arc 300  arc_rose_6.jpg
  magick rose: -virtual-pixel White -distort Arc 360  arc_rose_7.jpg
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output]

弧形变成完整的圆形环

更长的图像在非常大的角度范围内会“Arc” 失真得更好。例如,您可以将长图像(如文本消息)包裹成环。为了让您真正了解这里发生的事情,我设置了不同的 虚拟像素 背景色,这样您就可以看到原始图像的边界。

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 60     arc_circle_1.jpg
  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 120    arc_circle_2.jpg
  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 180    arc_circle_3.jpg
  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 270    arc_circle_4.jpg
  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 360    arc_circle_5.jpg
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]
瞧,我们已将标签图像“弧形处理”成一个完整的圆圈。如果您仔细观察完整圆圈图像的连接处,可能会看到一小行像素,其中连接处并不完全完整。这是由周围“SkyBlue虚拟像素 背景的影响造成的,因为我们实际上是在连接图像的两个边缘。在生成完整的圆圈时,您需要使用一种可以正确“连接”这两个边缘的虚拟像素方法。这通常是通过使用一种平铺 虚拟像素 方法来完成的,例如 Tile

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Tile -background SkyBlue \
          -distort Arc 360   arc_circle_tile.jpg
[IM Output]
不幸的是,正如您所看到的,这不仅将图像正确连接在一起,而且还在主环内和环外生成了图像的重复行。不好。截至 IM v6.4.2-6,一种新的 虚拟像素 方法,HorizontalTile,解决了这个问题。此方法仅水平平铺图像,因此它为我们圆形图像创建了一个良好的连接,但用当前背景色填充了平铺上方的区域和下方的区域,从而产生了一个完美的文本圆圈。

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc 360   arc_circle.jpg
[IM Output]
如果您在对图像进行“弧形处理”之前将输入图像颠倒过来,则可以将图像的原始“顶部”放在圆圈的内边缘。当然,您可能希望随后将结果“旋转”回正,但这种功能已经内置在“Arc” 失真方法中。

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background -background SkyBlue \
          -rotate 180 -distort Arc '270 180'  arc_flip.jpg
[IM Output]
第三个参数“top_radius” 将覆盖计算的“理想”中心线半径,以便图像的顶部成为给定半径的圆圈。这将创建一个宽度为 100 像素的环,尽管容纳图像的宽度为 102 像素,以便允许抗锯齿效果。

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc '360 0 50'  arc_radius.jpg
[IM Output]
图像仍然保持相同的纵横比,因此上面的图像本质上与之前相同,只是进行了缩放以适应请求的半径的圆圈。请记住,半径可以是浮点数,但弧线的中心总是对齐到像素“角点”,因此结果图像的宽度仍然是偶数像素。如果您提供第四个“bottom_radius”参数,则可以完全控制环的宽度或“径向高度”。

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc '360 0 45 30'   arc_inner.jpg
[IM Output]
这将扭曲图像的径向缩放比例,并有效地将径向缩放比例与结果图像的“弧线宽度”或角度分离。换句话说,原始图像的纵横比将不再保留。
您甚至可以强制它完全填充圆圈的内部,将输入图像的底部边缘包裹在失真中心的“极点”上。

  magick -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc '360 0 45 0'   arc_fill.jpg
[IM Output]

弧形扭曲示例

您可以使用 弧形失真 生成有趣的效果,例如将长方形棋盘图案弧形处理成环(使用 虚拟像素 设置“HorizontalTile” 会产生…

  magick -size 210x30 pattern:checkerboard -alpha set \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc 360   arc_checks.png
[IM Output]
通过使用默认的 虚拟像素 设置“Edge”,您可以产生更有趣的效果。

  magick -size 210x30 pattern:checkerboard  -virtual-pixel Edge \
          -distort Arc 360   arc_checks_edge.png
[IM Output]
当然,“Tile” 设置也会生成有趣的“径向”效果,使您可以生成圆形棋盘图案。

  magick -size 210x30 pattern:checkerboard  -virtual-pixel Tile \
          -distort Arc 360   arc_checks_tile.png
[IM Output]
通过控制结果图像的顶部和底部半径,可以进一步细化上述内容。以下是一些额外的“Arc” 失真示例,但我让您自己玩一下,找出它们的工作原理。您可以想出什么?

  magick -size 90x1 pattern:gray50 -scale 900x100 -normalize \
          -virtual-pixel Tile  -set option:distort:viewport 100x100-50-50 \
          -distort Arc 360  +repage  arc_radii.gif
[IM Output]

  magick -size 400x100 pattern:hs_diagcross \
          -virtual-pixel Tile  -set option:distort:viewport 100x100-50-50 \
          -distort Arc '360 0 80 0' +repage  arc_cross.gif
[IM Output]

  magick -size 360x80 xc: -draw "fill none stroke black line 0,5 360,80" \
          -virtual-pixel White  -distort Arc '360 0 50 0'  arc_spiral.gif
[IM Output]

  magick tree.gif -set option:distort:viewport 120x60-60-60 \
          -virtual-pixel Dither  +distort Arc '180 0 25 0' \
          +repage arc_rays.gif
[IM Output]
最后一个示例中的“光线”是伪随机“Dither虚拟像素 设置的副产品,导致来自原始图像左上角的“太阳”颜色的奇数像素模式。相同的抖动效果也会产生围绕“树木”图像的圆形“虚线”。您可以通过使用带有已修改以添加有趣边缘像素的图像的“Edge” 设置来实现类似且更受控的版本。

弧形中心点放置

默认情况下,“Arc” 将完全忽略图像可能具有的任何 虚拟画布 偏移量,甚至不会报告图像围绕其进行弧形处理的“中心”的位置。但是,了解“中心点”的位置非常有用。如果您不是使用“-distort”,而是使用特殊的加号形式“+distort”,则图像将被赋予一个 虚拟画布,以便中心位于虚拟画布的原点。换句话说,图像的“0,0”点被设置为弧形的“中心”。这对于将弧形图像定位到比完整圆圈更小的角度特别有用,其中弧形“中心”不是图像的中心。举个例子…

  magick logo: -resize x150 -gravity NorthEast -crop 100x100+10+0! \
          \( -background none label:'IM Examples' \
             -virtual-pixel Background +distort Arc '270 50 20' \
             -repage +75+21\! \)  -flatten  arc_overlay.jpg
[IM Output]
这里我创建了一个文本标签'Arc',并使用操作符的加号"+distort"形式将其扭曲成一个不完整的圆形。IM 使用图像的虚拟画布偏移量仔细地保留了弧的“中心”。这意味着,只需使用"-repage"带有“!”标志的相对调整偏移量,我们就可以将生成的文本圆形放置在我们想要的任何地方!例如,上面的示例中,位于像素坐标 75,21 的巫师帽的顶端。不幸的是,由于使用虚拟偏移量来定位图像,因此精确的定位仅限于整数像素大小。您无法将弧形扭曲定位到亚像素定义的位置,而不进行第二次扭曲。但是,您可以对极坐标扭曲执行此操作(见下文)。

极坐标扭曲(完整圆形扭曲)

Polar”扭曲(在 IM v6.4.2-6 中添加)是上面“Arc”扭曲的更低级版本。但它不会自动执行“最佳拟合”,也不会尝试保留图像的纵横比。6 个可选浮点数参数是...
Radius_Max Radius_Min Center_X,Center_Y Start_Angle,End_Angle
所有参数在间隔位置都是可选的。默认情况下,“CenterX,Y”将默认为输入图像区域的正中间。然后会生成一个完整的圆形极坐标图像,使得整个顶部边缘成为中心,而底部边缘则完全包裹在圆形的外部。左右边缘在中心点上方的“-180”到“+180”图像角度处相遇。由于必须给出“Radius_Max”,因此它应该是一些正值。  但是,如果您给出“0”的值,它将设置为中心与最近边缘之间的距离,因此,如果未给出其他值(默认值),则整个输入图像将映射到图像中间的圆形中。例如,让我们将世界地图变成极坐标视图,使用所有默认值。当然,在生成完整的圆形极坐标映射时,您应该指定虚拟像素设置为“HorizontalTile”...

  magick worldmap_sm.jpg -virtual-pixel HorizontalTile  \
          -background Black   -distort Polar 0   polar_arctic.jpg
[IM Output] ==> [IM Output]
当然,这会严重扭曲南半球,将南极洲完全包裹在“圆盘世界”的圆周上。
通过旋转源图像并裁剪它以仅显示极地帽,我们可以生成南极洲大陆的漂亮地图。我还指定了更大的输出半径,以使其更清晰,并要求 IM 使用扭曲操作符的加号形式将输出图像“拟合”到此大小。

  magick worldmap_md.jpg -rotate 180 -crop 100%x25%+0+0 +repage \
          -virtual-pixel HorizontalTile -background Black \
          +distort Polar 80 +repage  polar_antarctica.jpg
[IM Output]
请注意,以上并非地球的严格正确视图,因为笛卡尔地图是球体的表示,而不是极坐标中的图像。如果您使用特殊“Radius_Max”值“-1”,则扭曲图像的半径将设置为从中心到最远角(对角线)的距离。这样做是为了为我们接下来要介绍的完整图像“DePolar”扭曲提供理想的“反转”。(见下文(De)Polar Tricks,了解使用示例)。
请记住,与“Arc”扭曲不同,“Polar”(也称为“笛卡尔到极坐标”扭曲)不会尝试保留源图像的“理想”纵横比。请谨慎使用。
CenterX,Y”参数在将生成图像的中心定位在亚像素偏移处时最有用。也就是说,中心位于像素边界(整数)还是像素中心(偏移量为 0.5)。当然,它也决定了虚拟画布“层”的位置。但是,默认情况下,它被分配给图像的中间值(对于“-distort”,它使用输入图像作为视窗)或 0,0(对于“+distort”层图像)。下一个参数“Start_Angle,End_Angle”的使用频率更低,它限制了输入图像覆盖的角度,默认为 -180 到 180 度(0 为正下方)。与“Arc”扭曲一样,您可以使用它来旋转生成的极坐标图像。但它也可以用来生成“弧”。例如...

  magick worldmap_sm.jpg -virtual-pixel Black -background Black \
          +distort Polar  '60,20 0,0 -60,60' +repage  polar_arc.jpg
[IM Output] ==> [IM Output]
请注意,目前 IM 不会减小生成的层图像的大小,该图像已对齐,因此虚拟图像的原点位于坐标 0,0 处,如请求的那样。除了参数样式之外,这是“Arc”和“Polar”扭曲之间最大的区别。还要注意,左边缘(角度 -60)位于左侧。当您考虑到“Y”轴朝下(与所有图像旋转相同)时,这在数学上是正确的。
当然,与 Arc 一样,您可以使用虚拟像素平铺效果来生成重复模式。例如,这与最后一个示例完全相同,只是使用“HorizontalTileEdge”设置...

  magick worldmap_sm.jpg -virtual-pixel HorizontalTile -background Black \
          +distort Polar  '60,20 0,0 -60,60' +repage  polar_arc_tiled.jpg
[IM Output]

DePolar 扭曲(极坐标到笛卡尔坐标)

这本质上是“Polar”扭曲的逆运算,并且具有完全相同的可选参数集。6 个可选浮点数参数是...
Radius_Max   Radius_Min   Center_X,Center_Y   Start_Angle,End_Angle
同样,如果“Radius_Max”设置为“0”,则将使用“CenterX,Y”到最近边缘的距离,这意味着最大完整圆形中的任何内容都将被映射到与输入图像相同大小的图像中。例如,让我们将前面的“圆盘世界”反转回笛卡尔地图。

  magick polar_arctic.jpg  -distort DePolar 0  world_restored.jpg
[IM Output] ==> [IM Output]
由于输入图像的大小在两次扭曲过程中都已保留,因此上面的结果与原始地图基本相同。当然,由于图像在顶部“极点”和半径上都被压缩了,因此输出比您预期的模糊得多。
实际上,由于区域重采样算法 (EWA) 无法对圆弧进行采样,因此情况变得更糟。因此,对于“DePolar”扭曲,区域重采样 被关闭。建议改为使用某种形式的超级采样技术,如下节所示。
如果您允许 IM 使用“最佳拟合”(使用操作符的"+distort"形式),那么它将调整输出图像的大小,以保持“Radius_Max”为统一缩放,并将宽度设置为位于“Radius_Max”和“Radius_Min”中间半径的圆周距离。这本质上试图最大程度地保留极坐标图像的纵横比,尽管这可能会产生比预期更长更细的图像。例如。

  magick polar_arctic.jpg  +distort DePolar 0  world_restored_2.jpg
[IM Output] ==> [IM Output]

(De)Polar Cycle Tricks(径向/角度模糊)

如上所述,使用“Radius_Max”为“0”将确保在使用“Polar”(笛卡尔到极坐标)扭曲时,整个图像将被映射到圆形中,并且相同的设置将通过使用“DePolar”(极坐标到笛卡尔坐标)将该圆形映射回矩形图像。但是,如果您要“DePolar”一个矩形图像,然后使用“Polar”再次反转扭曲,则效果不会很好。例如,让我们取一张花朵图像,进行去极化,然后使用特殊的“Radius_Max”值为“0”(半径 = 最近边缘)恢复它。

  magick flower_sm.jpg -virtual-pixel Black \
          -distort DePolar 0  flower_depolar.jpg
  magick flower_depolar.jpg \
          -virtual-pixel HorizontalTile -background black \
          -distort  Polar  0  flower_circle.jpg
[IM Output] ==> [IM Output] ==> [IM Output]
现在,图像无法正常恢复,因为它被第一个“DePolar”扭曲所剪裁。即使如此,它本身也是一项有用的技术,并且可以用来为现有图像生成完美的圆形蒙版,其尺寸与给定的输入图像完全无关。要正确执行此“DePolar”-“Polar”循环技术,我们需要使用一个半径,该半径是从中心到最远角的距离。特殊的“Radius_Max”值为“-1”,将要求 IM 计算并使用距“中心点”最远的角作为半径。

  magick flower_sm.jpg  -virtual-pixel Black \
          -distort DePolar -1  flower_depolar-1.jpg
  magick flower_depolar-1.jpg \
          -virtual-pixel HorizontalTile -background black \
          -distort  Polar  -1  flower_restored.jpg
[IM Output] ==> [IM Output] ==> [IM Output]
恢复的图像略微模糊,这是由于在“DePolar”操作期间为了保留整个图像而对半径进行的压缩造成的。但是,这可以通过使用适当的超级采样技术来解决(见下一组示例)。但是,为什么您要将图像变成这种形式并再次恢复呢?好吧,通过在中间的“DePolar”版本的图像上应用其他扭曲,您可以非常轻松地生成一些非常奇特的径向或角度效果。例如,通过滚动中间图像,您将旋转输出图像,尽管您可能会遇到一些角部的剪裁...

  magick flower_sm.jpg -virtual-pixel Black -distort DePolar -1 \
          -roll +15+0 \
          -virtual-pixel HorizontalTile -background Black \
          -distort  Polar  -1  flower_polar_rotate.jpg
[IM Output]
请注意,旋转方向与旋转操作符SRT 扭曲相反。

Depolar-Polar 循环问题

在上面的图像旋转中,您可能已经注意到旋转图像边缘存在一些类似“阶梯”的扭曲。这是一个众所周知的问题,是由将图像的较大圆形周长压缩成输入图像的较小“宽度”造成的。例如,这里我取棋盘测试图像,只是将其通过正常的 Depolar-Polar 循环运行,没有任何更改。

  magick checks.png   -virtual-pixel Transparent \
          -distort DePolar -1   checks_depolar.png
  magick checks_depolar.png  -virtual-pixel HorizontalTile -background None \
          -distort  Polar  -1   checks_cycled.png
[IM Output] ==> [IM Output] ==> [IM Output]
您可以清楚地看到,在中间图像的点上,图像压缩导致的混叠效应。由于在初始的“Depolar”输入图像转换期间没有使用正常的区域重采样,因此它也加剧了这个问题。解决此问题的最佳方法是使用扭曲输出缩放来放大中间图像,然后缩小最终图像。这将提供超级采样结果,从而消除上面看到的压缩伪像。例如,这是更好的“无操作”去极化-极化循环,所有这些都在一条命令中...

  magick checks.png -virtual-pixel Background -background None \
          -set option:distort:scale 4  -distort DePolar -1 \
          -noop \
          -virtual-pixel HorizontalTile -background None \
          -set option:distort:scale .25 -distort  Polar  -1 \
          checks_cycled_ss.png
[IM Output]
如您所见,可怕的混叠效应几乎消失了。但是,请注意,非常高瘦的图像可能会使问题再次出现。最好的方法是将其限制为“横向”或宽图像,并使用超级采样,如上所示。现在,您只需将"-noop"操作符替换为您要生成的径向和旋转效果的适当命令。

示例 Depolar-Polar 效应

因此,让我们再次展示图像的更好的极坐标旋转,这次使用超级采样。但是,请注意,由于中间图像放大 4 倍,因此图像滚动的数量也需要放大 4 倍。

  magick flower_sm.jpg   -virtual-pixel Black \
          -set option:distort:scale 4   -distort DePolar -1 \
          -roll +60+0   \
          -virtual-pixel HorizontalTile -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_polar_rotate_ss.jpg
[IM Output]
如您所见,边缘的“阶梯”效应已消除,图像质量更高。
或者,您可以对中间图像应用简单的线性模糊(通过再次挤压和放大图像来实现)。

  magick flower_sm.jpg -virtual-pixel Black \
          -set option:distort:scale 4   -distort DePolar -1 \
          -scale 10%x100%\! -filter Gaussian -resize 1000%x100%\! +filter \
          -virtual-pixel HorizontalTile -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_angular_blur.jpg
[IM Output]
结果非常类似于图像的“旋转模糊”。这与误称的径向模糊操作符类似,但并不完全相同。实际上,结果比该专用模糊方法的质量更高。请注意,在应用的各种形式的虚拟像素设置中使用“black”颜色会导致边缘略微变暗,但在上面的情况下并不太糟糕。一种消除“黑色”边缘效果的方法是使用“transparency”颜色,然后在完成后完全关闭 alpha 通道,以便仅保留 IM 计算的实际颜色。另一种方法是使用两种“边缘”虚拟像素方法(“Edge”和“HorizontalTileEdge”),这将扩展图像的边缘进入未定义的虚拟画布空间。

  magick flower_sm.jpg -virtual-pixel Edge \
          -set option:distort:scale 4   -distort DePolar -1 \
          -scale 10%x100%\! -filter Gaussian -resize 1000%x100%\! +filter \
          -virtual-pixel HorizontalTileEdge -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_angular_blur_edge.jpg
[IM Output]
这显示了边缘附近更好的结果。
通过垂直模糊图像的极坐标版本,这次使用 运动模糊算子,而不是调整大小压缩,可以生成从图像中心向外移动的 **径向条纹**...

  magick flower_sm.jpg   -virtual-pixel Black \
          -set option:distort:scale 4   -distort DePolar -1 \
          -virtual-pixel Edge   -motion-blur 0x28-90 \
          -virtual-pixel HorizontalTile -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_radial_blur.jpg
[IM Output]
为了使结果仅模糊图像的亮部(花瓣),可以使用 Lighten 将其与原始图像合成,这样只有模糊的亮色仍然可见,暗色不会模糊到亮部,并破坏花朵中间的黄色斑点。

  magick flower_sm.jpg  flower_radial_blur.jpg \
          -compose Lighten -composite   flower_radial_blur_lighten.jpg
[IM Output]
另请参见 星星和彗星,它提供了另一个示例,但直接生成了中间的“DePolar”图像,然后再应用“Polar”畸变。特别感谢 Fred Weinhaus 对 DePolar-Polar 循环的特殊用途,并坚持确保这些畸变对于矩形图像完全可逆。他在许多 ImageMagick 脚本 中有效地使用了这种技术,包括 “bump”、“ripples” 和 “striations”。

桶形畸变(校正镜头畸变)

桶形畸变(添加到 IM v6.4.2-4 中)专门用于校正相机镜头在照片中引起的球面畸变。即桶形和枕形效应等畸变,它们实际上是彼此的相反。有关将此算子用于镜头畸变校正的实际应用,请参见部分 镜头校正。畸变是根据一组 4 个系数值实现的,这些系数值称为 ABCD,由 Helmut Dersch 教授 在一个现已消失的网站上定义。您可以在 Wayback Machine 存档中看到该网站的存档,网址为 校正桶形畸变。这些值基本上形成了一个畸变方程,使得...
Rsrc = r * ( A*r3 + B*r2 + C*r + D )
其中 “r” 是目标半径,“Rsrc” 是获取像素颜色的源像素。半径已归一化,因此对于输入图像的最小宽度或高度的一半,半径 = '1.0'。这似乎颠倒了,但这是因为 反向像素映射 技术用于确保对结果图像的完全覆盖。所有四个系数(ABCD)对于任何特定的相机、镜头和变焦组合都是固定的。所有三个通常与图像一起存储在 EXIF 配置文件 中。这一点很重要,因为它意味着一旦您有了相机的这些值,就可以使用它们来消除该相机和镜头组合拍摄的所有照片中存在的球面镜头畸变。'Barrel' 畸变方法所需的论据。通常您只提供 3 或 4 个值...
A   B   C   [ D   [ X , Y ] ]
可选的 X,Y 参数提供径向畸变的可选“中心”,否则默认为给定图像的精确中心(无论其虚拟偏移如何)。这些系数的设计使如果所有四个 AD 值加起来为 '1.0',则图像的最小宽度/高度将不会改变。由于这个原因,如果 D(控制图像的整体缩放)未提供,它将被设置为所有四个值加起来为 '1.0'。使用参数 '0.0 0.0 0.0'(相当于 A=B=C=0.0D=1.0')不会对输入图像产生任何改变,并且是这种畸变的“无操作”参数。以下是从原始网站上的一个示例,使用为拍摄照片的相机提供的系数。

  magick barrel_distorted.jpg -virtual-pixel black \
          -distort Barrel "0.0 0.0 -0.075 1.1" \
          barrel_distorted_fixed.jpg
[IM Output] ==> [IM Output]
请注意图像中的畸变是如何被校正的,使建筑物的支柱变直。但是,由于 4 个系数加起来的值大于 1.0,因此图像缩小了一点,从而在中间顶部和底部边缘产生了小的黑色区域(根据给定的 虚拟像素设置)。以下是在向每个输入系数添加 0.2 后的效果,同样,这些值加起来大于 1.0,因此生成的畸变图像会更小。

  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.2 0.0 0.0 1.0"   barrel_checks_A.png
  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.2 0.0 1.0"   barrel_checks_B.png
  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 0.2 1.0"   barrel_checks_C.png
  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 0.0 1.2"   barrel_checks_D.png
[IM Output] [IM Output] [IM Output] [IM Output]
减去 0.2 会产生相反的效果,尽管我使用更大的 'D' 值(缩小图像)来抵消效果,以便您更好地看到结果。

  magick checks.png -virtual-pixel gray \
          -distort Barrel "-0.2 0.0 0.0 1.3"   barrel_checks-A.png
  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.0 -0.2 0.0 1.3"   barrel_checks-B.png
  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 -0.2 1.3"   barrel_checks-C.png
  magick checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 0.0 1.3"    barrel_checks-D.png
[IM Output] [IM Output] [IM Output] [IM Output]
请注意 A 的值比 B 的值产生的影响更大,B 的值比 C 的值产生的影响更大,而 D 提供结果的整体缩放。这允许您使用每个系数来调整图像,以便您可以校正外边缘周围的一种畸变和中间的另一种畸变,无论是枕形还是桶形。非常通用。上述系数(ABCD)旨在与图像最小宽度或高度的一半的“归一化”半径一起使用(如 极坐标畸变 的“0”半径设置)。也就是说,它们与图像大小无关。因此,您可以对特定相机生成的任何图像使用相同的系数集,无论其质量大小(相机设置)如何,或者您是否将图像调整为更小的尺寸。您可以通过对每个系数使用适当的乘数/除数来调整系数值,以使用其他“归一化”半径值。例如,使用最大宽度/高度的一半,或使用对角线半径。
Helmut Dersch 还指出,您应该考虑对照片使用 LAB 色彩空间进行畸变校正,因为它可以产生更好的颜色插值。这实际上可能适用于所有畸变(包括 调整大小)。

测试表明,LAB 空间与 sRGB 一样非线性,但确实避免了在极值被裁剪时发生颜色畸变的可能性。请参见 人类颜色感知,以及 使用色彩空间校正调整大小 中的实际示例。

您还可以为 x 轴和 y 轴声明不同的系数集,允许您生成一些不寻常的畸变。
Ax Bx Cx Dx   Ay By Cy Dy   [ X , Y ]
使用单独的 X 和 Y 参数是 Fred Weinhaus 的 pinbarrel 脚本中的原型,尽管他的参数顺序相反,D 位于首位,A 位于末尾。通过使用正 C 值,以及仅针对 'y' 系数集的适当 D 值,您可以使图像变形,使其在中间垂直凸出。

  magick rose: -alpha set -virtual-pixel transparent \
          -distort Barrel "0.0 0.0 0.0 1.0   0.0 0.0 0.5 0.5" \
          barrel_bulge.png
[IM Output]
类似地,使用负 C 值,您可以使图像在中间“收缩”。

  magick rose: -alpha set -virtual-pixel transparent \
          -distort Barrel "0.0 0.0 0.0 1.0   0.0 0.0 -0.5 1.9" \
          barrel_pinch.png
[IM Output]
或者通过为 X 系数添加相反的效果,使其看起来像您用手指挤压图像,使其两侧鼓起来。

  magick rose: -alpha set -virtual-pixel transparent \
          -distort Barrel "0.0 0.0 0.5 0.5   0.0 0.0 -0.5 1.9" \
          barrel_pinch_2.png
[IM Output]

桶形反向畸变(另一种桶形畸变)

'BarrelInverse' 畸变方法与之前的 桶形畸变 畸变方法非常相似,实际上接受相同的一组参数。但是,应用的公式略有不同,方程的主要部分是对半径进行除法。也就是说,该方程已被反转。
Rsrc = r / ( A*r3 + B*r2 + C*r + D )
此方程不会产生 'Barrel' 畸变的“反转”。您无法使用它来“撤消”之前的畸变。
结果是,您将使用 ABC 的“负”形式,并对 D 进行等效调整,以获得类似但略有不同的结果。一些来源(如研究论文 校正镜头畸变的方法 (PDF))建议使用这种形式的镜头校正畸变可以获得更好的结果。例如,以下是使用这种形式的畸变的最后一个“收缩”示例的等效项。

  magick rose: -alpha set -virtual-pixel transparent \
          -distort BarrelInverse "0.0 0.0 -0.5 1.5   0.0 0.0 0.3 0.5" \
          barrel_inv_pinch.png
[IM Output]


投影畸变

这些畸变用于将存在于一个表面上的图像投影或映射到另一个表面上。投影线可以是平行的,也可以从某个特定位置辐射出来。从技术上讲,仿射透视畸变 也是“投影”的,因为它们将一个平面上的图像“投影”到另一个平面上的图像(分别使用平行和径向投影),但它们非常常见,因此在上面的示例区域中会更详细地进行讨论。以下是一些已实现的更不寻常的“投影畸变”,通常是在 IM 讨论论坛 的帮助下。

圆柱体到平面

'Cylinder2Plane' 畸变是从圆柱体中心的点到与该圆柱体相切的平面的径向投影畸变。
[diagram]
这种布置是特殊针孔相机(称为 P.90 相机)的典型布置,其中在形成相机中圆柱体的胶片上捕获 90 度弧的照片。以下是从这种相机拍摄的示例照片...
[photo]
问题是,由于胶片的物理布置,生成的图像会发生畸变,导致直线变成弯曲的弧线。本质上是一个弯曲的表面包裹在一个“点”投影源(针孔)周围。请注意,普通的针孔相机没有这个问题,因为您是从点源将图像投影到平面上。'Cylinder2Plane' 畸变通过将图像从其圆柱形排列投影到平面上来解决这个问题。它接受以下参数...
fov_angle   center_x,y   fov_output   dest_center_x,y
只需要第一个参数,即相机的视野角度。对于 P.90 相机,它使用 90 毫米(径向)焦距和标准 57 毫米宽胶片,这反过来会产生 90 / 57 * 180/pi 或 90.467 度的“视野”。例如,在这里,我将 P90 照片投影到平面上,使图像看起来更“正常”,并使直线再次变直。

  magick p90_orig.jpg -virtual-pixel Gray \
          +distort Cylinder2Plane 90.467  p90_plane.png
[IM Output]
请注意,图像的宽度和高度都已更改,因为我们使用的是畸变的“加号”版本,以便显示原始图像中的所有畸变像素。由于投影,它更宽,而实际图像沿垂直中心线的的高度没有改变。照片和算法来自 IM 讨论 校正弯曲胶片平面。讨论也延伸到关于 针孔相机算法渐晕校正 的另一个讨论中。
如果给出了特殊的 'fov_output',它将缩放生成的输出图像,使输出图像的宽度(通常是视口大小)完全匹配此角度。如果没有给出视口,则将启用最佳匹配,以最佳地近似图像的 1:1 缩放,同时仍将图像边缘对齐到整数。畸变的中心点参数(输入的切线和地平线点)。最后一个“中心”参数控制结果在视口图像“图层”中的精确浮点定位(即亚像素平移)。这与 'center_x,y' 参数允许您从更大的图像中提取部分。例如,从更大的 360 度全景图像中提取小的 90 度视图。未来:从 360 度全景图中提取更小的平坦“查看”图像,以及围绕 360 度缓慢平移的动画。

平面到圆柱体

'Plane2Cylinder' 畸变是上述投影的反向,并接受以下参数...
fov_angle   center_x,y
例如,这会撤消之前的 P.90 相机示例。

  magick p90_plane.png -virtual-pixel Black \
          +distort Plane2Cylinder 90.467  p90_restored.png
[IM Output]
结果中仍然包含之前添加的额外像素,并且还会添加更多像素。这些像素应该从以上结果中裁剪掉。在这里,我使用这种失真来生成一个带有胶片齿轮边缘孔的“胶片带”动画。

  magick -size 12x12 xc: -draw 'circle 6,6 6,2' -negate \
          -duplicate 5 +append +duplicate \
          rose: +swap -background black -append \
          -duplicate 3 +append \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -duplicate 19  -distort SRT '%[fx:72*t/n],0 1 0 0,0' \
          -distort Plane2cylinder 115 \
          -bordercolor Skyblue -border 0x3 -set delay 5 \
          film_strip_anim.gif
[IM Output]
解释
  • 首先绘制一个孔图像,然后复制并追加该图像以制作一个包含 6 个孔的序列。然后再次复制。
  • 然后添加内置的玫瑰图像,并将其夹在这些齿轮孔的两个副本之间,并将它们(用黑色填充)一起追加,以创建一个最终胶片带的单个帧。
  • 复制此帧以创建一个包含 4 帧的胶片带,这将定义最终图像的长度。
  • 使用 SRT “平移”失真创建 20 帧动画。参见 失真动画.
  • 然后将这 20 帧中的每一帧扭曲到一个圆柱体上,跨越 115 度的弧度,使用虚拟像素设置来生成胶片带的无限水平平铺。
  • 在这种情况下,角度仅针对输入图像的宽度,最终图像的宽度超过 180 度,因为我没有使用失真运算符的“plus”版本。因此,虽然扭曲后的宽度在宽度方向上缩小,但输出图像却没有缩小。
  • 最后添加一个边框,并应用动画设置。


多点和自由形式失真

谢泼德失真(类似太妃糖的失真)

谢泼德方法(添加到 IM v6.4.2-4 中)使用给定控制点的移动来失真图像,以“局部”效果的形式。您可以将其视为等效于代表源图像的厚厚的“太妃糖”块,在其中插入了销钉,然后将销钉移动。更准确地说,它根据 反距离加权插值 移动点。如果只使用一个控制点,自然整个图像都会移动(平移),就像您在一次点“仿射”失真中得到的一样。这没什么意思。所以让我们尝试移动两个控制点。例如,让我们通过拉动“考拉”的耳朵(位于“30,11”和“48,29”处)来折磨它……

  magick koala.gif -virtual-pixel Black \
          -distort Shepards '30,11 20,11  48,29 58,29' \
          koala_ear_pull.png
[IM Output] ==> [IM Output]
如您所见,由于控制点的移动,图像中两个控制点之间的部分被拉伸了。但是图像的其余部分基本保持不变,包括靠近控制点本身的图像、图像底部等等。位于控制点中间的区域被拉伸和拉伸以确保控制点被放置在您要求的位置。可能不太明显的是,控制点远端的部位也会被压缩,因此随着距离的增加,控制点对结果的影响会减小。也就是说,这种失真会生成“局部”失真。
让我们扩展我们的视图(使用 失真视口)以便我们可以更好地看到这一点……

  magick koala.gif -virtual-pixel Black \
          -set option:distort:viewport 115x115-20-20 \
          -distort Shepards '30,11 15,11  48,29 58,29' \
          +repage koala_ear_pull_2.png
[IM Output]
如您所见,图像的形状也发生了扭曲以适应考拉的拉伸“头部”。
为了避免这种效果,更典型的是还要“固定”图像的角点和可能的一些边缘,以使它们不移动。

  magick koala.gif -virtual-pixel Black \
          -set option:distort:viewport 115x115-20-20 \
          -distort Shepards '30,11 15,11  48,29 58,29
              0,0 0,0  0,74 0,74   74,0 74,0  74,74 74,74' \
          +repage koala_ear_pull_3.png
[IM Output]
即使只移动一个点,同时固定其他点(在本例中只是角点)也是有用的。例如,让我们只将考拉的鼻子(位于“28,24”处)移动到图像的中间。

  magick koala.gif -virtual-pixel Black \
          -distort Shepards '28,24 37,37
              0,0 0,0  0,74 0,74   74,0 74,0  74,74 74,74' \
          +repage koala_move_nose.png
[IM Output]
这个具体示例很特别,因为它是由 Fred Weinhaus 用于他的单点“动画变形”脚本“shapemorph”的失真。但是,他的原始脚本使用了一个缓慢的 DIY FX 运算符,因为 谢泼德失真 尚未添加到 IM 中。这个脚本实际上是 谢泼德失真 的原始想法的来源。

移动图像区域

您甚至可以通过将围绕该部分的一组点一起移动来移动图像的整个部分。例如,让我们通过使用围绕头部(红线)的点来将考拉的头部侧移,但同时固定我们不想移动的图像部分(绿线)。

  magick koala.gif -virtual-pixel Black -distort Shepards \
            '19,8, 29,8   19,27 29,27   26,34 36,34
                 33,37 43,37   36,37 46,37   53,37 63,37   58,25 68,25
             13,20 13,20  17,28 17,28  25,36 25,36
                 35,39 35,39   46,40 46,40   50,43 50,43 ' \
          +repage koala_head_move.png
[IM Output] ==> [IM Output]
请注意,虽然头部移动了,但头部的边缘确实发生了严重的扭曲。原因是失真不移动区域,而是移动点。如果这些边缘标记点距离太远,那么图像就会像太妃糖或果冻一样在这些点之间滴落、泄漏或弯曲。(旁注:实际术语是拉伸“橡胶板”或“气球”。)此外,如果两个控制点彼此靠近,但以不同的方向或量移动,图像可能会在它们周围局部旋转和弯曲。控制点最终仍然位于正确的位置,但周围的所有其他内容都严重扭曲以实现该目标。这就是头部边缘发生的事情。那么边缘标记点应该有多近呢?基本上至少是到任何其他以不同方式移动的点的距离的一半。因此,要么添加更多边缘点,要么在固定点和移动点之间增加一些额外距离。通过这样做,您将更好地定义图像可以拉伸和压缩的空间。另请注意,整个图像总体上也与头部一起向左移动。只有被固定或移动到特定目的地的控制点才能保证被放置在正确的位置。图像中远离任何控制点的任何部分也会根据所有控制点移动的粗略平均值移动。因此,最好有更多“固定”点,分布在整个图像中,甚至在图像外部的一些距离之外有一些负向移动点,以抵消整体平均移动。您还可以复制或重复控制点(列出两次)以使特定点对该区域的失真具有更大的影响或“力量”。以下是“将头部侧移”的另一个版本,但是这次我在移动(红色)和固定(绿色)点之间增加了一些额外间距。我还添加了更多固定点以减少失真图像的整体平均移动。

  magick koala.gif -virtual-pixel Black -distort Shepards \
            '15,15, 25,15   19,27 29,27   26,34 36,34
                33,37 43,37   36,37 46,37    53,37 63,37
             10,2 10,2   2,10 2,10   4,55 4,55   14,47 14,47
                25,47 25,47 45,51 45,51   55,45 55,45
                5,70 5,70  15,60 15,60   55,60 55,60   70,70 70,70' \
          +repage koala_head_move_2.png
[IM Output] ==> [IM Output]
上述最后要做的事情就是简单地设置一个更好的“-virtual-pixel”设置来设置上述未定义黑色区域应该是什么颜色。

谢泼德和图像旋转

这种失真的一方面是它不喜欢任何形式的旋转!例如,以下是 透视双线性前向 的重复,以及 谢泼德失真


magick mandrill_grid.jpg -alpha set -virtual-pixel black \ -distort Perspective \ '0,0 26,0 128,0 114,23 128,128 128,100 0,128 0,123' \ mandrill_pers.jpg
magick mandrill_grid.jpg -alpha set -virtual-pixel black -interpolate Spline \ -distort BilinearForward \ '0,0 26,0 128,0 114,23 128,128 128,100 0,128 0,123' \ mandrill_blin.jpg magick mandrill_grid.jpg -alpha set -virtual-pixel black -interpolate Spline \ -distort Shepards \ '0,0 26,0 128,0 114,23 128,128 128,100 0,128 0,123' \ mandrill_shep.jpg
[IM Output]
原始图像
==> [IM Output]
透视
[IM Output]
双线性
[IM Output]
谢泼德
注意 谢泼德失真 与其他两种失真方法相比,生成了一个非常弯曲的图像。这是因为它试图在靠近给定控制点的区域精确地保留图像。这包括图像的旋转。由于这种“保留”,网格会弯曲,使其在实际控制点处保持“正交”。这有点像控制点的“销钉”实际上不是圆形的销钉,而是“十字形”,迫使“果冻”或“橡胶板”保持图像的旋转。这也是这种失真可能产生的许多“漩涡”效果的来源。例如,如果我们在图像中取两个点并将它们彼此推开。图像会旋转,而不是旋转。例如,让我们尝试将考拉的耳朵向彼此推,而不是分开。

  magick koala.gif -virtual-pixel Black \
          -distort Shepards '30,11 40,11  48,29 38,29' \
          koala_ear_push.png
[IM Output] ==> [IM Output]

谢泼德功率因子

通常,谢泼德 IWD(反加权距离)的距离权重遵循平方反比定律(1/r2),但是从 IM v6.8.0-10 开始,您现在可以使用专家 defineshepards:power”来控制全局权重的“功率级别”。如果未定义,其值为 2.0,但通过将其定义得更小,您可以从失真图像的整体平均位移中生成更多围绕移动控制点的局部失真。使用更大的值会生成围绕控制点的更大影响区域。例如,以下是“拉动考拉耳朵”示例的重复,应用了不同的功率级别到失真权重。

  magick koala.gif -virtual-pixel Black -define shepards:power=0.5 \
          -distort Shepards '30,11 20,11  48,29 58,29' \
          koala_ear_pull_pow0.5.png
  magick koala.gif -virtual-pixel Black -define shepards:power=1.0 \
          -distort Shepards '30,11 20,11  48,29 58,29' \
          koala_ear_pull_pow1.png

magick koala.gif -virtual-pixel Black \ -distort Shepards '30,11 20,11 48,29 58,29' \ koala_ear_pull.png magick koala.gif -virtual-pixel Black -define shepards:power=3.0 \ -distort Shepards '30,11 20,11 48,29 58,29' \ koala_ear_pull_pow3.png magick koala.gif -virtual-pixel Black -define shepards:power=8.0 \ -distort Shepards '30,11 20,11 48,29 58,29' \ koala_ear_pull_pow8.png
[IM Output]
原始图像
& action
==> [IM Output]
power 0.5
[IM Output]
power 1.0
[IM Output]
power 2.0
(默认)
[IM Output]
power 3.0
[IM Output]
power 8.0
以上所有图像结果都使用完全相同的控制点移动集。唯一的区别是围绕这些控制点的影响区域。较小的功率会将“移动局部化”到仅靠近控制点的区域,而更大的功率会将更多图像拖动到控制点周围。在非常大的功率下,这种拉力往往会将图像沿着控制点之间中线的线撕裂成不同的区域。您甚至可以使用更大的功率,这只会将围绕源控制点的区域平移到围绕目标控制点的区域。这些区域将形成“沃罗诺伊区域”模式,并且可以包含源图像的副本。例如,在这里我将围绕考拉鼻子(坐标 28,24)的区域映射到 7 个不同的区域,以六边形模式排列,以非常高效的方式产生“昆虫眼”效果。

  magick koala.gif -virtual-pixel Black -define shepards:power=25 \
          -distort Shepards '28,24 35,35 \
                      28,24 20,10   28,24 50,10 \
                      28,24 20,60   28,24 50,60 \
                      28,24 10,35   28,24 60,35'  koala_hexagonal.png
[IM Output]
请记住 谢泼德失真 实际上等效于使用与 谢泼德,稀疏颜色运算符 相同的技术生成的位移图(将目标像素映射到源图像),这也受此相同功率因子的定义影响。

总之,谢泼德失真

谢泼德失真 是一种非常通用且自由形式的方法,将它的失真限制在由给定点的移动或不移动标记的区域。它的失真根据相邻控制点之间的距离被局部化和限制,尽管所有点仍然具有平均的全局影响。请记住,这种失真是由点驱动的,而不是由线或区域驱动的,因此当以不同方式移动的控制点彼此靠近时,点之间的部分可能会意外地凸出或旋转。它会旋转、拉伸和压缩控制点之间的图像,但它会尽力在控制点附近不旋转或缩放图像。最后,它可能会在远离任何控制点的图像上产生整体平均平移。但是,如果可以移动控制点块,同时保留它们的一般相对位置,它确实提供了一种实现一般且非常简单的点驱动“图像变形”技术的方法。Fred Weinhaus 的脚本 'shapemorph2' 使用 谢泼德失真 来提供一个通用的“动画图像变形”程序。
在内部,这种失真等效于使用 谢泼德稀疏颜色 梯度生成器来创建两个 相对位移图 来失真图像。这就是 Fred Weinhaus 的原始“shapemorph”脚本所做的,也是这种失真技术来源。
由于使用Shepards 扭曲所需计算的复杂性,IM 不会提供任何形式的“最佳拟合”目标视窗,使用加号“+distort”操作符形式。但是,您仍然可以使用扭曲视窗选项来定义更大的输出图像。
出于同样的原因,区域重采样被关闭。因此,极端压缩区域(超过 2 倍)可能会出现一些混叠现象。例如,请参阅最后一个示例中六边形图案的边缘。但是,可以使用超采样来提高最终图像质量,并减少此类混叠现象。