im_logo.gif

ImageMagick从6.0版本之后命令行有了很大改观,不再像之前一样乱糟糟的了。命令行的格式统一为:

command { [-setting]... "image"|-operation }...  "output_image"

其中{}部分可以重复若干次,用于处理多个图像。

为了弄清ImageMagick是如何处理多个图像的,先了解一下命令行的参数。每个{}对应一个输入图像,所有已经输入的图像构成一个栈(作者这么叫,其实好像和栈 也没啥大关系,当数组看吧),其中-setting部分指定设置参数(Setting Options),-operation指定操作参数(Image Operators)。设置参数一经设置对其后所有图像均生效,而操作参数作用于当前处理栈中的所有图像。

如果细分的话,关系如下:

  • Settings Options
    • Operator Settings: 操作设置;
    • Input Settings: 输入图像设置;
    • Output Settings: 输出图像设置。
  • Image Operators
    • Image Creation Operators: 创建图像;
    • Simple Image Processing Operators: 单个图像操作。如果栈中有多个图像,作用于每一个;
    • Multi-Image Sequence Operators: 多个图像序列操作。一般是将多个图像合并成一个;
    • Image Stack Operators: 操作栈中图像的顺序;
    • Miscelanious Special Operators: 杂七杂八。

整个处理过程可以用作者的一句话来概括:

Settings are saved in some way for later use, while Operators are applied immediately to the images.

文档中还有一个例子来说明,应该很好理解了。

值得一提的是,输入除了给文件名以外还有好几种形式,比较常用的例如:

# all images with extension jpg
convert *.jpg out.gif

# list.txt is an image list
convert @list.txt out.gif

# choose image-001.jpg ... image-005.jpg
convert image-%3d.jpg[1-5] out.gif

输出同理,例如output-%3d.jpg之类的。

图像几何

图像几何用来确定区域大小。基本格式如下:

  1. scale%: 宽高按相同比例缩放
  2. scale-x%x_scale-y_%: 宽高分别按各自比例缩放
  3. width: 宽度为指定值,高度自适应
  4. x_height_: 高度为指定值,宽度自适应
  5. width_x_height: 最大宽高值,保持宽高比
  6. width_x_height^: 最小宽高值,保持宽高比
  7. width_x_height!: 宽高为指定值,无视原图宽高比
  8. width_x_height>: 原图尺寸大于指定值时才做,保持宽高比(大于的定义下面解释)
  9. width_x_height<: 原图尺寸小于指定值时才做,保持宽高比(小于的定义下面解释)
  10. area@: 包含指定像素值,(尽量)保持宽高比

有个坑爹的地方需要注意一下:格式2中第一个百分号%不是必须的。也就是说’200x50%’不是表示宽度=200像素,高度=50%,而是宽度=200%,高度=5 0%。

因为别的都比较简单,只说说格式8和格式9。不妨把原图的尺寸记为w1xh1,指定的尺寸记为w2xh2。大于号>就是说原图在指定的尺寸中放不下时才做这个操作。无 论是宽放不下还是高放不下,都是放不下,即放不下的条件是w1 > w2 or h1 > h2。小于号<正好相反,是说原图在指定的尺寸中放得下时才做这个操作,即放得下的条件是w1 <= w2 and h1 <= h2。这两个符号的共同点是都相当于 将照片恰到好处地、保持宽高比地放入指定大小的像框中(这也是大于号和小于号都不用时的默认行为)。不同点是大于号只处理像框不足以容纳照片的情况,小于号只处理照片 无法充分利用像框的情况。

再深入一点,格式8、9可以和格式7放在一起用。格式7的作用是无视宽高比,也就是相当于大于号和小于号的判断要在宽度和高度方向分别进行。用这个选项照片可以完全填 满像框。

再深入一点,格式8、9可以和格式6放在一起用。格式6的作用是改变判断方式,把条件从“必须限制在一个框框”变成“必须覆盖一个片片”。也就是说,反过来看,指定的 是照片的尺寸,而我们要调整的是像框。大于号处理像框可以完全容纳照片时的情况,避免浪费,也就是判断条件为w1 > w2 and h1 > h2。小于号处理像框不能完全容纳照片时的情况,此时必须增大像框,判断条件为w1 <= w2 or h1 <= h2。

在格式7存在的前提下,有没有格式6已经无所谓了,所以格式6、7混用和只用格式7的效果是完全一样的。

记这些太麻烦了,根据需要整理出表达式然后到这里找吧。

常用功能

下面是比较常用的功能的用法:

#!/bin/bash

# flip (vertically)
convert logo: -flip flip.png

# flop (horizontally)
convert logo: -flop flop.png

# rotate (clockwise, filled with background color)
convert logo: -background "#123456" -rotate 30 rotate.png

# resize
convert logo: -resize 50x120% resize.png

# crop
convert logo: -crop 800x150+220+120 +repage crop.png

# composite
convert logo: rose: -geometry +200+100 -composite composite.png

# add border
convert logo: -bordercolor "#123456" -border 60x60 border.png

# raise
convert logo: -raise 30x30 raise.png

# draw text
convert logo: -fill "#00ff00" -font /tmp/wqy.ttf -pointsize 36 -draw "text 70,380 'This\'s 中文: \"Hi!\"'" text.png

# blur
convert logo: -blur 80 blur.png

# noise
convert logo: -noise 3 noise.png

# negate
convert logo: -negate negate.png

# monochrome (with dither)
convert logo: -monochrome mono_dither.png

# monochrome (without dither)
convert logo: +dither -monochrome mono_no_dither.png

# charcoal
convert logo: -charcoal 2 charcoal.png

# spread
convert logo: -spread 2 spread.png

# swirl
convert logo: -swirl 80 swirl.png

无聊之作

下面的代码可以产生本文开头的GIF动画:

#!/bin/bash

convert logo: -resize "50%" in.gif

for i in {1..20}
do
    iz=$(printf %03d $i)
    echo $iz
    convert in.gif -swirl $(echo "$i*4" | bc) swirl-$iz.gif
done

convert -delay 2 -loop 0 swirl-%03d.gif[1-19] part1.gif
convert -delay 2 -loop 0 swirl-%03d.gif[2-20] -reverse part2.gif
convert -delay 2 -loop 0 part1.gif part2.gif final.gif

下面的代码可以产生竖排文字:

#!/bin/bash
#
# shu.sh

if [[ $# -ne 2 ]]; then
    echo 'Usage: ./shu.sh <textfile> <height>'
else
    cat "$1" | ./shu.py "$2" | convert -background lightblue -fill "#336699" -pointsize 24 -font AR-PL-UKai-CN-Book label:@- png:-
fi

#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
# shu.py

import sys

IGNORE = ' \n,。!?;:“”'.decode('utf-8')

def shu():
    s = sys.stdin.read().decode('utf-8')
    w = int(sys.argv[1])
    i = 0
    t = []
    r = []

    for c in s:
        if c in IGNORE:
            continue

        if i == 0:
            r = []
            t.append(r)
        r.append(c)
        i = (i + 1) % w

    if i != 0:
        while i < w:
            r.append('  ')
            i += 1

    tt = zip(*t)
    for ii in range(len(tt)):
        ll = list(tt[ii])
        ll.reverse()
        ss = u''.join(ll).encode('utf-8')
        if ii == len(tt) - 1:
            sys.stdout.write(ss)
        else:
            print ss

def usage():
    print 'Usage: cat <textfile> | ./shu.py <height>'

if __name__ == '__main__':
    if len(sys.argv) != 2:
        usage()
    else:
        shu()

lantingjixu.png

Links: