【笔记】ImageMagick

前言

这是一篇教程形式的个人笔记(NON-PROFESSIONAL:由初学者编写),苦于国内缺乏IM相关的中文文档或教程,故决定尝试学习官方的英文文档(Examples of ImageMagick Usage (Legacy Version 6))。以教程形式编写是为了自己日后能看懂,也算本人学习新内容的方法。本文不定期但长期更新。


目录

命令行(Command Line)
工具(IM Tools)
    convert
    identify
    mogrify
    其他工具
调整图像大小(Resize or Scaling)
    resize
    thumbnail
    resample
    scale
    sampl
    emagnify
    Adaptive Resize
    Interpolative Resize

命令行(Command Line)

IM现行版本遵循以下命令行格式:

command { [settings] [operation] }...  "implict_write"

其中"{...}"可以被重复添加以对图像进行连续地操作(包括读取和创建图像),并且执行顺序将遵守用户命令的输入顺序,如:

magick convert -size 40x20 xc:red  xc:blue -append -rotate 90 append_rotate.gif

将会首先执行 -appen 再执行 -rotate,即先进行合并,再进行旋转。

命令行参数分为以下几类:

  • Setting Options(属性参数)

    大多属性参数可以以 - 或者 + 开头,用于设定某些功能或效果的属性,但不会对图像进行任何即时的修改

    一般情况下,以- 开头的参数后面都会紧跟一个具体的值,但以+ 开头的参数往往用于关闭某个效果,或将其设置为默认值,所以通常单独使用。

    例如:+gravity 将设置为默认的无重力状态,而-gravity North则明确指定了这一属性。

    设置参数分为如下几类:

    1. Operator Settings(操作参数)用于设定后续图像操作的属性,例如颜色和字体等。如:

      -dither  -gravity  -fill  -background  -bordercolor  -stroke  -font  -pointsize 
      -strokewidth  -box  -virtual-pixel  -interpolate
    2. Input Settings(输入参数)用于设定图像读取或创建的属性,通常是图像的元数据,输入参数仅在图像读取和创建时使用,否则将被忽略。如:

      -label  -delay  -dispose  -page  -comment  -size
    3. Output Settings(输出参数)用于设定图像写入或保存的属性,尽管可以输入在任何位置,但仅在图像输出时生效。如果不设定或使用+ 开头的参数,则图像会使用适当的默认值,通常这个默认值是图像读取时保存的值。如:

      -quality  -loop  -compression  -format  -path  -transparent-color
    4. Control & Debugging Settings(调试参数)用于设定IM的执行方式,包括输出内容、运行模式等。如:

      -verbose  -debug  -warnings  -quiet  -monitor  -regard-warnings
  • Image Operators(图像操作符)

    图像操作符即用户根据自身需求对图像进行处理的命令行参数,这些操作的属性由上述的属性参数决定,同时IM的图像操作是即时的,即图像操作符被检测到时,便会立即执行(所见即所得)。

    和属性参数一样,很多图像操作符也支持以-+ 开头,它们一般区别于操作的方式,如+append 用于水平合并,而-append 用于垂直合并。

    图像操作符分为如下几类:

    1. 创建操作符:从文件读取或创建新图像到内存,包括:

      image.png  xc:  canvas:  logo:  rose:  gradient:  radial-gradient:  plasma:  tile:  pattern:  label:  caption:  text:  
    2. 简单图像操作符:对每一个已经读入内存的图像进行独立地操作,因为图像操作是即时的,所以操作的图像必须在操作符之前给出,若给出多个图像,将依次对其进行处理。如:

      -crop  -repage  -border  -frame  -trim  -chop  -draw  -annotate  -resize  -scale 

      需要注意的是,某些操作符,例如-crop (用于裁剪)将输出多个图像,尽管此时输入的图像只有一个,这意味着输出图像的数量和输入图像的数量不一定是相等的。

    3. 多图像操作符:对所有已经读入内存的图像进行相关地操作,准确地说,是对当前序列中的图像进行相关的操作,因为最开始读入的图像不一定一直地储存在当前序列中。多图像操作符最终输出单个图像(一般)并替代原有的图像序列。这通常用于alpha合成、动画处理、颜色通道处理等,如:

      -append  -flatten  -mosaic  -layers  -composite  -combine  -fx  -coalesce  -clut  -average  -evaluate-sequence
    4. 图像堆栈操作符:对当前内存中图像的排列方式进行必要的修改,但不修改图像本身,通常用于特殊顺序的图像处理。如:

      (  )  -delete  -insert  -swap  -reverse  -duplicate  -clone  
    5. 杂项操作符:一些无法归类的特殊操作符,一般不会使用。如

      -geometry  -version  -list  -bench  -concurrent  -preview  

