ImageMagick 使用示例 -- API 与脚本编写
- Windows 与 DOS
- PHP
- 使用 Shell 命令的 PHP
- PHP Imagick(PHP 的 ImageMagick API)
-
PerlMagick(Perl Magick 脚本)
-
编写更好的 ImageMagick Shell/PHP 脚本的提示 为什么要使用多个 "magick" 命令 让 IM 更快(总体而言) 从源码编译 ImageMagick
- 在 Ubuntu 上从源码安装 ImageMagick
- MacOSX 上的 ImageMagick
- 编译 HDRI 版本的 IM
- 创建个人 ImageMagick 安装
本使用示例所讨论的 ImageMagick 命令行界面(CLI),只是你借助 ImageMagick 核心函数库(MagickCore)来使用、修改和控制图像的方法之一。它本质上是一种 'shell' API 接口。此外还有许多应用程序编程接口(API),可以让你从多种编程语言中更直接地使用它,参见 ImageMagick API。这里我将探讨改进你的 IM 脚本编写与编程的方法、Windows 与 Unix 脚本编写的差异,并了解从其他 API 和编程语言使用 IM 的基础知识。
API 与其他 IM 使用方法
用于实际图像处理的 API(Application Programming Interface),其实并不比使用 CLI 命令(比如 "magick",它本身就代表一种 shell API)更快。IM 中所有的图像处理都使用同一个 '核心' 库。所以,如果你在做诸如图像扭曲这类复杂任务,比起用 shell 的 "magick",使用 API 在整体处理 '速度' 上几乎没有区别。那么,为什么要用 API 而不用命令行? shell 会不断 'fork' 出许多不同的命令(不仅是 IM 的 "magick" 命令,尽管 shell 有一些 '内置命令'),每一个在运行时都必须被加载和初始化。每个 IM 命令也都必须重新初始化其配置文件、解析命令行参数、重新读取你正在处理的图像,并且常常还要把结果保存回磁盘。所有这些都需要时间,从而导致速度变慢。也就是说,所有这些额外的步骤都消耗时间和处理能力,所以如果你频繁地这样做,API 就开始变得有意义了。API 还能让你做一些命令行做不到的事情。
- 如果 API 已经在运行,你几乎不需要、乃至完全不需要启动时间。
- 可以在内存中保存许多图像列表的数组,并以任意你喜欢的顺序处理它们。例如我有一个程序,它读入数百张图像,在读取时把每张缩略成 32x32 像素的图像,然后用 magick 将它们两两成对、每次一对地逐一比较(100 张图像需要 9,900 次比较)。另一个例子则是在内存中按图像的整体颜色对它们排序!
- 你可以进行许多不同的图像处理线程,顺序任意。在处理下一张图像或下一步之前,你不必先 '完成' 某一串处理。例如,想象一个求解拼图的程序!
- 你可以从图像中获取信息,并以复杂的方式利用这些信息来修改图像处理,而无需一遍又一遍地重新初始化图像处理(重新读取配置和图像)。例如,在算出图像的取景要求之前先获取图像尺寸。
- 遍历图像,对每一张图像做完全不同的事情!例如生成一段动画序列,每一帧都以略微不同的方式扭曲。
- 对图像数据进行直接且完全随机的访问。例如在图像中寻找一张 '脸'。
但对于大多数不涉及大量图像、或以明确定义的方式对图像进行常规处理的情形,命令行差不多同样快。不过,你可以通过以下方式节省大量时间和重复处理…… * 善用中间图像的 MPC 图像保存(以便更快地重新读取)。参见我的脚本 divide_vert。 * 使用管道化进行并行处理(甚至可以为不同步骤使用不同的机器!),以更好地利用处理器,并避免把中间图像保存到磁盘。关于一个管道化脚本的例子,参见 enlarge_image,其中一个步骤把图像通过管道送入序列中的下一个(有时是可选的)步骤。 * 使用循环单独处理每张图像,然后再把图像结果的数据流通过管道送入最终的 '把它们全部合并' 步骤。我有很多这样的脚本…… 关于这样一个脚本,参见 分层图像的程序化排布。
当然,在 API 中,对于涉及访问图像的特殊处理任务,你也本可以使用不同且更快的技术。而当我们发现并有时间时,往往会把这些技术编入核心库。图像扭曲和各种 FX 表达式就是其中一例。
Windows 与 DOS
关于使用 CLI API 进行 Windows 与 DOS 脚本编写的示例,已移至 Windows 下的用法。
PHP(从 "system()" 函数调用 IM 命令)
PHP 用户有三种使用 ImageMagick 的方式。
- "
imagick" PECL 接口 - "
MagicWand" 接口 - 使用 "system()" 和 "exec()" 函数来运行 CLI 的 "
magick" 命令。
由于 IM 示例 的存在,后一种方法(以及我们接下来要看的第一种方法)近年来已成为从 PHP 使用 IM 最常见的方法。当然,对某些情况来说,这也许不是最好的方法(见上文),这时可以使用 API 接口,不过可能需要系统管理员在你的 PHP 环境中启用它们。
使用 Shell 命令的 PHP
关于使用这一技术的具体信息,最好的来源是 IM 论坛用户 Bonzo 和他的网站 RubbleWebs。请注意,PHP 运行 "magick" 所处的环境与你在命令行中得到的不同,甚至很可能是以不同的用户身份运行。因此,在命令行下能工作的东西,要在 PHP 的 web 驱动脚本中运行起来,可能需要一些调整。以下是对 ISP 的命令行 IM 接口进行初步测试的推荐步骤,前提是你在远程系统上没有直接的命令行 'shell' 访问权限。也就是说,你只能上传 web 文件来执行。所以我们首先要做的,是尝试找出系统上的 "magick" 命令、安装的是哪个版本,以及 PHP 运行所处的环境。在 Linux Web 服务上,把这个 PHP 脚本上传到 ISP 的 web 服务器并访问它……
<?php
header('Content-Type: text/plain');
system("exec 2>&1; pwd");
system("exec 2>&1; type magick");
system("exec 2>&1; locate */magick");
system("exec 2>&1; magick -version");
system("exec 2>&1; magick -list type"); <!-- before IM v6.3.5-7 -->
system("exec 2>&1; magick -list font");
print("------ENVIRONMENT VARIABLES-------\n");
system("exec 2>&1; env");
?>
这会运行相当多的命令,来查看你的环境是什么样的。第一个 "pwd" 告诉你运行该 PHP 脚本时所在的当前目录。这个目录不一定就是 PHP 脚本所在的目录,而且你也许无法从 PHP 脚本向该目录写入。接下来的两个命令用 "type" 告诉你 "magick" 是否在默认提供的命令 PATH 上可用,如果可用,它位于何处。"locate" 命令应当能找到服务器上存在的所有 "magick" 命令(假设它是一台 linux 服务器),但也可能找到其他并非 ImageMagick 的 "magick" 命令、文件和目录。你需要自行解读结果。接下来的三个命令假定 "magick" 在命令 PATH 上,请它报告版本号,以及 IM 认为自己能访问哪些字体。哪个命令报告字体取决于所安装的 IM 版本有多旧。如果你只看到错误,那么 "magick" 就不在命令行路径上,你的 ISP 提供商没有正确初始化 web 服务器的 "PATH" 和 "LD_LIBRARY_PATH" 以将其包含进来。他们定义了什么,请查看 "env" 命令的输出。如果是这种情况,你需要弄清楚它究竟位于何处,并使用类似下面的 PHP 脚本。这会降低你脚本的可移植性,因为它是针对那个特定 ISP 硬编码的。例如,假设 "magick" 命令位于 "/opt/php5extras/ImageMagick/bin",那么你可以设置一个变量来指定它的位置。对于要在不同 ISP 主机上使用的 PHP 脚本,这通常作为应用程序配置和安装过程的一部分来完成。
<?php
$im_path="/opt/php5extras/ImageMagick/bin"
header('Content-Type: text/plain');
system("exec 2>&1; $im_path/magick -version");
system("exec 2>&1; $im_path/magick -list type");
system("exec 2>&1; $im_path/magick -list font");
?>
如果你遇到 "ldd" 库错误,说明 "LD_LIBRARY_PATH" 有误,ISP 在安装时肯定没把工作做好,你需要报告该错误,让他们修正 web 服务器的 "LD_LIBRARY_PATH" 环境变量设置,或者重新安装 ImageMagick。除了设置 magick 命令的位置之外,你也可以在开头用类似这样的一行来调整 PATH 环境变量。不过,这种方法在典型的 PHP 系统配置下常常被默认 '拒绝'……
putenv("PATH=" . $_ENV["PATH"] . ":/opt/php5extras/ImageMagick/bin");
putenv("LD_LIBRARY_PATH=" . $_ENV["LD_LIBRARY_PATH"] .
":/opt/php5extras/ImageMagick/lib");
之后,试着从 IM 示例中挑一些较简单的例子,让它们跑起来。例如,把 IM 的 'rose' 图像作为 JPEG 图像文件输出回 web 用户……
<?php
header( 'Content-Type: image/jpeg' );
passthru("magick rose: jpg:-");
?>
如果你需要设置 magick 命令的位置,可以用……
<?php
header( 'Content-Type: image/jpeg' );
$magick="/opt/php5extras/ImageMagick/bin/magick"
passthru("$magick rose: jpg:-");
?>
或者如果你遇到库的问题,可以试试类似这样的写法……
<?php
header( 'Content-Type: image/jpeg' );
$magick="/opt/php5extras/ImageMagick/bin/magick";
$libs="LD_LIBRARY_PATH=\'" . $_ENV["LD_LIBRARY_PATH"] .
":/opt/php5extras/ImageMagick/bin/magick\'";
system("$libs $magick rose: jpg:-");
?>
如果你仍然什么都看不到,请查看我那份原始的 PHP 提示与技巧 文件,了解重定向错误消息的技术。
当那个基础脚本能工作后,你可以试用 PHP 测试脚本列出的字体之一(请根据你的 PHP 服务器修改下面的内容)。例如,在我当时能用的一台 Solaris 服务器上,我注意到 'Utopia' 字体集可用,于是我得以尝试用该字体创建一个标签……
<?php
header('Content-Type: image/gif');
passthru("magick -pointsize 72 -font Utopia-Italic label:'Font Test' gif:-");
?>
Shell 到 PHP 的转换示例
这里有一个相当典型的 ImageMagick 命令……
magick -background none -fill red -gravity center \
-font Candice -size 140x92 caption:"A Rose by any Name" \
\( rose: -negate -resize 200% \) +swap -composite output.gif
转换成 PHP 后,它会变成类似这样的东西……
<?php
header('Content-Type: image/gif');
$color="red";
$image="rose:";
$scale="200%";
$size="140x96";
$string="A Rose by any Name";
passthru( "magick -background none -fill '$color' -gravity center" .
" -font Candice -size '$size' caption:'$string'" .
" \\( '$image' -negate -resize '$scale' \\) +swap -composite" .
" gif:-" );
?>
注意我仍然把 "magick" 命令那条长命令行拆开,以便让图像处理序列更易于理解,也便于以后编辑。这是用 PHP 的字符串拼接来完成的,而不是 shell 脚本中使用的 shell 续行。也请注意后续各行开头多出的空格。以及把原命令中原有的其他反斜杠加倍。或者,你也可以用单引号而不是反斜杠来保护那些选项。我还用了一些 PHP 变量,以便更容易调整所生成的 PHP 脚本图像,从而更好地控制结果。不过,当我把这些选项插入 "magick" 时,我用单引号来保护它们不被 shell 进一步修改。但要当心那些被插入字符串中的单引号!你可以把这些选项做成 PHP 的输入参数,这样就能为从 web 请求传入的任意输入文本生成图像。你还可以在同一个 system 调用字符串内执行多条 shell 命令。事实上,只要你愿意,单个 system 调用可以包含一整个 shell 脚本!所以你可以在一个 system 调用中完成 shell 循环和多条命令(连同清理)。这是很多人没意识到可行的一点。基本上,只要小心,你就能同时充分利用 PHP 提供的数学运算和 shell 的脚本能力。只是要注意引号。关于从 PHP 调用 ImageMagick 命令的各种示例,参见 Rubble Web, 用 PHP 编写 IM 代码,它介绍了大约四种不同的技术。
注意多余的引号
请注意,PHP 中的 IM 命令通常会被额外一层引号(一般是双引号)包裹,因此必须小心处理这额外一层引号。记住,当 PHP 执行一个字符串时…… PHP 进行它的引号、反斜杠和变量替换。Shell 接着拆分参数,并进行它自己的变量与引号替换。如果存在 "2>&1" 之类的文件描述符重定向,它也会处理。ImageMagick 得到的是一个参数数组,但它也会进行自己的处理:专门针对 DOS 的文件名元字符处理(dos 环境不处理元字符),以及针对诸如 coder:*.gif[50x50] 这类参数的处理——由于 coder: 前缀或 [...] 读取修饰符,shell 无法展开它们。也就是说,有大量的参数解析!这可能意味着大量的引号和反斜杠处理。需要谨慎和预先思量。我建议你至少读一读关于 程序执行函数 的 PHP 手册,其中包括:PHP exec()、system() 和 passthru()。也看看 反引号运算符。尤其重要的是,弄清楚究竟返回了什么(一般只有最后一行),以及传给调用方客户端的是什么(其余全部)。
PHP 安全
记住……
_在网络上,你唯一能信任其不具潜在敌意的用户,
就是那些 *积极* 怀有敌意的用户。
-- Programming Perl - Camel Book, r3_
你必须彻底检查从用户传给 IM 命令的所有输入参数。确保参数正是你所期望的。在与万维网打交道时,过于严格远比过于宽松要好。需要留意的一些常见事项包括
- 参数中的二进制字符——常见的破解手法
- 参数中意外出现的空格、制表符、换行和回车
- 反斜杠(目录分隔符)和 '
..' 路径。此外在 windows 下,文件名中的 '\' 和 ';' - 包括 "
~*?[]{}<>" 在内的文件展开元字符,外加 ImageMagick 特有的特殊 "@" 元字符 - 包括 "
$#;" 在内的其他 shell 元字符,以及三种引号字符 '''、'"'、'```' - 参数与 ImageMagick 所期望的不符。要了解 IM 对特定选项能理解哪些类型的参数,请使用 "
[-list](https://imagemagick.org/command-line-options/#list)" 来查看。例如,用户输入的 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 选项只有 10 种不同的设置。 - 等等……
对于任何一种 web 编程工作,理解安全性、以及黑客如何利用精心构造的参数来颠覆被调用的命令,都至关重要。不仅是 PHP,shell 和 ImageMagick 也一样。IM 需要特别小心,因为它例如可以读取一个密码文件并把它 magick 成一张图像返回。再说一遍…… 当涉及 web 时,过度严格远比打开一个意想不到的安全漏洞要好。
写入文件系统
如上所述,而且如果你照做了上面的初始步骤,你就会确切地知道,PHP 在服务器上一般以一个不同的、更受限的用户身份运行。因此,它通常无法写入包含脚本的目录(或它实际运行所在的任何位置)。出于安全原因,你一般 不会想写入那个目录!如果你真的想让 PHP 写文件,就让它把图像(或数据)保存到 "/tmp" 中一个唯一的文件名,而且最重要的是,无论是正常退出还是发生任何错误,都要 事后清理。一个不好好清理的应用程序,能多快用遗留的临时文件把磁盘塞满,实在令人吃惊。如果保存的文件(图像)需要能被 web 服务器看到,就为这些文件专门建一个 '由程序写入' 的子目录。应该怎么做。 大多数 PHP 应用程序实际上通过使用数据库后端,完全避免向文件系统写入任何东西。也就是说,cookie、令牌、用户数据、图像等全都写入数据库,例如(按复杂度和规模排序)SQLite、PostgresSQL、MySQL 和 Oracle。它在文件系统中什么都不保存。系统程序员通常会在安装应用程序时把这些信息配置到 PHP 应用程序中。图像通常由同一个、或另一个 PHP 脚本来再现,它查出图像 'blob' 并输出给客户端。图像可以作为 '内联' 图像随 HTML 本身一起发送(参见 "[inline:](files.html#inline)" 格式,其中有 HTML 内联图像的演示),或者作为单个 '合而为一' 的多重图像发送,这样客户端 HTML/JAVA 就只需一次图像请求,而不是 20 次单独的请求。最后一点。应当始终具备某种清理旧数据的方法。一个两年没登录的用户,很可能应当把他的数据删除。
获取错误输出
试试下面这些方法之一……
<?php
exec("/usr/local/bin/magick -version",$out,$returnval);
print_r($out[0]);
?>
或者
<?php
exec("/usr/local/bin/magick -list",$out,$returnval);
print_r($out);
?>
或者像下面这样使用 shell_exec
<?php
$IM_version=shell_exec("/usr/local/bin/magick -version");
echo $IM_version
?>
要包含 STDERR 输出……
<?php
$array=array();
echo "<pre>";
exec("magick read_test.png write_test.png 2>&1", $array);
echo "<br>".print_r($array)."<br>";
echo "</pre>";
?>
以上内容来自 IM 用户论坛的讨论 如何在 php 中显示 IM 的错误信息?。也请参见我自己的 PHP 调试错误日志 笔记。它指向 PHP 手册中关于错误日志的部分 PHP 错误处理与日志,尤其请看示例小节。
更安全的 ImageMagick 命令……
出于安全原因,理想情况下你会想避免用 shell 把单个长字符串解析成一条命令和若干参数。自己来做更好!这意味着你把参数作为一个由分开的字符串组成的数组提供给命令,而不是作为单个由 shell 解析的字符串。这样做可以防止 shell 语法错误的可能、shell 所需的额外引号负担,也防止某个黑客破坏 shell 命令并运行他自己命令的可能(非常糟糕)。另一方面,你会失去 shell 的脚本编写、管道和文件重定向能力,但当你已经在使用 PHP 或其他包装语言时,这通常不是多大的损失。在 PHP 中,我能找到的唯一能不经 shell 直接调用命令的函数是 pcntl_exec() 函数。它基本上避开了 shell,直接调用命令。不过,它是一个真正的 'execl()' 系统调用,会用给定的命令替换当前进程。也就是说,它不会做把它作为子进程运行所需的 'fork()' 和文件描述符链接。因此 pcntl_exec() 对于一般用途实在太底层,实现一个 '不用 shell' 的命令可能会变得相当复杂。我非常惊讶 PHP 接口至今还没有提供一个更简单、更安全的 '避开 shell' 的命令调用。不过话说回来,我并不是 PHP 程序员。而 Perl 则提供了若干安全调用子命令和进程的方法,这往往使它作为 web 接口比 PHP 更可取。有懂 PHP 安全的人愿意指点一二,或提供更多信息的线索吗?
PHP 'IMagick' API
要测试 PHP PECL Imagick 模块是否确实在工作,请把一张简单的测试图像 "image.jpg" 和这个 PHP 脚本上传到同一个可从 web 访问的目录。
<?php
$handle = imagick_readimage( getcwd() . "image.jpg" );
if ( imagick_iserror( $handle ) ) {
$reason = imagick_failedreason( $handle ) ;
$description = imagick_faileddescription( $handle ) ;
print "Handle Read failed!<BR>\n";
print "Reason: $reason<BR>\n";
print "Description: $description<BR>\n";
exit ;
}
header( "Content-type: " . imagick_getmimetype( $handle ) );
print imagick_image2blob( $handle );
?>
网上有一本 PHP IMagick 书,更多使用 IMagick 的例子可以在 Mikko 的博客 找到。IMagick 唯一的问题是它一直没有得到维护和升级,所以可能有若干函数无法工作或已缺失。请确保你使用的是 v3.x 的 IMagick 和当前版本的 IM。它确实能工作,对大多数事情也工作良好,但如果你需要做其他事情,那么其他 PHP 方法也许是更好的选择。
PHP 'MagickWand'
你可以用下面的方法检查 PHP MagickWand 模块是否是 PHP 安装的一部分……
<?php
if (extension_loaded('magickwand')) {
echo "PHP MagickWand is available!\n";
} else {
echo "PHP MagickWand is NOT available!\n";
}
?>
但要检查它是否确实在正常工作,请上传某张测试图像 "image.png" 和这个脚本……
<?php
$image = NewMagickWand();
if( MagickReadImage( $image, 'image.png' ) ) {
header( 'Content-Type: image/jpeg' );
MagickSetImageFormat( $image, 'JPEG' );
MagickEchoImageBlob( $image );
} else {
echo "Error in MagickReadImage()";
echo MagickGetExceptionString($image);
}
?>
对上述内容不作任何保证,不过欢迎更多反馈。我一般不用 PHP 编程,但我用上述方法测试过一个 SunONE-PHP5 的测试安装(三种方法全都试了:命令行、magick、MagickWand)。
复杂的 PHP 脚本……
如果你需要既生成又输出 HTML 和图像,请考虑这样设计你的 PHP 脚本:让不同的 HTML 请求或输入选项,从同一个或不同的 PHP 脚本,生成你 web 文档上所需的各个部分。也就是说,一个顶层 PHP 脚本输出带有适当 标签的 HTML,这些标签用适当的选项调用它自己(或另一个 PHP 脚本),来创建或修改显示在那第一个顶层 PHP 脚本上的图像。这正是很多相册和绘图 PHP 脚本所做的。全都通过对 URL 调用的 GET 和 PATH_INFO 扩展来控制。注意,你不能在 IMG 标签内使用 POST。通过这种方式,你应当能完全避免为 PHP 生成的网页去生成、保存和清理临时图像的需要。那是一种充满问题的解决方案,比如资源限制和垃圾回收,是一种非常糟糕的编程技术。这一技术在两本 ImageMagick 书 中都有探讨,不过其中实际使用的 ImageMagick 已经开始有点过时了。
Perl Magick 脚本
PerlMagick API 是把 "magick" 命令转换成脚本的好方法,这种脚本还能处理数据库、大量图像,或其他方式难以做到的更复杂的图像处理。最好的帮助就是看 PerlMagick 的 'demo' 脚本,它既在源码里,通常也安装在 PerlMagick 的文档区域。在我的系统上,那位于 "/usr/share/doc/ImageMagick-perl-*/demo/"。这个目录里有越来越多读取、写入和处理各种图像的简单示例。此外还有一个 "demo.pl" 脚本,它列出了几乎所有常见的图像处理选项,以及你如何使用它们。把命令行的 "magick" 命令转换成 perl 时,有几点你需要记住。
- 首先要记住的是,PerlMagick 不会自动删除处理后新生成的图像。许多操作符会从旧图像创建一张新的、被修改的图像,而另一些则直接修改现有图像。
- 另外,许多操作符不会把某个特定操作应用到整个图像列表,而只应用到你给它的列表中的第一张图像。这意味着你需要自己遍历图像序列(perl 数组)。
- 你可以拥有许多图像序列。事实上,你通常把每张图像都读入它自己单独的图像序列,而不必像在命令行上那样凑合着用单个图像序列。
- 而且,一旦图像进入内存,你就能轻松获取现有图像的尺寸。这意味着你可以直接创建新画布,而无需像在命令行上那样克隆并清空一张现有图像。不过,克隆图像也会复制图像的元数据,所以对于数码照片这类图像,你也许会想留意那些元数据。
- 在每一个主要处理之后,尤其是在读取图像时,检查图像错误(如 PerlMagick 页面所示)。
要把命令行转换成 perl,你基本上就是以完全相同的顺序做完全相同的操作。不过,由于图像一般不会被删除,而且多个图像序列很常见,"magick" 命令中括号和额外克隆操作的使用通常不成问题。转换脚本时最难的部分,通常是把命令行选项映射到 PerlMagick 的函数调用。我发现最快的方法是获取 IM 的源代码,查看文件 "MagickWand/mogrify.c",搜索你遇到麻烦的那个特定命令行选项。例如对于 -threshold 选项,就连同引号一起搜索 "threshold"。会有两处匹配,一处是为确认所有选项都被找到而做的快速语法解析,另一处则是该选项实际的内部调用。在这里你会找到所用库函数的名字,它通常会直接映射到 Perl 函数。在这个例子中就是…… BilevelImageChannel()
安全警告
在编写供公众使用的脚本时,尤其是世界上任何人都可能运行的基于 web 的 PHP 脚本,检查一切可能来自未知(甚至已知)用户的东西至关重要。我指的是 一切,从参数、文件名、URL,到图像也一样。在你验证某个输入参数之前,那个参数可能包含字母、数字、空格、标点,甚至 'null' 和控制字符。在你彻底检查它之前,都应把它视为可疑,不应使用。你是否在使用某种由 web 控制的输入表单,并不重要。稍有知识的人完全可以根本不用那个输入表单,就用他自己的参数调用你的 PHP。别以为他们不会这么干,机器人就在外面,读取输入表单、构造自己 '篡改过' 的参数,试图侵入随机的脚本。
文件名中的元字符
作为一个安全问题,你尤其要当心的是含有空格、引号、标点、控制字符或其他元字符的文件名,因为 IM 和 shell 都可能试图展开它们。问题在于,一个名为 '*?@${&) .jpg' 的文件在 UNIX 下其实是完全合法的文件名,但如果程序(比如 shell 和 IM)也做文件名展开,很多程序在处理它时都会遇到麻烦。记住,即使你阻止了 shell 进行 'glob' 元字符展开,IM 自己也会做这种展开(为了 DOS 用法)。因此,阻止所有这类字符(并给出错误)大概是明智之举。作为一项安全措施,如果文件名中含有任何未知或不寻常的字符——凡是不是字母、数字或预期后缀的东西——就报错并中止,往往是个好主意。在把这样的文件名传给 shell 命令或 IM 之前就这么做。宁可严格得多、把事情拦下来,也别宽松放行、让什么坏东西溜过去。
编写更好的 ImageMagick Shell/PHP 脚本的提示
这些是我针对一份贡献者发到 IM 邮件列表供他人使用的 shell 脚本,提出的一些基本脚本编程要点。我原本是私下把它们发给作者的(他将保持匿名),他对此心怀感激。它们并非全都是 IM 特有的,但作为标准编程实践,无论如何都应当采用。尤其是如果你打算让别人使用、查看和/或修复你的程序或脚本。这反过来会让你的脚本更有用。
- 把 '帮助' 或 '文档' 放在脚本和程序的开头。这样别人无需安装或运行脚本,就能容易得多地弄清某个程序做什么。我自己常常宁可扔掉一个没有清楚说明其用途注释的程序,也不愿去编译或运行这样一个未知的脚本。事实上,我见过一些庞大的项目,其首个 README 文件甚至没说这个庞大而复杂的项目是做什么的!程序员只是想当然地认为,既然你下载了它,就一定知道它是干什么的!另外,确保像 '-?' 这样的 '错误选项' 不仅会打印选项的梗概,还会简短地概述该程序做什么,或到哪里去找这类帮助。别只是指向一个十年后可能就消失的远程网站!关于一个会打印自身 '开头注释' 的脚本示例,参见 IM scripts 区域里的 "jigsaw" 脚本。Perl 可以用 POD 做自我文档(参见 perl 的 "
Pod::Usage" 模块)。例如参见贡献的 dpx_timecode.pl 脚本。把帮助作为第一个子例程中的 'here 文件' 也是可以接受的,对大多数语言都适用。但要把那个子例程放在开头,而不是脚本或程序的末尾或中间。 - 确保把你的代码清理干净,删除陈旧过时的代码和注释,尽你所能让一切整洁有序。要简洁,(如有可能)用简单注释标出各阶段。同样参见 "jigsaw" 脚本。
- 确保临时文件在结束时被清理。用 "trap" shell 命令在文件退出或中断时删除它们。当然,你可以多次重用单个临时文件,所以并不需要很多,尤其是用 IM v6 的 magick 命令时。同样参见 "jigsaw" 脚本,并搜索 "trap"。
- 另外,并非人人都用系统 X,尽管在你看来也许如此。不要提及特定的系统要求,或纠正问题的方法。对你管用的东西,对他们的系统和配置也许完全不合适。他们甚至可能没有互联网访问!就只说 ImageMagick 的 "
magick" 命令没找到。如果你想加上安装要求或建议,请把它作为另一份更详尽文档的一部分加进去。 - 要检查所用的 IM 版本是否足够高,或者加入向后兼容的更改。正是为了这个目的,我在整个 IM 示例中特意标注了各种特殊功能是在何时加入的 '版本警告' 说明。这让创建脚本时只需一次最小的版本检查变得更容易。这里有一个简单的方法,可以在 shell 脚本中获取一个用于测试的单一版本号。它提取 4 个版本号,插入适当数量的零,使每个数都成为 2 位,从而生成一个简单的 8 位数字。
IM_VERSION=`magick -list configure | \ sed '/^LIB_VERSION_NUMBER /!d; s//,/; s/,/,0/g; s/,0*\([0-9][0-9]\)/\1/g'`
例如 IM v6.3.5-10 会生成 "06030510",而紧接着的下一个发行版 IM v6.3.6.0 会生成 "06030600"。上面的 PHP 版本可从 RubbleWeb, 字体列表 示例页获取。得到的字符串可以用简单的数值测试或字符串测试来检验,以判断可用的 ImageMagick 版本是否足够新,能满足你脚本想做的事情。例如……
if [ "$IM_VERSION" -lt '06030600' ]; then
echo >&2 "The perspective distortion operator is not available."
echo >&2 "Sorry your installed ImageMagick is too old -- ABORTING"
exit 10
fi
另外注意我是如何把中止的确切原因,特别是这次版本检查所针对的特殊功能,输出给用户的。否则你以后可能会忘记为什么需要那个特定版本(或更高)。你也可以针对特定版本修改 IM 的行为。例如,假设我想获取可用字体的列表。在 IM 版本 v6.3.5-7 之前,"[-list](https://imagemagick.org/command-line-options/#list)" 的设置 "type" 会返回 '已知字体' 列表。在之后的版本中,你需要改用 "font" 设置。所以这里我可以做一次版本检查,以使用正确的设置来获取所需信息。
if [ "$IM_VERSION" -lt '06030507' ];
then font_list="type";
else font_list="font";
fi
avail_fonts=`magick -list $font_list | cut -d\ -f1 |\
egrep -v '^($|----|Path:|Name$)'`
警告:PERL 中以 '0' 开头的字符串可能被解释为八进制数!!!不过,只要首位数字仍是 '0',比较两个八进制数结果依然正确。建议谨慎,并检查版本测试。测试版本的另一种替代做法,是用 "expr" 代替 "[" 测试……
if expr + "$im_version" \>= "06030507" >/dev/null; then
...
警告:上面多出的那个 '+' 通常并不需要,至少对这个测试是这样,但如果变量可能包含特殊关键字 'match" 就需要它,因为那会给 "expr" 带来问题,尤其是当它被用于字符串或子串处理时。
* 你也可以利用 "[-list](https://imagemagick.org/command-line-options/#list)" 的信息输出,来检查某个特殊功能是否已被加入当前安装的 ImageMagick。
magick -list distort | grep 'Arc' >/dev/null 2>&1
if [ "$?" -ne 0 ]; then
echo >&2 "Arc distortion method not available."
echo >&2 "Sorry your installed ImageMagick is too old -- ABORTING"
exit 10
fi
不过要警告,"[-distort](https://imagemagick.org/command-line-options/#distort) Arc" 这类新方法,常常会在其真正适合一般使用之前,就已在 IM 的开发过程中出现。因此版本检查也许仍是更好的主意。这就是为什么在 IM 示例中,我会(请留意各种符号)标注某个新功能稳定到足以供一般使用时的 IM 版本。
* 不要使用非常非常长的单行。尤其是复杂的 'convert' 命令。用续行方法(如上所示)把它们拆开,比如 UNIX 里的 '\'、DOS 里的 '^',以及 PHP 里的 '.' 字符串拼接。不过我并不是说把每一个设置和操作都放到单独一行。每行做一个主要的操作或阶段。创建新图像、修改图像、与其他图像合并,等等。把某个操作符所需的所有操作设置,放在那个操作符正前面。把每一行都当作单个处理步骤。这让你能把各行分开,从而更容易阅读和理解各个处理步骤。操作符分隔得越清楚,复杂的图像处理就越容易跟上。使用额外的 括号、在不同阶段对各行进行缩进,甚至在大的处理块之间加入空行,都能让又大又长的操作的主要阶段更容易看清。我在整个 IM 示例中到处使用这些技巧,好让例子更易于跟随和理解,所以尽管四处看看吧!最后,关于某条命令在做什么的额外注释,能对别人(甚至两个月后的你自己)阅读和理解你的脚本产生很大的不同。可惜你目前无法在一条长命令行中插入注释!
* 尽量不要依赖太多外部程序,或者只在它们可用时才使用(并提供可能的替代方案)。别人很可能没有那个程序,或者更愿意用别的东西。如果某个程序的使用可以是可选的,就让它可选。要么置于用户的控制之下,要么在找到时自动使用。只要可能,就尽量不要把它做成强制要求。例如,你可以利用 "pngcrush"、"optipng"、"pngnq" 来把 PNG 压缩得比 IM 通常提供的更好(IM 的设计是通用而非专用)。还有用于 GIF 动画 LZW 压缩优化的 "gifsicle"、"intergif" 等,各有优缺点。只是别把它做成脚本能工作的硬性要求。作为一个实际例子,旧版本的 "gif2anim" 没有用 ImageMagick 的 "magick identify" 来查找 GIF 特有的元数据,而是依赖一个打过补丁的 "[giftrans](http://www.ict.griffith.edu.au/anthony/software/#giftrans)" 版本。由于 ImageMagick "magick identify" 的改进,后来不再需要这个要求,于是我移除了它,让脚本能被更广泛地使用。ImageMagick 自身也有大量可选要求,比如用于读取 postscript 和 PDF 文档的 "ghostscript",或用于正确处理 SVG 矢量图像的 "librsvg"。当它们可用时,IM 会工作得很愉快。IM 把这些库视为可选,只在你想处理那些格式的图像时才需要它们。这里有一段代码片段,你可以用它来检查脚本的依赖(在非常精简的 cygwin 环境中尤其有用)……
# Check Dependencies to scripts correct working
DEPENDENCIES="sed awk grep tr bc magick identify" # adjust to suit
for i in $DEPENDENCIES; do
type $i >/dev/null 2>&1 ||
Usage "Required program dependency \"$i\" missing"
done
- 把上一点再推进一步。如果你在一个使用 IM 的 shell 脚本中需要浮点运算,你可以用 IM 自己来做这个运算,而不必依赖诸如 '
awk' 或 'bc' 这样(尤其在 windows 上的 cygwin 中可能不可用)的其他程序。例如,这里我们让 'magick' 来计算以度数给定的某个特定角度的 sin()……angle=-20 sine=`magick xc: -format "%[fx:sin( $angle *pi/180)]" info:` echo $sine
上面会输出值 '-0.34202'。你可以用 精度操作控制 来调整小数位数。默认设置为 6 位。
* 让用户来决定使用哪种输入/输出图像格式。ImageMagick 主要是一个图像转换器,能够使用许多不同的格式。它可以输出到屏幕、postscript、打印机,或把图像通过管道送入另一个命令做进一步处理。不要把用户限制在某种特定格式。例如 "jigsaw" 和 "gif_anim_montage" 脚本允许用户指定任意的输入或输出图像。这样用户就能把图像通过管道送进或送出该脚本,从而允许用其他程序和脚本做进一步处理。例如,我常常用类似这样的命令……
gif_anim_montage animation.miff show:
以便把脚本的结果显示到我的显示屏上,而不是保存到文件。事实上,在我的很多脚本里,如果输出缺失,就默认使用 "show:"。我没有把输入限制为只能来自 GIF 动画文件,也没有把输出限制为只能是 GIF、PNG 或 JPEG 图像格式,而是让脚本能读取和处理 ImageMagick 所能处理的任何格式。事实上,IM 可以从文件、管道、当前显示,甚至用 "URL:" 或 "HTTP:" 输入格式从万维网读取。除非这会带来安全隐患(比如用于 web 时),否则不要限制这些可能性。
* 读取输入图像、输出多重图像,都只做一次!如果用户提供了一个管道文件名或一个 URL,你就不应试图把那些图像读取一次以上,否则可能出问题。当你需要多次引用某张图像时,请使用临时文件、克隆的图像,或用 "[MPC:](files.html#mpc)" 保存输入图像的副本。如果你能处理来自管道的多重图像,那就更好。同样参见 "jigsaw" 脚本,那是一个当你被迫多次、或以与程序参数所暗示的不同顺序处理输入图像时,把输入图像保存到临时文件的例子。
这些从根本上给了使用你程序的用户更多自由,去做 '他们' 想做的事,而不是 '你' 认为他们想做的事。不要通过对脚本将被用于何处做假设,来限制他们或你自己。附注:我的主要专长是 UNIX 脚本编写,跨越许多不同的架构以及各种 'flavor' 的 UNIX、LINUX 和其他类 UNIX 系统,背后有 25 年以上的经验。关于上述内容,我应该知道自己在说什么。
为什么要使用多个 "magick" 命令
- Willem 于 2006 年 10 月 25 日(周三)写道……
- 我一直在想;有时我看到在你的例子里,你为了得到想要的结果不止一次地调用 Convert。一般来说,我会觉得不需要不止一次地调用 magick;应该全都能在 1 次调用中完成(不过命令会更复杂)。你同意这个说法吗?
我完全同意。不过在 IM 版本 6 之前,那实际上是不可能的,因为当时的 IM 并未设计成一条命令能做多于一个、或至多两个操作。但 IMv7 应当允许你在单条命令中完成所有处理。可惜即便如此也并非总是可能。我出于若干可能的原因使用多条命令。通常在示例页面里,我这样做是为了能显示中间图像的结果,以便更好地演示所涉及的中间处理阶段。在同一示例区域稍后,我也许会重复这个过程,但用单条命令,也许还稍微复杂一些。所以原则上,是的,单条命令可以完成所有图像处理。你完全可以把图像处理技术全部组合进单条命令。我自己一直这么做。这个例外出现在我需要提取信息、随后把该信息插入下一条命令的情形。这方面的一个例子是 模糊裁切 技术,它要求你对图像的一个模糊副本提取裁切的结果。这个结果随后被用来裁剪原始图像。我在更新 缩略图圆角 示例时也这样做过,那里我用 IM 自己、借助一张图像的尺寸来为下一条命令生成一个 draw 命令。不过,有一些 提案 将允许直接从先前已读入内存的图像生成选项。在诸如 'jigsaw' 脚本(见高级技术,拼图块)这样的脚本中,我常常出于另一个原因——可选处理——而使用多条命令。这让用户提供的各种输入选项,能在图像处理序列中选择额外的步骤。所以对于可选处理,我通常也为每个处理阶段使用分开的命令。在这种情况下,临时文件基本上无法避免。不过我通常至多只需要一两张临时图像,每个步骤都把图像处理回同一个或前一个临时文件名,供下一个可选处理步骤继续。例如,在替换源图像的同时处理图像。
magick /tmp/image1.png ..operations.. /tmp/image1.png
在这种情况下,MPC 文件可以让下一处理步骤对中间文件的读取几乎瞬间完成,因为图像只是从内存转储到磁盘,然后由下一条命令 '换页' 读回。这避免了 IM 去格式化和解析某种图像文件格式,不过它确实会让临时文件更大,因为那只是未压缩的内存。我用来避免临时文件的另一种替代做法,是把处理中的图像从一条 shell 命令(if-then-else-fi 或 while 循环)管道化到另一条。这被称为图像管道,在一些示例中有演示。这方面的一个例子见 MIFF 图像流,其中多张图像被一个接一个地生成到同一个输出管道,供下一条命令拾取并把它们全部合并成最终图像。最后,你可能需要根据先前处理步骤的结果来改变你的处理方式。例如在图像比较中,我常常需要发现某些信息以供后续处理步骤使用,或改变图像在后续阶段应如何处理。比较一张图表或卡通,可能需要与真实照片图像相当不同的比较技术。如果多条命令的使用开始成为问题,也许就是该转向诸如 PerlMagick 这样的 API 接口的时候了,在那里多个图像序列可以全部保存在内存中,从而避免不必要的磁盘 IO。
让 IM 更快(总体而言)
让 IM 工作得更快的方法有很多。这里是一些需要牢记的最重要方面。随着你往列表下方走,速度提升会变得更小,或需要对 IM 安装做更复杂的更改。
- IM Q8(即每个颜色值 8 位,每像素 3 到 4 字节)比默认的、具有更高颜色分辨率的 IM Q16 快很多(快 3 到 4 倍)。如果你的图像不需要 Q16,也许你应该把你的 IM 换成 Q8 版本。但要注意,只使用 8 位的内部质量会影响整体图像处理,因为中间图像会丢失信息。关于其反面,参见 HDRI。
- 如有可能,使用单条 "
magick" 命令,完成对单张图像所需的全部处理。这样能省去初始化、创建临时文件或命令间管道,甚至省去为那些管道所做的图像文件格式编码/解码以及磁盘 I/O。当然,有时你仍需使用多条命令,以支持可选的图像处理步骤,比如涉及图像尺寸、颜色的计算,或者可选的处理步骤。IMv7 的脚本编写会在这方面有所帮助。 - shell 脚本本质上很慢。它是解释执行的,需要多个步骤和额外的磁盘文件处理。当然,多亏了新的 IM v6 选项处理,这一点已有所改善,它允许你在单条命令中完成大量图像处理操作。即便如此,你也很少能在单条 "
magick" 命令中完成所有事情,所以你常常不得不使用多条命令来达成所愿。因此,像 perl、ruby,或 PHP 的 magick 模块这样的 API 更快,因为它去除了 shell 和 IM 命令行 API 两者的解释环节。它还减少了 IM 在读取字体和颜色定义时所经历的初始化步骤。 - API 还能(只要你有足够内存)在程序的整个生命周期内保存所有图像,甚至多个图像列表。这意味着你可以随意改变正在处理哪些图像,而无需像在命令行上那样对图像进行洗牌和腾挪。当你还需要基于先前图像处理步骤做额外计算时,这尤其有用。
- 写入 GIF 图像文件格式很慢。IM 必须努力地把图像的颜色削减(量化)到该文件格式有限的颜色内。即便如此,你也常常需要做额外的工作才能做对,尤其是对 GIF 动画。PNG 和 JPEG 更快,但代价是 PNG 的尺寸和 JPEG 的质量损失。不过说实话,就质量而言 GIF 图像其实更差!
- 预先准备和缓存图像,比如背景、叠加层、边框、蒙版,或者预先生成颜色查找表、扭曲映射、模板、蒙版等。所有这些都能对你的处理时间产生很大的不同。想一想有什么能提前做好。一个庞大的预生成图像库,可能比试图按需创建图像要快得多。也请参见把 "MPC:" 用于中间图像和缓存图像。这些是磁盘上的内存映射图像,本质上读取时间为零,但对其他用途或在其他机器上毫无用处。它们只应在一次主要的图像处理过程开始时创建,而不应长期保存,因为软件或系统哪怕最微小的升级都会使它们失效,并很可能导致段错误。
- 如果你能改用 Alpha 合成,或更简单的 Evaluate, 简单数学运算,或其他技术,就避免使用 FX, 特效图像操作符。如果你确实需要使用它们,请尽量把其使用限制在尽可能小的图像上,或(处理灰度时)限制在图像的单个通道上。
- 当若干次较小的模糊也许更快时,就避免使用大尺寸的 图像模糊。做一些计时测试,看看哪个更快。同样的道理也适用于诸如高斯和阴影这类其他 形态学 和 卷积操作符。
- 对小区域的复杂处理,使用更小的子图像或区域。例如,(用区域)找到并提取一个人的眼睛,然后再蒙版和重新着色,能比处理图像的整个大版本带来巨大的速度提升。差异越大,节省越多。
- 在读取大图像、甚至大量图像时,最好使用 读取修饰符,在读取每张图像后立即对其调整尺寸或裁剪,从而减少其整体内存需求。对于 JPEG,你可以使用特殊的 '
[jpeg:size](formats.html#jpg_read)' 库修饰符,甚至避免内存的分配。这反过来能防止 '磁盘颠簸'(它会让计算机变得非常慢)。尤其是在涉及许多大图像时,比如生成 蒙太奇目录索引 或其他多图像拼贴时。 - 对于必须从磁盘处理的 真正巨大的图像,把它们分成更小的块来处理也许更好。
- 同样对于大图像…… 如果你有 Windows 64 位操作系统,就使用 64 位的 ImageMagick 发行版。它使用更大的地址空间,能把比 32 位 Windows 更大的图像装入内存。
- IM 默认对单个图像处理操作使用多个线程。这意味着一台有两个或更多 '核' 的计算机,一般会比单 CPU 机器更快地处理图像。对于大图像,OpenMP 多线程能力能带来明确的速度优势,因为它使用更多 CPU 来完成单个图像处理操作。注意,在 IM 内部,只有单个图像处理操作会被并行化。所以,节省更多体现在大图像处理上,而不是在处理大量图像时(见下条)。
- 对于小图像,使用 IM 的多线程能力不会给你带来多少优势。这种情况下,对不同图像同时运行多个 convert 能带来更高的吞吐量。这在多个 PHP web 请求可能启动多个图像 "magick" 命令的情形下也会发生。在上述任一情形中,启用多线程都可能因 CPU 争用而极为有害,最好通过把 '
MAGICK_THREAD_LIMIT' 环境变量设为 '1' 来禁用 OpenMP。参见 IM 论坛讨论 线程拖慢 'convert'。你也许还想看看MAGICK_THROTTLE,让 ImageMagick 在更合适的时点更频繁地交出对 CPU 的控制权。 - 如果你在对图像做大量小操作(比如绘图),尽量不要使用任何颜色名。用哈希颜色(比如 "
#00AA99")或 rgb 数值(比如 "rgb(0,160,100)")来指定颜色,以免 IM 不得不加载颜色名表(它相当大!)。你也可以尝试删除或精简系统的 '字体' 列表定义文件(来自 "type.xml")。或者把那些文件完全删除,改为直接按文件名指定字体。基本上,就是减少那些 IM 在某个特定图像处理需要时才会读取和初始化的额外配置信息的加载。所以,要么别用它们,要么减小配置文件的大小和影响。 - 把 ImageMagick 构建为共享库(默认)能大大减少加载时间。库和编码器模块只在需要时才加载,所以动态版本的 IM 在图像处理期间不会加载任何它不需要用到的东西。此外,共享库往往会保持可用,所以第二次运行时也许无需重新加载。
- 如果你把 ImageMagick 作为 Apache 模块的一部分来调用,这也能减少启动时间,因为一部分会被加载一次并保持可用以供多次使用,而不必一遍又一遍地重新加载。随着将来出现一个常驻运行的 'daemon' IM 进程,这也许会变得更实用。
从源码编译 ImageMagick
从 SRPM 为 linux 构建 ImageMagick RPM
实际构建 RPM 并 不 需要 root,不过安装 RPM 需要 root。我用这个方法在 Fedora Linux 系统 下生成并安装 IM,但据 报告 它在 CentOS 5.4(Enterprise Redhat)Linux 系统 上也能用(更多针对 CentOS 上 IM 的具体说明 参见此处)。首先从 Linux 源 RPM 获取最新的源 RPM 发行版。首先确保你的机器拥有它所需的全部编译器和工具。
sudo yum groupinstall "Development Tools"
sudo yum install rpmdevtool libtool-ltdl-devel
| "sudo" 是一个在你被允许时以 root 身份运行命令的程序;否则请使用 root shell,并从上面移除 "sudo" 部分。
---|---
对于像 CentOS 5.5 这样的旧系统,似乎你还需要这些包
sudo yum compat-libstdc++ gcc-c++ gcc-objc++ libstdc++ libstdc++-devel
接下来,你还应安装 IM 构建时所需库的开发包。获取最常见那些包的简单方法,是先安装开发版的 IM,尽管我们稍后会构建一个新的来替换它。
sudo yum install ImageMagick-devel
你还应确保这些包及其依赖(比如 jpeg 和 png 的开发库)也已安装:
freetype-devel ghostscript-devel libwmf-devel jasper-devel lcms-devel bzip2-devel librsvg2 librsvg2-devel liblpr-1 liblqr-1-devel libtool-ltdl-devel autotrace-devel
"ImageMagick examples" 中的一些示例可能也会用到这些可选包和库所提供的程序,但构建过程并不需要它们。
gnuplot autotrace
一般来说,所有这些包都是可选的,但如果没有安装,那么使用那些库的 '编码器' 和操作符也许不会被自动构建进去。例如,"liblqr" 模块是启用 液态缩放操作符 所必需的。现在,下载一个 SRPM(源 RPM)包,从中构建你的二进制 RPM。或者用现有的 TAR 或 SVN 下载,按如下方式构建一个 SRPM……
rm config.status config.log
nice ./configure
rm *.src.rpm
make srpm
注意,一旦你有了 SRPM,就可以构建用于安装的实际 RPM。
nice rpmbuild --nodeps --rebuild ImageMagick*.src.rpm
这会在你的主目录里创建一个 "rpmbuild" 子目录,在其中展开 SRPM 源码,并构建 IM 的编译好的包 RPM 版本。 |
在较旧版本的 Fedora 和 Redhat 上,这是在一般仅限 root 的 "/usr/src" 中完成的。不过你可以把这个目录改为归你所有或可写,这样无需完整的 root 访问权限就仍能进行构建。 |
|---|---|
| 现在,从构建目录获取刚构建好的 RPM。这只会抓取 ImageMagick 核心和 PerlMagick 这两个包,你也许想抓取不止这两个,不过那就看你了…… |
cp -p ~/rpmbuild/RPMS/*/ImageMagick-[6p]*.i[36]86.rpm .
清理并删除构建区域(包括那些可能已为你创建的)……
rm -rf /var/tmp/rpm-tmp.* ~/rpmbuild
现在你可以安装你构建的 RPM 包了。这需要你是 root(参见上面关于 "sudo" 命令的说明)……
sudo rpm -ihv --force --nodeps ImageMagick-*.i[36]86.rpm
"--nodeps" 通常是必需的,因为 Linux 系统上有时存在一些不寻常的依赖。要升级一个现有的安装,我一般这样做(以 root 身份)。
sudo rpm -Uhv --force --nodeps ImageMagick-*.i[36]86.rpm
如果你想更进一步,我建议你查看 IM 网站上的 高级 Unix 源码安装指南。
以后要删除 IM,你只需这样删除该包即可(同样以 root 身份)……
sudo rpm -e --nodeps ImageMagick\*
有时我只是想把 IM 的所有痕迹从系统中彻底清理干净、抹除。为此,我先用前面的命令从系统中删除实际的包(下面给出一个变体)。然后我运行下面的删除命令。注意:我对此不作任何保证,我会事先彻底检查这些命令,确保它们不会删除不该删除的东西。如果它遗漏了什么,或删除了不该删除的东西,请告诉我,以便我更新它。
rpm -e --nodeps `rpm -q ImageMagick ImageMagick-perl`
rpm -e --nodeps `rpm -q ImageMagick-devel`
rm -rf /usr/lib/ImageMagick-*
rm -rf /usr/lib/lib{Magick,Wand}*
rm -rf /usr/share/ImageMagick-*
rm -rf /usr/share/doc/ImageMagick-*
rm -rf /usr/include/{ImageMagick,Magick++,magick,wand}
rm -rf /usr/lib/perl5/site_perl/*/i386-linux-thread-multi/Image/Magick*
rm -rf /usr/lib/perl5/site_perl/*/i386-linux-thread-multi/auto/Image/Magick*
rm -rf /usr/share/man/man?/*Magick*
rm -f /usr/lib/pkgconfig/ImageMagick.pc
警告:其他包可能需要已安装的 IM,所以如果你删除它,我建议你立即对你的计算机系统进行包更新,这样它会再次安装你的 Linux 系统所提供的、原始的默认(通常相当旧)版本的 ImageMagick。这一般涉及使用一个 'GUI 软件更新' 包或 "yum upgrade" 命令。祝愉快。
在 Ubuntu 上从源码安装 ImageMagick
要获取构建 ImageMagick 所需的全部开发库,请使用下面的命令
sudo apt-get install imagemagick libmagick++-dev
"Shane" 写的一个网页介绍了如何 在 Ubuntu 8.04 上从源码安装 ImageMagick。我没试过,但它用 "make" 把 IM 直接安装到 "/usr/local"。它不生成安装用的 'DEB' 包,这不是一个理想的解决方案。如果有人知道如何为 Ubuntu 创建 'DEB' 包,请告诉我。也许可以用 Debian 打包入门。
在 MacOSX 上编译
在 MacOSX 上安装 ImageMagick 最简单的方法是使用 MacPorts。不过下面是一些指向为 MacOSX 编译的信息的指引。我从没用过它,所以不知道它是否管用或是否会有帮助。但请参见 不用 Fink 或 MacPorts 安装 ImageMagick 和 在 Snow Leopard 上安装 ImageMagick。上述内容是对 IM 用户论坛上的一次讨论 的转述。
编译 HDRI 版本的 IM
关于编译 HDRI 版本 IM 的信息,请参见 IM 主网站上的 在 ImageMagick 中启用 HDRI。另外,关于 Windows 和 Ubuntu Linux 的具体信息,请参见用户论坛上的 傅里叶变换发布讨论。
创建个人 ImageMagick
你并不总能享有对进行图像工作的机器的超级用户访问权限,而且往往那些拥有该权限的人并不想更新他们的 ImageMagick 安装。也许是出于包管理问题,或兼容性问题。如果你有命令行访问权限(比如通过 SSH),那也并非全无办法。你可以安装并使用一个个人版本的 ImageMagick。坏消息是,你仍然需要系统管理员来安装编译器和开发包(见 上文),但这些往往已经存在,所以并不总是问题。首先决定你想把自己的 IM 版本安装到哪个子目录。专用目录是最好的选择,因为这意味着你只需删除那整个目录就能移除你的安装。就我而言,我会安装到我主目录的 "apps/im" 子目录。
export MAGICK_HOME=$HOME/apps/im
现在,要安装一个个人版本,请下载、解包 ImageMagick 源码并切换到其中的目录。然后把它配置为一个 'uninstalled'(未安装)版本。
rm config.status config.log
nice ./configure --prefix=$MAGICK_HOME --disable-installed \
--enable-shared --disable-static --without-modules --with-x \
--without-perl --without-magick-plus-plus --disable-openmp \
--with-wmf --with-gslib --with-rsvg --with-xml \
CFLAGS=-Wextra \
;
nice make clean
nice make
nice make install
上面定义中重要的是带有 "--prefix" 的 "--disable-installed"。其余部分禁用你个人版 ImageMagick 中更为可选的部分的编译。你可以按喜好修改它们。现在,要使用你自己安装的版本而非通常的系统版本,你只需设置下面的环境变量。
export MAGICK_HOME=$HOME/apps/im
export PATH="$MAGICK_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$MAGICK_HOME/lib:$LD_LIBRARY_PATH"
现在,如果我输入
magick -version
我就能看到你刚安装的、更新的 IM 版本被默认使用。注意,用 "--disable-installed" 选项创建的 Imagemagick 需要设置变量 "$MAGICK_HOME"。另外两个环境变量确保我们使用个人版本,而不是可能同时安装的任何系统版本。
警告: 不要混用上面的变量。要么全部定义,要么不要以这种方式定义它们。你使用的 IM 可执行文件,也必须使用与构建该可执行文件时相同的库、编码器和配置文件。混用系统版本和你的个人版本很可能导致段错误和内存错误。
你可以在不重新编译的情况下移动你个人 IM 的位置,但你不仅需要修改上面的环境变量,还需要更改(或移除)你个人安装版本的 "delegate.xml" 文件中,"[show:](files.html#show)" 委托所使用的 "magick display" 程序的硬编码路径。关于 IM 这一方面的更多信息,参见 委托。
为了让我能方便地在系统安装版本和多个个人版本的 IM 之间切换,我通常根本不设置上面的变量。相反,我在调用特定版本的 IM 之前,调用一个设置那些变量的脚本。例如,我有一个用 HDRI 编译的个人版 IM,我只在 ImageMagick 示例中的特定示例上使用它。大多数图像工作我更愿意用非 HDRI 的系统安装版本,通常不想用这个版本。于是我在个人区域 "$HOME/apps/im_hdri" 安装了一个 'HDRI' 版本的 IM,并创建了一个我称为 "hdri" 的脚本,内容是……
#!/bin/sh
#
# hdri imagemagick_command....
#
# Run the HDRI version of imagemagick (or other personal installed IM)
#
# Where is the HDRI version of IM stored
export MAGICK_HOME=$HOME/apps/im_hdri
# Set the other two environment variables
export PATH="$MAGICK_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$MAGICK_HOME/lib:$LD_LIBRARY_PATH"
# Execute the HDRI version of the command
exec "$@"
现在,如果我输入……
hdri magick -version
我看到我运行了 HDRI 质量版本的 ImageMagick,但只在我需要时。如果我不在上面前面加 "hdri",那我就会运行通常的系统版本 IM。警告:如果脚本没找到你的个人版本 IM,它会悄无声息地退回使用系统版本。上面的 'version' 检查是一个重要的测试,用来确认我实际使用的是个人版本,而不是系统版本。你也可以用 'which' 来确认脚本试图执行的究竟是哪个 magick 命令。
hdri which convert
也就是说,这个脚本足够灵活,你实际上不必运行 "magick",而是可以运行任何命令,比如 ImageMagick shell 脚本,从而让那个脚本使用 HDRI 的 magick,而不是通常的系统 convert。
hdri some_im_script image.png image_result_hdri.png
![[IM Text]](../static/img/api/hdri_version.txt.gif)