ImageMagick 示例 --
扭曲图像
- 索引
-
ImageMagick 示例前言和索引
-
简单图像扭曲 (重新排列像素顺序)
-
旋转和剪切
-
圆形扭曲
-
动画 (有趣的例子)
简单图像扭曲
简单图像扭曲运算符只是重新排列图像中的像素。像素数量甚至图像大小都保持不变。关键特征是图像不会丢失任何信息,它只是重新排列,并且可以很容易地恢复到正常状态,而不会有任何质量损失(不包括保存时类似 JPEG 的有损压缩)。基本上,它只是重新排列像素,而不会破坏、覆盖、复制或颜色合并或以其他方式修改原始图像的内容。只是在图像中移动像素位置。![[IM 输出]](../images/koala.gif)
翻转和镜像
对于这些例子,让我们使用这个看起来很可爱的考拉图像...最简单的图像失真是重新排列图像中的像素,以便"-flip "它上下颠倒。
|
![]() |
或者通过使用"-flop ",您可以生成镜像图像。
|
![]() |
![]() ![]() |
在 IM v6.6.6-5 之前,"-flip " 和 "-flop " 运算符都不会修改图像的虚拟画布偏移,相对于可能存在的更大的虚拟画布。 |
转置和横置,对角线
"-transpose
" 和 "-transverse
" 图像操作会生成图像的对角线镜像。"-transpose " 沿图像左上角到右下角的对角线镜像图像。
|
![]() |
而 "-transverse " 沿图像左下角到右上角的对角线镜像图像。
|
![]() |
![]() ![]() |
在 IM v6.6.6-5 之前,"-transpose " 和 "-transverse " 运算符都不会修改图像的虚拟画布偏移,相对于可能存在的更大的虚拟画布。 |
矩形旋转
以上显示的所有四种类型的操作,本质上都会生成原始图像的镜像。"-rotate
" 运算符提供图像的其他非镜像版本,包括原始图像本身。
magick koala.gif -rotate 0 rotate_0.gif magick koala.gif -rotate 90 rotate_90.gif magick koala.gif -rotate 180 rotate_180.gif magick koala.gif -rotate -90 rotate-90.gif magick koala.gif -rotate 360 rotate_360.gif |
![[IM Output]](rotate_0.gif)
![[IM Output]](rotate_90.gif)
![[IM Output]](rotate_180.gif)
![[IM Output]](rotate-90.gif)
![[IM Output]](rotate_360.gif)
-rotate
" 仅在您使用 90 度倍数的旋转角度时才是简单的扭曲。任何其他角度都会在图像中引入其他更复杂的像素级扭曲。参见下面的 旋转。![]() ![]() |
您可能会注意到,正旋转角度是顺时针方向,这在逻辑上似乎不正确。然而,在内部,这是数学上正确的,并且是由使用否定 Y 轴造成的。也就是说,Y 轴从顶部开始,正向下方。由于坐标系是反向的,因此旋转角度在数学上也是反向的。 |
![]() ![]() |
数字照片也可以使用 "-auto-orient " 运算符旋转以匹配记录的 相机方向。这是在 IM v6.2.7-8 中添加的。 |
滚动图像,就像一台坏电视
您还可以"-roll " 水平滚动图像(就像一台不同步的电视)。滚动量(图像的位移)以像素为单位给出。
|
![]() |
当然,您也可以横向滚动图像...
|
![]() |
或者通过使用负像素数,您可以将其向相反方向滚动。
|
![]() |
-tile-offset
" 设置定义的内容,即在 "-tile
" 选项读取平铺图像时要应用的滚动量。 简单图像扭曲总结
所有这些运算符中最重要的方面是,您可以将它们以多种不同的方式组合在一起,使得结果与根本没有执行任何操作完全相同。
|
![]() |
旋转和剪切
虽然 简单扭曲运算符(以上)保留了图像的大小和颜色,但下一组则没有。这些运算符的结果不适合原始大小,甚至不适合图像的原始光栅网格。旋转图像 --简单图像旋转
如您在上面看到的,"-rotate
" 运算符可以执行简单的图像保留扭曲,当您以 90 度为单位旋转图像时。但是,使用其他角度,旋转后的图像将无法很好地拟合到矩形图像中。因此,为了确保没有图像数据丢失,最终图像的大小会相应放大,以容纳旋转后的图像。
|
![]() |
ImageMagick 添加的额外空间将使用当前的 "-background " 颜色设置进行着色。允许您指定用于填充角的颜色。
|
![]() |
当然,如果您想用透明色填充,您需要确保图像可以处理透明度(通过启用添加 Alpha 通道),并保存到可以处理透明度的图像格式。
|
![]() |
![]() ![]() |
在 6.1.2 版本之前,"-rotate " 未能正确处理透明度,在旋转后的图像角部产生黑色和透明条纹。解决此问题的变通方法相当复杂,涉及分别旋转 alpha 通道和颜色。 |
-crop
" 将图像恢复到原始大小。如果您不知道原始大小是多少,您可以使用 alpha 合成技巧(参见“Src
”合成方法)将图像恢复到原始大小。
|
![]() |
-rotate
" 运算符还理解两个额外的标志。如果在旋转参数(在数字之前或之后)中添加了“>
”符号,那么只有当图像的宽度大于高度时,才会旋转图像。也就是说,"90>
" 只会将“横向”(宽)样式图像旋转成“纵向”(高)样式图像,以使所有图像都为“纵向”样式。另一个标志“<
”则相反,只旋转高度大于宽度的图像。例如,"90<
" 将确保所有图像都为“横向”。此标志的另一个用途是按不同的量旋转“纵向”和“横向”图像。也就是说,您可以给出两个不同的 "-rotate
" 操作,使您将“纵向”图像旋转一个方向,并将“横向”图像旋转另一个方向。数字照片也可以使用 "-auto-orient
" 运算符旋转以匹配 相机方向(基于图像的 EXIF 元数据)。但是请记住,重新保存到 JPEG 格式可能不是一个好主意。 旋转运算符内部
从 IMv7.7.3-4 开始,旋转运算符 现在使用 扭曲运算符 和 缩放-旋转-平移(SRT)扭曲。以下是使用底层 SRT 扭曲 的更直接的旋转。
|
![]() |
或者使用扭曲运算符的“+”版本来调整画布大小。
|
![]() |
剪切图像 --线性位移
"-shear
" 运算符获取像素的每一行(或列),并沿其滑动,使得每一行(或列)相对于相邻行(或列)位移相同。它的两个参数以角度形式给出。与 "-rotate
" 一样,该操作会增加结果图像的大小,以确保不丢失任何信息。但是剪切更复杂,因为它实际上是一个双重操作。
magick koala.gif -background Blue -shear 20 shear_rot.gif magick koala.gif -background Blue -shear 20x0 shear_x.gif magick koala.gif -background Blue -shear 0x50 shear_y.gif magick koala.gif -background Blue -shear 20x50 shear_xy.gif magick koala.gif -background Blue -shear 20x0 -shear 0x50 shear_xy2.gif magick koala.gif -background Blue -shear 0x50 -shear 20x0 shear_yx.gif |
![[IM Output]](shear_rot.gif)
![[IM Output]](shear_x.gif)
![[IM Output]](shear_y.gif)
![[IM Output]](shear_xy.gif)
![[IM Output]](shear_xy2.gif)
![[IM Output]](shear_yx.gif)
-shear
"(第四张图片)实际上等同于先进行 X 剪切,然后进行 Y 剪切(并对图像进行适当的裁剪),如第五张或倒数第二张图片所示。请注意,剪切的顺序会产生不同的结果。如果只提供一个数字(在参数中没有任何 'x
',如第一张图片),那么 "-shear
" 将在 X 和 Y 方向上同时应用它,作为一种简陋的旋转方式。"-background " 颜色设置当然用作添加的额外空间的颜色。
|
![]() |
![]() ![]() |
在 IM 版本 6.1.2 之前,"-shear " 不处理透明度。解决此问题的方法相当复杂,涉及分别对 alpha 通道和颜色进行剪切。 |
-shear
" 不是旋转图像的正确方法。要真正使用剪切来正确旋转图像,您需要执行多个剪切操作,形式为 "-shear {X}x{Y} -shear {X}x0 -crop ...
",但是计算 '{X}
'、'{Y}
' 和最终裁剪的正确值需要一些三角学。"旋转运算符" 实际上以前就是以这种方式实现的,并且执行此操作的 API 函数仍然可用,但不再从命令行提供。![]() ![]() |
请注意,在 X 方向上剪切不会影响图像的高度,而在 Y 方向上剪切不会影响图像的宽度。结果是,图像中某个对象所覆盖的区域不会改变(只有包含图像的周围容器会改变)。 |
![]() ![]() |
"剪切运算符" 是作为对源图像的直接“扭曲”(仅扭曲单个行和列中的像素)实现的。因此,它不使用 插值设置 或 虚拟像素设置。 因此,添加到图像的区域只填充当前的 " -background " 颜色,并且没有提供保留图像原始颜色的方法。 |
使用剪切的等距立方体
虽然剪切不是使用起来最友好或最简单的运算符,但这并不意味着您不能用它们做一些花哨的事情。以下是如何使用 "-shear
" 创建等距立方体的示例。在 Windows 下使用 IM 中的类似 Windows 批处理示例 开发的。请注意,上面的图像没有正确地拼接在一起。它们应该使用 Plus Alpha 合成,但使用的是覆盖。有关更多信息,请参见 对齐两个蒙版图像。因此,您可能会在正确对齐三个图像时遇到问题,导致出现间隙或图像重叠。由于定位仅限于整数定位,因此此问题可能尤其严重。在这种情况下,使用更大的尺寸,更易于管理的坐标,以及对数学进行一些调整会有所帮助。在将图像合并在一起之后,将结果缩放到其最终尺寸将锐化并清理沿接缝的任何轻微错位。另一个类似的示例,但使用 仿射扭曲,并使用正确的 alpha 合成,是 3D 立方体,使用仿射分层。一种极大地简化了生成如上所述的立方体所需的图像处理的方法。 波动图像 - 正弦波位移
"-wave " 运算符类似于 "-shear ",因为它向图像添加“线性位移”。但是,此运算符将仅根据正弦波函数垂直位移像素列。"-wave " 运算符有两个参数。第一个是像素将向上或向下位移的最大高度或 *振幅*,第二个是正弦函数的 *波长*(以像素为单位)。
|
![]() |
请注意,由于像素可以位移到给定的 *振幅*,因此始终会在图像的顶部和底部添加这么多额外的空间,即使实际上不需要这些空间。例如,通过调整参数使 *波长* 成为图像宽度的两倍,您可以将图像制成弧形。
|
![]() |
在这种情况下,可以使用 "-chop "、"-shave ",甚至 "-trim " 操作来移除未使用的空间。让我们通过使用负振幅来翻转弧形,并使用 "-chop " 来移除 "-wave " 运算符添加的未使用的空间,来清理前面的示例。
|
![]() |
当然,"-background " 颜色设置可用于定义添加到图像的额外空间。
|
![]() |
从上面的示例中可以看出,"-wave " 仅在垂直或“Y”方向上应用。如果要添加 X 方向的波形,则需要在应用波形之前和之后旋转图像。
|
![]() |
-wave
" 的另一个限制是,波形仅从零开始。也就是说,最左边的列没有位移,而接下来的几行向下位移(正 X 方向),除非您为初始垂直偏移指定负 *振幅*。基本上,"-wave " 运算符(目前)不允许您为正弦函数的起点指定偏移量。但是,可以通过使用 "-splice " 添加然后移除图像偏移来解决此问题。
|
![]() |
-wave
" 不会使用当前的 虚拟像素设置 来定义添加区域的颜色,但它会查看当前的 插值设置 来将颜色从源映射到生成的图像。这意味着 wave 倾向于在图像上垂直条带上略微模糊像素。圆形扭曲
到目前为止,图像扭曲相当温和,图像数据的拉伸、扩展或压缩非常小。也就是说,数据基本上保持不变。接下来的几个图像运算符会导致图像扭曲到无法确定原始图像的程度。颜色被扭曲成模糊的一团。它也恰好将扭曲效果限制在圆形区域,图像矩形的边缘几乎没有扭曲。这意味着,您可以在较小的区域上使用这些运算符,使用 区域运算符,结果仍然会与原始图像融合,而不至于看起来像是被剪切、扭曲然后粘贴回原处。也就是说,这些运算符被称为“局部”扭曲,因为它们可以用于扭曲图像的较小区域。内爆图像
"-implode " 运算符扭曲图像,使所有像素都向中心拉伸。这有点像在图像的中心放一个真空吸尘器或“黑洞”,并将像素吸进去。但是,建议您只使用非常小的值开始,然后慢慢增加这些值,直到您获得所需的结果。大多数新手用户往往使用过大的值,并对结果感到失望。例如,这是一个典型的图像内爆...
|
![]() |
使用越来越大的值基本上会将圆圈内的所有像素吸入虚无。
|
![]() |
1.0
' 的 "-implode
" 值也会受到 虚拟像素设置 的影响,因为该算法开始对超出实际图像边界本身的边界进行颜色引用。由于默认情况下,"-virtual-pixel
" 设置为“边缘”,因此图像的边缘颜色或周围的框架会对结果产生重大影响。例如,这两张图像相同,只是其中一张添加了白色边框。这基本上显示了使用从图像边界之外查找的颜色区域。该区域通常由 "-virtual-pixel
" 设置定义。
|
![]() ![]() |
Background
',将产生与添加 "-border
" 相同的效果,但不会扩大图像。其他 虚拟像素 设置可以在中心内爆区域中产生更有趣的效果。例如,使用 'Tile
' 设置可以添加图像的高度扭曲副本。例如,这里我使用此设置内爆简单的盒子图像...
|
![]() |
-virtual-pixel
" 效果。随着内爆到很小区域的像素数量增加,以及内爆参数的大小变得非常大,结果开始出现“像素化”外观。要获得更好的、更一致的结果,您可以使用称为 超级采样 的技术来增加内爆使用的像素数量。基本上,通过使用更大的图像(必要时扩大源图像)、进行扭曲,然后将结果缩小到其最终尺寸,您将产生更好的结果。
|
![]() |
通过在要内爆的图像周围使用更大的 "-border ",然后再次将其删除,您还可以将图像的边缘向内扭曲到中心。
|
![]() |
从 IM 版本 6.2.1 开始,您也可以使用透明边框或具有透明度的图像...
|
![]() |
爆炸图像
通过对 "-implode " 运算符使用负值,您可以将图像爆炸。但是,这更像是放大图像的中心,将所有中间半径像素推向边缘,而不是真正的爆炸。
|
![]() |
使用更大的值实质上会将图像中最中心的像素放大成一个圆形,该圆形的大小是图像最小维度的三分之二。
|
![]() |
|
![]() |
旋转图像漩涡
"-swirl " 运算符就像蛋糕搅拌器。它会以您作为参数给出的度数将图像围绕圆圈扭曲。
|
![]() |
通过添加边框并将 "-implode " 组合在一起,您可以营造出漩涡将图像吸入虚无的视觉效果。
|
![]() |
动画(有趣的例子)
最后,让我们生成一些这些扭曲的 GIF 动画。为此,我生成了一些简单的 shell 脚本,用于生成动画图像,您也可以下载并使用自己的测试图像进行播放。这使我想到一个重要点。如果使用这些扭曲生成一系列图像,最好始终从原始图像进行扭曲,而不是在图像上反复进行增量扭曲。这对于旋转图像尤其如此,因为旋转图像在结果中会有一定的模糊,尽管在任何单个操作中都很小,但是如果您反复执行操作,就会出现这种情况。所有脚本都使用“生成的“magick
”命令”技术来创建动画。也就是说,一个 shell 脚本创建一个长的单个命令,然后执行该命令。这避免了生成临时文件的需要,但也可能难以调试。另一个替代方案是使用一种称为 MIFF 图像流 的方法,该方法在循环中生成单个图像,并将其“管道”到最终的“合并”命令中。这在示例 分层图像的编程定位 和 地图中的图钉 中更清楚地示范了。
![]() |
shell 脚本 "animate_mixer " 使用原始图像上的 "-swirl " 生成每一帧。漩涡在一个方向上动画,然后反向动画以形成一个连续的循环。这实际上是 IM 中一个非常典型的扭曲动画示例。这种动画的变化是让动画解开成一个不同的但类似的图像。 |
![]() |
shell 脚本“animate_whirlpool ”不仅在每个图像帧上使用“-swirl ”,还使用 "-implode ",并使用不断增加的参数大小。我使用了一个“lightblue ”边框颜色来表示用于显示整个图像将被“吸入排水管”的额外空间,但我应该使用相同的白色背景颜色,以获得更好的、更逼真的效果。 |
![]() |
图像中间的爆炸(请参阅脚本 "animate_explode "。图像再次放大,以使整个图像爆炸,并在中心绘制一个彩色点来定义最终颜色。 |
![]() |
使用 shell 脚本 "animate_flex ",通过更改 "-wave " 函数的正负幅度,图像的中心向上和向下弯曲。 |
![]() |
使用 shell 脚本 "animate_flag ",我创建了一个“偏移波浪”动画,使图像像旗帜一样飘动。可以通过垂直偏移图像的每一帧来改进动画,以便使左侧边缘保持不变,并且可能添加一个旗杆。但是,这需要您通过数学方法确定该偏移量,这可能很棘手。 |
![]() |
脚本 "animate_rotate " 生成了这个旋转动画,但使用上面描述的原始图像裁剪每一帧,以保留原始图像大小。
|
![]() |
作为比较,这里是一个使用默认设置和“-distort SRT {angle} ”命令生成的考拉旋转。用于生成它的脚本是 "animate_distort_rot "。请注意,使用这种旋转方法,图像的清晰度要高得多,并且在先前版本中明显存在的旋转“抖动”现象也消失了。 |
奖励动画和电影
![[IM 输出]](../images/swirl_video.gif)
-swirl
”扭曲运算符创建了一个很棒的视频,使用 IM OCaml API 脚本制作。选择右边的 GIF 动画以下载视频的完整版本。您能制作一个好的视频来演示扭曲映射技术吗?您是否知道网络上其他地方有这样的视频?给我发邮件。