总结:属性参数只是常量设定,并不修改图像,但图像操作符会参照属性参数,并立即修改图像。

需要牢记的是,在IM6及更新的版本中,你总是需要在图像操作符之前添加操作图像,就像:

convert  storm.gif -flip  cmd_flip_postfix.gif

而不是:

convert  -flip  storm.gif   cmd_flip_legacy.gif

因为下面这种旧的格式会导致一些出乎意料的bug。

下面是一个示例:

magick convert  a.png b.png -append c.png -background skyblue +append result.png

如果你使用 Windows,那么你应该将convert替换为magick convert再运行,如上。

convert :表明你需要转换和修改图像(IM的工具之一,在下文会介绍其他工具),实际上该字段提示IM进行某些初始化工作,例如申请内存创建空图像序列等。

a.png b.png :最简单的创建图像操作符,表明你需要从这两个文件中读取两个图像到内存,此时图像序列拥有两个图像。

-append :多图像操作符,表明立即进行图像垂直合并,在此之前给出了两个图像,于是只有两个图像会被合并。两个并不是特殊的数量,上文已经提到:当你给出多个图像,即使超过两个,IM也会处理所有给出的图像,即垂直合并所有图像。

让我们先停下来看看目前做了些什么,我们使用了convert工具,读取了两个磁盘中的文件,并将其垂直合并,但关键的问题是:目前的图像序列有几个图像?答案是:一个。尽管我们在此之前读取了两个图像,但我们使用了多图像操作符-append ,它会立即输出一个新的图像,并代替原有的图像序列。让我们继续:

c.png :读取一个新的图像,并加入到图像序列,此时图像序列有两个图像,另一个图像是-append 的输出。

-background skyblue :属性参数,设定背景颜色为天蓝色,这将影响接下来的操作(如果背景颜色可能影响该操作的结果),但此时没有任何图像被修改

+append :多图像操作符,表明立即进行图像水平合并,此时考虑背景颜色是否会影响该操作的结果,也许你想到了,如果图像(目前序列中的两个图像)的高度不一致,那么水平合并后必定会“留白”,此时上面设定的天蓝色设定了这一空白的颜色。

倘若你不刻意设定-background 或者使用+background ,那么背景颜色将会使用默认的白色。

在某些情况下,你希望“留白”是真正的空白,那么你可以设定-background transparent ,结果是空白的那部分将会完全透明。(也许你认为真正的空白不是透明,但这个不重要。)

不管怎样,此时,图像序列中应当只存在一个图像,确保你理解了这句话。

result.png :表明输出图像到文件,由于该字段处于命令的末尾,所以默认隐含了-write 这一操作符,在某些时候你可以在命令中插入-write 以输出当前序列中的所有图像。

同时注意到,单个图像进行输出,其给定的文件名中暗示了图像的格式,如这里的.png 将指示IM输出一个png格式的图像。(但在图像创建操作符中并非如此,因为扩展名实际上不决定已存在图像的实际格式。)

工具(IM Tools)

convert

最主要的工具,在之前仅用于图像格式之间的转换,随后陆续添加了很多图像处理的功能,但依旧保留了之前的名字convert ,目前作为图像复杂创建或处理的命令行工具。

在上文我们介绍了图像操作符,在实际应用中,我们会在convert使用大量的操作符,在此我们需要重新审视操作符处理图像的方式,以更好的了解这一工具。

对于单图像操作符,其总是处理当前图像序列中所有的图像;而多图像操作符也总是如此,而且生成的图像将会替代原有的序列;将上述两个规律统一起来,不难得到一个结论:这些操作都是以图像序列为单位的。

无论是简单图像操作符还是多图像操作符,IM始终以图像序列为单位进行处理,而不是以图像为单位(事实上,一个图像仅仅是长度为1的特殊图像序列),这决定了IM始终处理当前序列中的所有图像。

在之前,我们只知道convert,所以理应为IM套上这一结论,但当我们接下来了解到IM的其他工具时,这一结论就不一定成立了,所以现在需要声明的是,上述结论实际上只适用于convert这一工具。

以图像序列为单位,很自然地就能推导出操作符的处理方式,但其运行的效果在人们看来貌似是不自然的,但这一设计无疑是成功的,因为我们将在后面介绍如何灵活地调整图像序列以有规律地处理多张图像,到那时它会爆发出自己强大的能量。

identify

用于获取图像信息,默认情况下它输出一个简短的细节摘要,包括图像名称、图像格式、图像大小、画布偏移量、颜色深度、文件大小等,尝试如下命令:

magick identify a.jpg

一个类似的输出:

a.jpg JPEG 1439x1520 1439x1520+0+0 8-bit sRGB 435.04KiB 0.000u 0:00.002

若需要尽可能详细的信息,可以添加-verbose 命令,如下:

magick identify -verbose a.jpg

同时,identify支持用转义字符输出特定的信息,使用-format 参数进行格式化,例如:

magick identify -format %m a.jpg

其中%m 转义为图像格式,\n 表示换行,只有一个图像时是否换行是无关紧要的,但当你在后面添加多个图像时,使用换行可以分开每个图像的信息。同样的,%k 将转义为图像的颜色数量,你可以在这里了解所有的转义字符。

使用identify 读取图像信息而不是convert 是因为前者总是尽可能地读取图像最小部分的内容以获取需要的信息,而不是将整个图像读入内存,这一行为称为ping ,这决定了较低的资源消耗,同时带来了局限性——很多图像元数据将无法获取。

例如png图片标签的获取,通过下面的命令你可以为一个png图片设置标签:

magick convert a.png -set label "this is the label" a-label.png

这将为文件名为a.png 的图像设定一个标签this is the label 并输出a-label.png ,此时尝试使用identify 获取该标签:

magick identify -format %l a-label.png

其中%l 转义为图像标签,但奇怪的是当你按下回车时,它返回了空内容。原因是ping 技术导致了获取的信息过少,这时你可以使用%[label] 代替%l (前者会自动关闭ping),此时将会正常输出label:

magick identify -format %[label] a-label.png
this is the label

或者你可以直接添加+ping 属性参数来禁用它,如:

magick identify +ping -format %l a-label.png
this is the label

mogrify

与convert相似,但在批量读取和保存时更方便,只能同时处理一个文件,默认情况下将会保存覆盖原文件,所以使用该工具容易破坏原文件,应当注意备份。当然,可以使用某些参数另外设置保存路径,例如:

magick mogrify -format jpg *.png

此处-format 并不是格式化,而是设定了保存时的格式与后缀名,该命令将会把现行目录下的所有png文件转换为jpg文件。

magick morify -path output -thumbnail 100x100 *

此处-path 指定了保存时的目录,在这里它将图像输出到名为output 的目录,你必须提前创建这个目录,-thumbnail 100x100 表示为图像创建一个尺寸为100x100的缩略图,末尾的* 表示处理该目录下的所有图像文件。

注意:即使使用了上述的参数,在输出时若仍然存在同名文件,mogrify仍然会将其覆盖。

mogrify只能同时处理一个文件,即以单个图像为单位进行处理(这与convert截然不同),这意味着它不能使用多图像操作符和图像堆栈操作符,它只用于进行大量但简单的单图像处理,一旦涉及个性化,它的命令会显得极其繁琐,此时应当使用convert。

其他工具

  • composite: 专门用于图像叠加,一次只处理两个文件。

  • montage :用于在处理图像时生成图像缩略图。

  • display :用于显示静态图像。

  • animate:用于显示动画。

  • compare:用于比较图像。

  • stream:用于管道处理。

  • import:用于截取显示器图像。

  • conjure:IM脚本语言。

调整图像大小(Resize or Scaling)

接下来,我们会详细讨论如何使用IM调整图像的大小,在本章会讲一些相对简单的东西(图像操作符的使用),然而调整图像大小并不是个简单的事(有时候反而十分棘手),在下一章我们会深入研究其中的原理。

resize

使用-resize 调整图像像素大小(下面简称“尺寸”)是最常用的,其后紧跟一个参数,指定新的尺寸。阅读以下代码:

magick convert a.jpg -resize 64x64 a2.jpg

该代码尝试将a.jpg 转换为 a2.jpg ,其新的尺寸为64x64,但一般情况下该代码输出的图像尺寸都不会是64x64,原因是-resize 默认情况下会保留宽高比,在此前提下将图像转换到尽量接近所给尺寸的大小。

你也可以只提供宽度,例如-resize 64 ,IM将会保持比例调整至对应的尺寸。

若希望强制修改图像尺寸而忽略原比例,尝试添加标志! 如下:

magick convert a.jpg -resize 64x64! a2.jpg

此外还有其他的标志:

magick convert a.jpg -resize 64x64^> a2.jpg
magick convert a.jpg -resize 64x64^< a2.jpg

标志>< 指定了图像调整的过滤条件,前者将只调整原尺寸大于所给尺寸64x64的图像,即仅缩小;而后者只调整原尺寸小于所给尺寸64x64的图像,即仅放大;一般情况下,若图像原尺寸与目标尺寸一致,-resize 不会对图像进行任何修改。

注意:在Windows系统的命令提示符中,>< 都具有特殊意义,此时需要使用转义字符^>^<

magick convert a.jpg -resize 50% a2.jpg

使用百分数成比例地进行调整,此时% 是一个标志。注意:调整的结果并不一定是精确的,因为按百分数进行调整最终会舍入到最接近的整数,我们将在之后介绍如何改变这一情况。

同时注意到上述仅提供了一个参数50% ,这指定了宽度的比例,而IM会自适应其高度,若要强制修改高度,可以使用50%x10% ,此时会忽略图像宽高比例。

magick convert a.jpg -resize 4000@ a2.jpg

使用@ 标志限制指定图像最大的像素数量(宽度 * 高度),IM会调整图像使其接近这一数值,注意默认情况下依然会保持图像宽高比例,故最终的结果只会接近这一数值(一般情况)。

特别地,你可以使用@^> 缩放大于这一尺寸的图像,但@^< 是不被支持的。

你也可以在文件读取时为其调整尺寸,用[] 包括目标尺寸紧跟在文件名后,参数所用的格式与上述一致。

magick convert a.jpg[64x64] a2.jpg

最后,请注意:标志是否发挥作用与其所在的位置无关,而决定于标志是否存在,例如%5050% 的效果是一致的,尽管后者更具备可读性。由于这一机制,将会导致一些误解,例如-resize 50%x30 实际上的效果是宽度和高度分别调整到原来的50%和30%,而不是50%和30px。

thumbnail

-thumbnail-resize 的用法相同,也具有基本相同的效果,但前者专门用于生成缩略图,故在速度上有一定优化(特别是大文件),除此之外,-thumbnail 会自动删除图像中除color profile之外的文档数据以减少缩略图的文件体积,若希望删除color profile 以进一步压缩体积,你需要使用-strip 参数。

resample

用于调整图片的分辫率大小,即重采样。分辫率决定了一个图像的像素密度,即图片的精度。这通常用于调整图像以适用某些硬件设备(如显示器、打印机),当然,图像尺寸也会同时发生变化,即表示图像的像素数量会发生变化。但图像在真实空间里显示的大小是不会变化的,即保证了图像在对应分辫率的显示器上显示时,看起来是一样大的。

用法与-resize 类似,如:

magick convert a.jpg -resample 320 a2.jpg

只有少数图像格式可以在文件中储存图像的分辫率(如PNG、JPEG、TIFF),故对于不支持的格式,IM无法得知其原始分辫率,此时应当在-resample 之前使用-density 参数指定图像的原始分辫率(dpi为单位),如果不指定,IM将以72dpi为缺省值。

若在-resample 之后使用-density 参数,即只指定图像的输出分辫率大小,并且不会修改图像的像素大小。

scale

相比于 -resize 更简化、快速。在缩小时,它只是简单的进行颜色平均以决定新像素的颜色;在放大时,它只是简单的进行像素复制。

如上图(左a.gif,右b.gif),对大小为32x32的a.gif缩小,执行以下命令:

magick convert a.gif -sacle 16 b.gjf

此时对原图像每个四像素的矩形进行颜色平均,平均的结果(灰色)作为新图像的一个像素的颜色。因为原图像是32x32,缩小到16x16,恰好前者是后者的整数倍(边长是两倍,面积是四倍),于是缩小的结果是自然的,因为原图像恰好可以完全分割成对应倍数的全等矩形。如若原尺寸不是目标尺寸的整数倍,则会出现Moiré pattern (莫尔条纹)。实际上这种处理方式有其独特的名字——Pixel Mixing (像素混合),我们将在下一章深入了解它。

image-20200809155605640

如上图,对a.gif进行放大,比如放大到64x64,执行以下命令:

magick convert a.gif -sacle 64 b.jpg

此时仅对原图像每个四像素的矩形进行复制,不会出现颜色平均,只有像素行列的不断重复。然而,如果目标尺寸不是原尺寸的整数倍,则会在图像的某些位置出现颜色平均(如下图),我们将在下一章解释这一情况。

image-20200809160923299

sample

更为简化的调整图像大小的方式。在放大图像时,它只复制像素,这与上述的-scale 相似,但不完全一致,不一样的地方在于非整数倍的放大,-sample 不会出现颜色平均,它有新的表现形式(如下图),这种处理方式也有其独特的名字——Box Filter

image-20200809161111116

在缩小图像时,它只删除像素,或者说,它将图像分割为一连串的矩形区域,并对每个区域进行一个像素的颜色采样,作为缩小后的结果,这保证了新的图像不会产生任何新的颜色,这在某些方面是很有用的,例如GIF压缩。然而这种采样经常导致一些可怕的结果,例如:

image-20200809162136466

原因是采样的过程中删除了“有用的像素”,这种现象叫Sever Aliasing Effect (严重锯齿效果)。默认情况下,IM总是采样区域中心的像素颜色,若区域有偶数个像素,则总是选择左上的中心像素。

magnify

magnify 后面无需添加参数,因为它总是使图像扩大两倍,这使用了一种特别的放大滤镜Scale2X ,这个算法试图平滑地放大像素而不产生新的颜色,在处理二次元图像时有一定优势。一个示例如下:

magick k.png -magnify -magnify -magnify k2.png 

连续三次放大,使得原图像放大8倍,在处理像素风格的图片时可以得到一个相对高清的结果:

image-20200809235740358

Adaptive Resize

-adaptive-resize 使用Mesh Interpolation (网格插值,双线性插值的变体)方法放缩图像,与-resize 不同,该操作符可以避免图像在颜色急剧变化(这通常发生在边缘)时产生模糊,特别是在轻微地放大缩小(尤其是放大)时具有良好的效果,下图使用-resize(左) 和-adaptive-resize (右)放大同一图像,可以看到右边更为清晰。

image-20200810000831296

当然,与所有像素插值算法一样,在放大或缩小倍数较大时(50%以上),它会导致混叠和莫尔条纹。

Interpolative Resize

使用Interpolation Method 放缩图像,也就是常说的 “线性插值”

To Be Continued……