如何加速复杂的图像处理?
How to speed up a complex image processing?
每个用户都可以上传 100 张 TIFF(黑白)图像。
流程要求:
将 tif
转换为 jpg
。
将图像调整为 xx。
将图片裁剪为 200 像素。
添加文字水印。
这是我的 PHP 代码:
move_uploaded_file($image_temp,$destination_folder.$image_name);
$image_name_only = strtolower($image_info["filename"]);
$name=$destination_folder.$image_name_only.".jpg";
$thumb=$destination_folder."thumb_".$image_name_only.".jpg";
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$destination_folder.$image_name. ' '.$name.' 2>&1';
exec($exec, $exec_output, $exec_retval);
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$name. ' -resize 1024x '.$name;
exec($exec, $exec_output, $exec_retval);
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$name. ' -thumbnail 200x200! '.$thumb;
exec($exec, $exec_output, $exec_retval);
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$name. " -background White label:ش.پ12355 -append ".$name;
exec($exec, $exec_output, $exec_retval);
此代码有效。但每张图像的平均处理时间为 1 秒。
所以对于 100 张图像,它可能需要大约 100 秒。
如何加快整个过程(转换、调整大小、裁剪、水印)?
编辑
I have a Server G8:Ram:32G,CPU:Intel Xeon E5-2650(4 Process)
version:ImageMagick 6.9.0-3 Q16 x64
FEATURES:OpenMP
convert logo: -resize 500% -bench 10 1.png
Performance[1]: 10i 0.770ips 1.000e 28.735u 0:12.992
Performance[2]: 10i 0.893ips 0.537e 26.848u 0:11.198
Performance[3]: 10i 0.851ips 0.525e 27.285u 0:11.756
Performance[4]: 10i 0.914ips 0.543e 26.489u 0:10.941
Performance[5]: 10i 0.967ips 0.557e 25.803u 0:10.341
Performance[6]: 10i 0.797ips 0.509e 27.737u 0:12.554
Performance[7]: 10i 0.963ips 0.556e 25.912u 0:10.389
Performance[8]: 10i 0.863ips 0.529e 26.707u 0:11.586
资源限制:
Width: 100MP;Height: 100MP;Area: 17.16GP;Memory: 7.9908GiB;Map: 15.982GiB;Disk: unlimited;File: 1536;Thread: 8;Throttle: 0;Time: unlimited
0。两种方法
基本上,可以通过两种不同的方式或结合使用两种方式来应对这一挑战:
- 尽可能聪明地构造你的命令。
- 以 speed-up 换取质量损失。
接下来的几节将讨论这两种方法。
1。检查您拥有的是哪个 ImageMagick:'Q8'、'Q16'、'Q32' 或 'Q64'?
首先,检查您的确切 ImageMagick 版本和 运行:
convert -version
如果您的 ImageMagick 在其版本字符串中有 Q16
(甚至 Q32
或 Q64
,这是可能的,但太过分了!):
这意味着,所有 ImageMagick 的内部函数都将所有图像视为具有 16 位(或 32 或 64 位)通道深度。
这为您提供了更好的图像处理质量。
但与 Q8
相比,它还需要双倍内存。
所以同时也意味着性能下降。
因此:您可以测试通过切换到 Q8
-build 可以获得哪些性能优势。
(Q
是 ImageMagick 构建支持的 'quantum depth' 的符号。)
尽管如此,您将付出可能的 Q8
性能提升和质量损失。
只需检查 Q8
相对于 Q16
获得的速度,以及您遭受的质量损失。
然后决定你是否可以忍受这些缺点...
在任何情况下,Q16
将使用两倍于每个图像的 RAM 来处理,并且 Q32
将再次使用两倍于 Q16
的内存量。
这与输入文件中看到的实际 bits-per-pixels 无关。
16 位图像文件在保存时也会比 8 位图像文件占用更多磁盘 space。
Q16
或 Q32
需要更多内存,您始终必须确保有足够的内存。
因为 超过 你的物理内存将是一个非常糟糕的消息。
如果更大的 Q
将进程交换到磁盘,性能将直线下降。
1074 x 768
像素图像 (width x height
) 将需要以下数量的虚拟内存,具体取决于量子深度:
Quantum Virtual Memory
Depth (consumed by 1 image 1024x768)
------- ------------------------------
8 3.840 kiB (=~ 3,75 MiB)
16 7.680 kiB (=~ 7,50 MiB)
32 15.360 kiB (=~ 14,00 MiB)
另外请记住,一些 'optimized' 处理管道(见下文)将需要在虚拟内存中保留图像的多个副本!
一旦可用 RAM 无法满足虚拟内存,系统将开始交换并从磁盘中获取 "memory"。
在那种情况下,所有聪明的命令管道优化当然都消失了,并开始倒过来。
ImageMagick 的生日是在 CPUs 一次只能处理 1 位的时代。
那是几十年前的事了。
从那时起 CPU 体系结构发生了很大变化。
过去,16 位运算花费的时间是 8 位运算的两倍,甚至更长。
然后 16 位处理器出现了。
16 位运算成为标准。
CPUs 针对 16 位进行了优化:
突然之间,某些 8 位操作可能比 16 位操作花费的时间更长。
如今,64 位 CPU 很常见。
因此,Q8
vs. Q16
vs. Q32
的实际争论甚至可能是无效的。
谁知道?
我不知道有任何严肃的基准测试。
如果有人(对 CPUs 和对现实世界程序进行基准测试有非常深入的了解)有一天会 运行 进行这样的项目,那将会很有趣。
是的,我看到你在 Windows 上使用 Q16
。
但为了完整起见,我还是想提一下......
以后会有其他用户阅读这个问题和给出的答案。
很有可能,因为您的输入 TIFF 仅为黑白,Q8
构建的图像质量输出对于您的工作流程来说已经足够好了。
(我只是不知道它是否也会更快:
这在很大程度上也取决于您运行正在使用的硬件资源...)
此外,如果您安装的运动支持HDRI
(高动态分辨率图像),这也可能会导致一些速度损失。
谁知道?
因此,使用配置选项 --disable-hdri --with-quantum-depth=8
构建 IM 可能会也可能不会导致速度提高。
从来没有人认真地测试过这个……
我们唯一知道的是:
这些选项会降低图像质量。
然而,大多数人甚至不会注意到这一点,除非他们仔细观察并进行直接 image-by-image 比较...
2。检查您的 ImageMagick 的功能
接下来,检查您的 ImageMagick 安装是否附带 OpenCL and/or OpenMP 支持:
convert -list configure | grep FEATURES
如果是这样(就像我的一样),您应该会看到类似这样的内容:
FEATURES DPC HDRI OpenCL OpenMP Modules
OpenCL(用于 C 计算 L 语言)利用 ImageMagick 的 并行计算 功能(如果 compiled-in)。
除了 CPU 图像处理操作之外,这还将使用您计算机的 GPU。
OpenMP(对于M ulti-P rocessing) 做类似的事情:
它允许 ImageMagick 在系统的所有核心上并行执行。
因此,如果您有一个 quad-core 系统并调整图像大小,则调整大小会发生在 4 个内核上(如果您有超线程,则甚至是 8 个)。
命令
convert -version
打印有关支持的功能的一些基本信息。
如果 OpenCL/OpenMP 可用,您将在输出中看到其中之一(或两者)。
如果两者中的 none 出现:
考虑获取最新版本的 ImageMagick,其中编译了 OpenCL and/or OpenMP 支持。
如果您自己从源代码构建包,请确保使用 OpenCL/OpenMP。
通过在 'configure' 步骤中包含适当的参数来执行此操作:
./configure [...other options-] --enable-openmp --enable-opencl
ImageMagick 关于 OpenMP 和 OpenCL 的文档在这里:
- Parallel Execution With OpenMP。
仔细阅读。
因为 OpenMP 不是灵丹妙药,并不是在所有情况下都有效...
- Parallel Execution With OpenCL。
与上述相同。
此外,并非所有 ImageMagick 操作都是 OpenCL-enabled。
link 这里有一个列表。
-resize
就是其中之一。
从源构建 ImageMagick 和配置构建的提示和说明,解释各种选项,位于此处:
此页面还包含对 --with-quantum-depth
配置选项的简短讨论。
3。对您的 ImageMagick 进行基准测试
您现在还可以使用内置 -bench
选项使 ImageMagick 运行 成为您命令的基准。
例如:
convert logo: -resize 500% -bench 10 logo.png
[....]
Performance[4]: 10i 1.489ips 1.000e 6.420u 0:06.510
上面带有 -resize 500%
的命令告诉 ImageMagick 运行 convert
命令并在每个方向上将 built-in IM logo:
图像缩放 500%。
-bench 10
部分告诉它 运行 循环执行相同的命令 10 次,然后打印性能结果:
- 因为我启用了 OpenMP,所以我有 4 个线程 (
Performance[4]:
)。
- 它报告它 运行 10 次迭代 (
10i
)。
- 速度接近每秒 1.5 次迭代 (
1.489ips
)。
- 总 user-alotted 时间为 6.420 秒。
如果您的结果包含 Performance[1]:
,并且只有一行,那么您的系统没有启用 OpenMP。
(你 可能 能够打开它,如果你的构建确实支持它:运行 convert -limit thread 2
。)
4。调整 ImageMagick 的资源限制
了解您系统的 ImageMagick 是如何根据 资源限制 设置的。
使用这个命令:
identify -list resource
File Area Memory Map Disk Thread Time
--------------------------------------------------------------------
384 8.590GB 4GiB 8GiB unlimited 4 unlimited
上面显示了我当前系统的设置(不是默认设置——我过去确实调整过它们)。
这些数字是 ImageMagick 将使用的每种资源的最大数量。
您可以使用 headers 列中的每个关键字来优化您的系统。
为此,请使用 convert -limit <resource> <number>
将其 设置 为新的限制。
也许你的结果看起来更像这样:
identify -list resource
File Area Memory Map Disk Thread Time
--------------------------------------------------------------------
192 4.295GB 2GiB 4GiB unlimited 1 unlimited
files
定义了 ImageMagick 可以同时打开的最大文件数。
memory
、map
、area
和 disk
资源限制以字节为单位定义。
对于 设置 它们为不同的值,您可以使用 SI 前缀,例如 500MB)。
当您执行 OpenMP for ImageMagick 在您的系统上,您可以运行.
convert -limit thread 2
这将启用 2 个并行线程作为第一步。
然后 re-run 基准测试,看看它是否真的有所作为,如果有的话有多大。
之后你可以将限制设置为 4 甚至 8 并重复练习....
5。使用 Magick 像素缓存 (MPC) and/or Magick 持久注册表 (MPR)
最后,你可以尝试ImageMagick像素缓存的一种特殊内部格式。
这种格式称为 MPC
(Magick 像素缓存)。
它只存在于内存中。
创建 MPC 时,处理后的输入图像以未压缩的光栅格式保存在 RAM 中。
所以基本上,MPC 是 ImageMagick 的原生 in-memory 未压缩文件格式。
它只是将内存直接转储到磁盘。
读取是根据需要从磁盘到内存的快速内存映射(类似于内存页面交换)。
但是不需要图像解码。
(更多技术细节:MPC 作为一种格式不可移植。
它也不适合作为 long-term 存档格式。
它唯一适合作为 high-performance 图像处理的中间格式。
一张图片需要两个文件。)
如果您仍想将此格式保存到磁盘,请注意:
- 图像属性被写入扩展名为.mpc.
的文件
- 图像像素被写入扩展名为.cache.
的文件
它的主要优势体现在...
- ...处理非常大的图像,或者当
- ...对 "opertion pipelines".
中的同一张图像应用多项操作
MPC 专为符合条件 "read many times, write once".
的工作流模式而设计
有人说这样的操作性能会提高,但我没有亲身体验。
首先将您的基本图片转换为 MPC:
convert input.jpeg input.mpc
然后才运行:
convert input.mpc [...your long-long-long list of crops and operations...]
然后看看这是否能显着节省您的时间。
很可能你甚至可以使用这种 MPC 格式 "inline"(使用特殊的 mpc:
符号,见下文)。
MPR 格式(内存持久寄存器)做类似的事情。
它将图像读入指定的内存寄存器。
如果需要多次访问,您的流程管道还可以从该寄存器再次读取图像。
该图像保留在当前命令管道退出的寄存器中。
但我从未将此技术应用到现实世界的问题中,所以我不能说它在现实生活中的效果如何。
6.构建合适的 IM 处理管道,一次性完成所有任务
如您所描述的过程,它由 4 个不同的步骤组成:
- 将 TIFF 转换为 JPEG。
- 将 JPEG 图像调整为 xx(??什么值??)
- 将 JPEG 裁剪为 200 像素。
- 添加文字水印。
请通过阅读您的代码片段判断我是否理解正确:
- 您有 1 个输入文件,一个 TIFF。
- 您需要 2 个最终输出文件:
- 1 张 JPEG 缩略图,大小为 200x200 像素;
- 1个标签为JPEG,宽度为1024像素(高度保持输入TIFF的纵横比);
- 1(未标记)JPEG 只是一个您不想保留的中间文件。
基本上,每个步骤都使用自己的命令 -- 总共 4 个不同的命令。
这可以通过使用单独执行所有步骤的单个命令管道来大大加快。
此外,您似乎并不真的需要 保留 未标记的 JPEG 作为最终结果——但是您将其生成为中间临时文件的一个命令将其保存到磁盘.那么我们可以尝试完全跳过这一步,并尝试在没有额外写入磁盘的情况下获得最终结果。
可以采用不同的方法来进行此更改。
我现在只向您(和其他读者)展示一个——而且只针对 CLI,而不是 PHP。
我不是 PHP 人——将我的 CLI 方法 'translate' 转化为适当的 PHP 调用是你自己的工作。
(但无论如何:请先使用我的命令进行测试,真正使用 CLI,看看在 运行 将方法设置为 PHP!)
但请首先确保您真正了解更复杂的ImageMagick命令行的架构和结构!
这个目标可以参考这个我的另一个回答:
您的 4 个步骤 运行 变成了以下单独的 ImageMagick 命令:
convert image.tiff image.jpg
convert image.jpg -resize 1024x image-1024.jpg
convert image-1024.jpg -thumbnail 200x200 image-thumb.jpg
convert -background white image-1024.jpg label:12345 -append image-labelled.jpg
现在 运行将此工作流程整合为一个管道命令...
以下命令执行此操作。
它应该执行得更快(无论按照我的上述步骤 0.--4. 得到的结果如何):
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY +write image-1024.jpg \) \
\( mpr:XY -thumbnail 200x200 +write image-thumb.jpg \) \
\( mpr:XY -background white label:12345 -append +write image-labelled.jpg \) \
null:
说明:
-respect-parentheses
:
需要真正使 相互独立 sub-commands 在 \( .... \)
括号内执行。
+write mpr:XY
:
用于将输入文件写入 MPR 存储寄存器。
XY
只是一个标签(你可以使用任何东西),稍后需要re-call相同的图像。
+write image-1024.jpg
:
将第一对括号内执行的子命令的结果写入磁盘。
+write image-thumb.jpg
:
将在第二对括号内执行的子命令的结果写入磁盘。
+write image-labelled.jpg
:
将在第三对括号内执行的子命令的结果写入磁盘。
null:
:
终止命令管道。
必需,因为否则我们将以最后一个子命令的右括号结束。
7。将 4 个单独的命令与单个管道进行基准测试
为了大致了解我的建议,我执行了 运行 下面的命令。
第一个 运行s 4 个单独命令的序列 100 次(并以不同的文件名保存所有结果图像)。
time for i in $(seq -w 1 100); do
convert image.tiff \
image-indiv-run-${i}.jpg
convert image-indiv-run-${i}.jpg -sample 1024x \
image-1024-indiv-run-${i}.jpg
convert image-1024-indiv-run-${i}.jpg -thumbnail 200x200 \
image-thumb-indiv-run-${i}.jpg
convert -background white image-1024-indiv-run-${i}.jpg label:12345 -append \
image-labelled-indiv-run-${i}.jpg
echo "DONE: run indiv $i ..."
done
我对 4 个命令(重复 100 次!)的结果是:
real 0m49.165s
user 0m39.004s
sys 0m6.661s
第二个命令对单个流水线进行计时:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY -resize 1024x \
+write image-1024-pipel-run-${i}.jpg \) \
\( mpr:XY -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( mpr:XY -resize 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
单管道的结果(重复100次!)是这样的:
real 0m29.128s
user 0m28.450s
sys 0m2.897s
如您所见,单个管道比 4 个单独的命令快约 40%!
现在您还可以投资 multi-CPU、大量 RAM、快速 SSD 硬件来进一步加快速度:-)
但首先要运行将此 CLI 方法写入 PHP 代码...
关于这个话题还有一些话要说。
但我的时间 运行 现在已经结束了。
几天后我可能会 return 这个答案并更新它...
更新: 我不得不用基准测试的新数字更新这个答案:
最初我忘记了将 -resize 1024x
操作(愚蠢的我!)包含到流水线版本中。
包含它后,性能提升仍然存在,但不再那么大了。
8。使用-clone 0
复制内存中的图像
这里有另一种替代方法,可以尝试使用上面建议的命名内存寄存器代替 mpr:
方法。
它使用(再次在 'side processing inside parentheses' 中)-clone 0
操作。
它的工作方式是这样的:
convert
从磁盘读取一次输入 TIFF 并将其加载到内存中。
- 每个
-clone 0
运算符都会复制第一个加载的图像(因为它在当前图像堆栈中具有索引 0
)。
- 整个命令管道中的每个 "within-parenthesis" sub-pipeline 都对克隆执行一些操作。
- 每个
+write
操作将各自的结果保存到磁盘。
所以这里是基准测试的命令:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -resize 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
我的结果:
real 0m19.432s
user 0m18.214s
sys 0m1.897s
令我惊讶的是,这比使用 mpr:
!
的版本更快
9。使用 -scale
或 -sample
而不是 -resize
此替代方法很可能会加快您调整大小的速度 sub-operation。
但这可能会导致图像质量稍差(您必须验证,如果这种差异很明显)。
有关 -resize
、-sample
和 -scale
之间差异的一些背景信息,请参阅以下答案:
我也试过了:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -scale 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
我的结果:
real 0m16.551s
user 0m16.124s
sys 0m1.567s
这是迄今为止最快的结果(我将其与 +clone
变体相结合)。
当然,这种修改也可以应用于您的初始方法运行宁 4 个不同的命令。
10。通过在命令中添加 -depth 8
来模拟 Q8
构建。
我实际上并没有 运行 并测量这个,但完整的命令是。
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -scale 1024x -depth 8 \
-background white label:12345 -append \
+write d08-image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
此修改也适用于您最初的"I run 4 different commands"-方法。
11。按照 Mark Setchell
的建议,将其与 GNU parallel
相结合
如果您的整个工作流程允许这种并行化,这当然只对您适用且合理。
对于我的小基准测试来说,它是适用的。
对于您的网络服务,您可能一次只知道一项工作...
time for i in $(seq -w 1 100); do \
cat <<EOF
convert image.tiff \
\( -clone 0 -scale 1024x -depth 8 \
-background white label:12345 -append \
+write d08-image-labelled-pipel-run-${i}.jpg \) \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
EOF
done | parallel --will-cite
结果:
real 0m6.806s
user 0m37.582s
sys 0m6.642s
user
和real
时间明显的矛盾可以这样解释:
user
时间表示在 8 个不同的 CPU 核心上计时的所有时间滴答的总和。
从用户看手表的角度来看,速度要快得多:不到 10 秒。
12。摘要
选择您自己的喜好 -- 结合不同的方法:
通过构建更智能的命令管道可以获得一些加速(与当前图像质量相同) .
避免 运行ning 各种命令(其中每个 convert
都会导致一个新进程,并且必须从磁盘读取其输入)。
将所有图像处理打包到一个进程中。
使用 "parenthesized side processing"。
使用 -clone
或 mbr:
或 mbc:
或什至组合这些。
可以通过以性能换取图像质量额外获得一些加速:
您的一些选择是:
-depth 8
(必须在 OP 系统上声明)与 -depth 16
(OP 系统上的默认值)
-resize 1024
对比 -sample 1024x
对比 -scale 1024x
如果您的工作流程允许,请使用 GNU parallel
。
一如既往,@KurtPfeifle 提供了一个非常有理由和解释的答案,他所说的一切都是可靠的建议,您最好仔细聆听并遵循。
虽然还有更多可以做的事情,但是我不能作为评论添加,所以我把它作为另一个答案,虽然它只是对 Kurt 的增强...
我不知道 Kurt 使用的输入图像大小,所以我制作了一张 3000x2000 并将我的 运行 时间与他的进行比较,看它们是否具有可比性,因为我们有不同的硬件。在我的机器上,单个命令 运行 需要 42 秒,流水线命令 运行 需要 36 秒,所以我想我的图像大小和硬件大致相似。
然后我使用 GNU Parallel 来 运行 并行作业 - 我认为您会在 Xeon 上从中受益匪浅。这是我所做的...
time for i in $(seq -w 1 100); do
cat <<EOF
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY -resize 1024x \
+write image-1024-pipel-run-${i}.jpg \) \
\( mpr:XY -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( mpr:XY -background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
EOF
done | parallel
如您所见,我所做的只是将需要 运行ning 的命令回显到 stdout 并将它们通过管道传输到 GNU Parallel。 运行 这样,在我的机器上只需要 10 秒。
我也曾尝试使用 ffmpeg
模仿功能,并想出了这个,这在我的测试图像上看起来非常相似 - 你的里程可能会有所不同。
#!/bin/bash
for i in $(seq -w 1 100); do
echo ffmpeg -y -loglevel panic -i image.tif ff-$i.jpg
echo ffmpeg -y -loglevel panic -i image.tif -vf scale=1024:682 ff-$i-1024.jpg
echo ffmpeg -y -loglevel panic -i image.tif -vf scale=200:200 ff-$i-200.jpg
done | parallel
在我的 iMac 上使用 3000x2000 image.tif 输入文件 运行 只需 7 秒。
我在 homebrew
下安装 ImageMagick libturbo-jpeg
失败得很惨。
我一直听到一些人说 GraphicsMagick(几年前的一个分支,从 ImageMagick 分支出来)比 ImageMagick 快得多。
所以我借此机会试了一下。因此我的第二个答案。
我执行了 运行 以下 4 个单独的 gm
命令循环。这使得结果与我的其他答案中记录的 4 个单独的 convert
命令相当。它发生在同一台机器上:
time for i in $(seq -w 1 100); do
gm convert image.tiff gm-${i}-image.jpg
gm convert gm-${i}-image.jpg -resize 1024x gm-${i}-image-1024.jpg
gm convert gm-${i}-image-1024.jpg -thumbnail 200x200 gm-${i}-image-thumb.jpg
gm convert -background white \
gm-${i}-image-1024.jpg label:12345 -append gm-${i}-image-labelled.jpg
echo "GraphicsMagick run no. $i ..."
done
结果次数:
real 1m4.225s
user 0m51.577s
sys 0m8.247s
这意味着:对于这个特定的工作,在这台机器上,我的 Q8
GraphicsMagick(版本是 1.3.20 2014-08-16 Q8
)速度较慢 64 秒 比我的 Q16
ImageMagick(版本是 6.9.0-0 Q16 x86_64 2014-12-06
)需要 50 秒 每个 100 运行 秒。
当然,这个简短的测试及其结果绝不是万无一失的声明。
您可能会问:这台机器及其 OS 在进行每次测试时还做了什么?哪些其他应用程序同时加载到内存中? etc.pp,你是对的。 -- 但您现在可以自由地 运行 您自己的测试。您可以做的一件事是为两个测试提供几乎相同的条件:运行 它们同时在 2 个不同的终端 windows!)
我忍不住用 libvips 尝试这个基准测试。我使用了这两个脚本:
#!/bin/bash
convert image.tiff \
\( -clone 0 -scale 1024x -depth 8 label:12345 -append \
+write d08-image-labelled-pipel-run-.jpg \) \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-.jpg \) \
null:
和libvips using the Python interface:
#!/usr/bin/python3
import sys
import pyvips
image = pyvips.Image.thumbnail("image.tiff", 1024)
txt = pyvips.Image.text("12345")
image = image.join(txt, "vertical")
image.write_to_file(f"{sys.argv[1]}-labelled.jpg")
image = pyvips.Image.thumbnail("image.tiff", 200)
image.write_to_file(f"{sys.argv[1]}-thumb.jpg")
然后在带有 IM 6.9.10-86 和 vips 8.11 的 3000 x 2000 RGB tiff 图像上,两者都带有 libjpeg-turbo,我看到:
$ /usr/bin/time -f %M:%e parallel ../im-bench.sh ::: {1..100}
78288:1.83
$ VIPS_CONCURRENCY=1 /usr/bin/time -f %M:%e parallel ../vips-bench.py ::: {1..100}
59512:0.84
所以 libvips 的速度大约是原来的两倍,而且使用的内存更少。
(我最初是在 2015 年发布的。我在 2021 年使用当前 libvips 的代码对其进行了更新,并在现代 PC 上重新运行,因此与上面的原始版本相比,处理速度有了巨大的提高)
每个用户都可以上传 100 张 TIFF(黑白)图像。
流程要求:
将
tif
转换为jpg
。将图像调整为 xx。
将图片裁剪为 200 像素。
添加文字水印。
这是我的 PHP 代码:
move_uploaded_file($image_temp,$destination_folder.$image_name);
$image_name_only = strtolower($image_info["filename"]);
$name=$destination_folder.$image_name_only.".jpg";
$thumb=$destination_folder."thumb_".$image_name_only.".jpg";
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$destination_folder.$image_name. ' '.$name.' 2>&1';
exec($exec, $exec_output, $exec_retval);
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$name. ' -resize 1024x '.$name;
exec($exec, $exec_output, $exec_retval);
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$name. ' -thumbnail 200x200! '.$thumb;
exec($exec, $exec_output, $exec_retval);
$exec = '"C:\Program Files\ImageMagick-6.9.0-Q16\convert.exe" '.$name. " -background White label:ش.پ12355 -append ".$name;
exec($exec, $exec_output, $exec_retval);
此代码有效。但每张图像的平均处理时间为 1 秒。 所以对于 100 张图像,它可能需要大约 100 秒。
如何加快整个过程(转换、调整大小、裁剪、水印)?
编辑
I have a Server G8:Ram:32G,CPU:Intel Xeon E5-2650(4 Process)
version:ImageMagick 6.9.0-3 Q16 x64
FEATURES:OpenMP
convert logo: -resize 500% -bench 10 1.png
Performance[1]: 10i 0.770ips 1.000e 28.735u 0:12.992
Performance[2]: 10i 0.893ips 0.537e 26.848u 0:11.198
Performance[3]: 10i 0.851ips 0.525e 27.285u 0:11.756
Performance[4]: 10i 0.914ips 0.543e 26.489u 0:10.941
Performance[5]: 10i 0.967ips 0.557e 25.803u 0:10.341
Performance[6]: 10i 0.797ips 0.509e 27.737u 0:12.554
Performance[7]: 10i 0.963ips 0.556e 25.912u 0:10.389
Performance[8]: 10i 0.863ips 0.529e 26.707u 0:11.586
资源限制:
Width: 100MP;Height: 100MP;Area: 17.16GP;Memory: 7.9908GiB;Map: 15.982GiB;Disk: unlimited;File: 1536;Thread: 8;Throttle: 0;Time: unlimited
0。两种方法
基本上,可以通过两种不同的方式或结合使用两种方式来应对这一挑战:
- 尽可能聪明地构造你的命令。
- 以 speed-up 换取质量损失。
接下来的几节将讨论这两种方法。
1。检查您拥有的是哪个 ImageMagick:'Q8'、'Q16'、'Q32' 或 'Q64'?
首先,检查您的确切 ImageMagick 版本和 运行:
convert -version
如果您的 ImageMagick 在其版本字符串中有 Q16
(甚至 Q32
或 Q64
,这是可能的,但太过分了!):
这意味着,所有 ImageMagick 的内部函数都将所有图像视为具有 16 位(或 32 或 64 位)通道深度。
这为您提供了更好的图像处理质量。
但与 Q8
相比,它还需要双倍内存。
所以同时也意味着性能下降。
因此:您可以测试通过切换到 Q8
-build 可以获得哪些性能优势。
(Q
是 ImageMagick 构建支持的 'quantum depth' 的符号。)
尽管如此,您将付出可能的 Q8
性能提升和质量损失。
只需检查 Q8
相对于 Q16
获得的速度,以及您遭受的质量损失。
然后决定你是否可以忍受这些缺点...
在任何情况下,Q16
将使用两倍于每个图像的 RAM 来处理,并且 Q32
将再次使用两倍于 Q16
的内存量。
这与输入文件中看到的实际 bits-per-pixels 无关。
16 位图像文件在保存时也会比 8 位图像文件占用更多磁盘 space。
Q16
或 Q32
需要更多内存,您始终必须确保有足够的内存。
因为 超过 你的物理内存将是一个非常糟糕的消息。
如果更大的 Q
将进程交换到磁盘,性能将直线下降。
1074 x 768
像素图像 (width x height
) 将需要以下数量的虚拟内存,具体取决于量子深度:
Quantum Virtual Memory
Depth (consumed by 1 image 1024x768)
------- ------------------------------
8 3.840 kiB (=~ 3,75 MiB)
16 7.680 kiB (=~ 7,50 MiB)
32 15.360 kiB (=~ 14,00 MiB)
另外请记住,一些 'optimized' 处理管道(见下文)将需要在虚拟内存中保留图像的多个副本! 一旦可用 RAM 无法满足虚拟内存,系统将开始交换并从磁盘中获取 "memory"。 在那种情况下,所有聪明的命令管道优化当然都消失了,并开始倒过来。
ImageMagick 的生日是在 CPUs 一次只能处理 1 位的时代。 那是几十年前的事了。 从那时起 CPU 体系结构发生了很大变化。 过去,16 位运算花费的时间是 8 位运算的两倍,甚至更长。 然后 16 位处理器出现了。 16 位运算成为标准。 CPUs 针对 16 位进行了优化: 突然之间,某些 8 位操作可能比 16 位操作花费的时间更长。
如今,64 位 CPU 很常见。
因此,Q8
vs. Q16
vs. Q32
的实际争论甚至可能是无效的。
谁知道?
我不知道有任何严肃的基准测试。
如果有人(对 CPUs 和对现实世界程序进行基准测试有非常深入的了解)有一天会 运行 进行这样的项目,那将会很有趣。
是的,我看到你在 Windows 上使用 Q16
。
但为了完整起见,我还是想提一下......
以后会有其他用户阅读这个问题和给出的答案。
很有可能,因为您的输入 TIFF 仅为黑白,Q8
构建的图像质量输出对于您的工作流程来说已经足够好了。
(我只是不知道它是否也会更快:
这在很大程度上也取决于您运行正在使用的硬件资源...)
此外,如果您安装的运动支持HDRI
(高动态分辨率图像),这也可能会导致一些速度损失。
谁知道?
因此,使用配置选项 --disable-hdri --with-quantum-depth=8
构建 IM 可能会也可能不会导致速度提高。
从来没有人认真地测试过这个……
我们唯一知道的是:
这些选项会降低图像质量。
然而,大多数人甚至不会注意到这一点,除非他们仔细观察并进行直接 image-by-image 比较...
2。检查您的 ImageMagick 的功能
接下来,检查您的 ImageMagick 安装是否附带 OpenCL and/or OpenMP 支持:
convert -list configure | grep FEATURES
如果是这样(就像我的一样),您应该会看到类似这样的内容:
FEATURES DPC HDRI OpenCL OpenMP Modules
OpenCL(用于 C 计算 L 语言)利用 ImageMagick 的 并行计算 功能(如果 compiled-in)。 除了 CPU 图像处理操作之外,这还将使用您计算机的 GPU。
OpenMP(对于M ulti-P rocessing) 做类似的事情: 它允许 ImageMagick 在系统的所有核心上并行执行。 因此,如果您有一个 quad-core 系统并调整图像大小,则调整大小会发生在 4 个内核上(如果您有超线程,则甚至是 8 个)。
命令
convert -version
打印有关支持的功能的一些基本信息。 如果 OpenCL/OpenMP 可用,您将在输出中看到其中之一(或两者)。
如果两者中的 none 出现: 考虑获取最新版本的 ImageMagick,其中编译了 OpenCL and/or OpenMP 支持。
如果您自己从源代码构建包,请确保使用 OpenCL/OpenMP。 通过在 'configure' 步骤中包含适当的参数来执行此操作:
./configure [...other options-] --enable-openmp --enable-opencl
ImageMagick 关于 OpenMP 和 OpenCL 的文档在这里:
- Parallel Execution With OpenMP。 仔细阅读。 因为 OpenMP 不是灵丹妙药,并不是在所有情况下都有效...
- Parallel Execution With OpenCL。
与上述相同。
此外,并非所有 ImageMagick 操作都是 OpenCL-enabled。
link 这里有一个列表。
-resize
就是其中之一。
从源构建 ImageMagick 和配置构建的提示和说明,解释各种选项,位于此处:
此页面还包含对 --with-quantum-depth
配置选项的简短讨论。
3。对您的 ImageMagick 进行基准测试
您现在还可以使用内置 -bench
选项使 ImageMagick 运行 成为您命令的基准。
例如:
convert logo: -resize 500% -bench 10 logo.png
[....]
Performance[4]: 10i 1.489ips 1.000e 6.420u 0:06.510
上面带有 -resize 500%
的命令告诉 ImageMagick 运行 convert
命令并在每个方向上将 built-in IM logo:
图像缩放 500%。
-bench 10
部分告诉它 运行 循环执行相同的命令 10 次,然后打印性能结果:
- 因为我启用了 OpenMP,所以我有 4 个线程 (
Performance[4]:
)。 - 它报告它 运行 10 次迭代 (
10i
)。 - 速度接近每秒 1.5 次迭代 (
1.489ips
)。 - 总 user-alotted 时间为 6.420 秒。
如果您的结果包含 Performance[1]:
,并且只有一行,那么您的系统没有启用 OpenMP。
(你 可能 能够打开它,如果你的构建确实支持它:运行 convert -limit thread 2
。)
4。调整 ImageMagick 的资源限制
了解您系统的 ImageMagick 是如何根据 资源限制 设置的。 使用这个命令:
identify -list resource File Area Memory Map Disk Thread Time -------------------------------------------------------------------- 384 8.590GB 4GiB 8GiB unlimited 4 unlimited
上面显示了我当前系统的设置(不是默认设置——我过去确实调整过它们)。
这些数字是 ImageMagick 将使用的每种资源的最大数量。
您可以使用 headers 列中的每个关键字来优化您的系统。
为此,请使用 convert -limit <resource> <number>
将其 设置 为新的限制。
也许你的结果看起来更像这样:
identify -list resource File Area Memory Map Disk Thread Time -------------------------------------------------------------------- 192 4.295GB 2GiB 4GiB unlimited 1 unlimited
files
定义了 ImageMagick 可以同时打开的最大文件数。memory
、map
、area
和disk
资源限制以字节为单位定义。 对于 设置 它们为不同的值,您可以使用 SI 前缀,例如 500MB)。
当您执行 OpenMP for ImageMagick 在您的系统上,您可以运行.
convert -limit thread 2
这将启用 2 个并行线程作为第一步。 然后 re-run 基准测试,看看它是否真的有所作为,如果有的话有多大。 之后你可以将限制设置为 4 甚至 8 并重复练习....
5。使用 Magick 像素缓存 (MPC) and/or Magick 持久注册表 (MPR)
最后,你可以尝试ImageMagick像素缓存的一种特殊内部格式。
这种格式称为 MPC
(Magick 像素缓存)。
它只存在于内存中。
创建 MPC 时,处理后的输入图像以未压缩的光栅格式保存在 RAM 中。 所以基本上,MPC 是 ImageMagick 的原生 in-memory 未压缩文件格式。 它只是将内存直接转储到磁盘。 读取是根据需要从磁盘到内存的快速内存映射(类似于内存页面交换)。 但是不需要图像解码。
(更多技术细节:MPC 作为一种格式不可移植。 它也不适合作为 long-term 存档格式。 它唯一适合作为 high-performance 图像处理的中间格式。 一张图片需要两个文件。)
如果您仍想将此格式保存到磁盘,请注意:
- 图像属性被写入扩展名为.mpc. 的文件
- 图像像素被写入扩展名为.cache. 的文件
它的主要优势体现在...
- ...处理非常大的图像,或者当
- ...对 "opertion pipelines". 中的同一张图像应用多项操作
MPC 专为符合条件 "read many times, write once".
的工作流模式而设计有人说这样的操作性能会提高,但我没有亲身体验。
首先将您的基本图片转换为 MPC:
convert input.jpeg input.mpc
然后才运行:
convert input.mpc [...your long-long-long list of crops and operations...]
然后看看这是否能显着节省您的时间。
很可能你甚至可以使用这种 MPC 格式 "inline"(使用特殊的 mpc:
符号,见下文)。
MPR 格式(内存持久寄存器)做类似的事情。 它将图像读入指定的内存寄存器。 如果需要多次访问,您的流程管道还可以从该寄存器再次读取图像。 该图像保留在当前命令管道退出的寄存器中。
但我从未将此技术应用到现实世界的问题中,所以我不能说它在现实生活中的效果如何。
6.构建合适的 IM 处理管道,一次性完成所有任务
如您所描述的过程,它由 4 个不同的步骤组成:
- 将 TIFF 转换为 JPEG。
- 将 JPEG 图像调整为 xx(??什么值??)
- 将 JPEG 裁剪为 200 像素。
- 添加文字水印。
请通过阅读您的代码片段判断我是否理解正确:
- 您有 1 个输入文件,一个 TIFF。
- 您需要 2 个最终输出文件:
- 1 张 JPEG 缩略图,大小为 200x200 像素;
- 1个标签为JPEG,宽度为1024像素(高度保持输入TIFF的纵横比);
- 1(未标记)JPEG 只是一个您不想保留的中间文件。
基本上,每个步骤都使用自己的命令 -- 总共 4 个不同的命令。 这可以通过使用单独执行所有步骤的单个命令管道来大大加快。
此外,您似乎并不真的需要 保留 未标记的 JPEG 作为最终结果——但是您将其生成为中间临时文件的一个命令将其保存到磁盘.那么我们可以尝试完全跳过这一步,并尝试在没有额外写入磁盘的情况下获得最终结果。
可以采用不同的方法来进行此更改。 我现在只向您(和其他读者)展示一个——而且只针对 CLI,而不是 PHP。 我不是 PHP 人——将我的 CLI 方法 'translate' 转化为适当的 PHP 调用是你自己的工作。
(但无论如何:请先使用我的命令进行测试,真正使用 CLI,看看在 运行 将方法设置为 PHP!)
但请首先确保您真正了解更复杂的ImageMagick命令行的架构和结构! 这个目标可以参考这个我的另一个回答:
您的 4 个步骤 运行 变成了以下单独的 ImageMagick 命令:
convert image.tiff image.jpg
convert image.jpg -resize 1024x image-1024.jpg
convert image-1024.jpg -thumbnail 200x200 image-thumb.jpg
convert -background white image-1024.jpg label:12345 -append image-labelled.jpg
现在 运行将此工作流程整合为一个管道命令... 以下命令执行此操作。 它应该执行得更快(无论按照我的上述步骤 0.--4. 得到的结果如何):
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY +write image-1024.jpg \) \
\( mpr:XY -thumbnail 200x200 +write image-thumb.jpg \) \
\( mpr:XY -background white label:12345 -append +write image-labelled.jpg \) \
null:
说明:
-respect-parentheses
: 需要真正使 相互独立 sub-commands 在\( .... \)
括号内执行。+write mpr:XY
: 用于将输入文件写入 MPR 存储寄存器。XY
只是一个标签(你可以使用任何东西),稍后需要re-call相同的图像。+write image-1024.jpg
: 将第一对括号内执行的子命令的结果写入磁盘。+write image-thumb.jpg
: 将在第二对括号内执行的子命令的结果写入磁盘。+write image-labelled.jpg
: 将在第三对括号内执行的子命令的结果写入磁盘。null:
: 终止命令管道。 必需,因为否则我们将以最后一个子命令的右括号结束。
7。将 4 个单独的命令与单个管道进行基准测试
为了大致了解我的建议,我执行了 运行 下面的命令。
第一个 运行s 4 个单独命令的序列 100 次(并以不同的文件名保存所有结果图像)。
time for i in $(seq -w 1 100); do
convert image.tiff \
image-indiv-run-${i}.jpg
convert image-indiv-run-${i}.jpg -sample 1024x \
image-1024-indiv-run-${i}.jpg
convert image-1024-indiv-run-${i}.jpg -thumbnail 200x200 \
image-thumb-indiv-run-${i}.jpg
convert -background white image-1024-indiv-run-${i}.jpg label:12345 -append \
image-labelled-indiv-run-${i}.jpg
echo "DONE: run indiv $i ..."
done
我对 4 个命令(重复 100 次!)的结果是:
real 0m49.165s
user 0m39.004s
sys 0m6.661s
第二个命令对单个流水线进行计时:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY -resize 1024x \
+write image-1024-pipel-run-${i}.jpg \) \
\( mpr:XY -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( mpr:XY -resize 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
单管道的结果(重复100次!)是这样的:
real 0m29.128s
user 0m28.450s
sys 0m2.897s
如您所见,单个管道比 4 个单独的命令快约 40%!
现在您还可以投资 multi-CPU、大量 RAM、快速 SSD 硬件来进一步加快速度:-)
但首先要运行将此 CLI 方法写入 PHP 代码...
关于这个话题还有一些话要说。 但我的时间 运行 现在已经结束了。 几天后我可能会 return 这个答案并更新它...
更新: 我不得不用基准测试的新数字更新这个答案:
最初我忘记了将 -resize 1024x
操作(愚蠢的我!)包含到流水线版本中。
包含它后,性能提升仍然存在,但不再那么大了。
8。使用-clone 0
复制内存中的图像
这里有另一种替代方法,可以尝试使用上面建议的命名内存寄存器代替 mpr:
方法。
它使用(再次在 'side processing inside parentheses' 中)-clone 0
操作。
它的工作方式是这样的:
convert
从磁盘读取一次输入 TIFF 并将其加载到内存中。- 每个
-clone 0
运算符都会复制第一个加载的图像(因为它在当前图像堆栈中具有索引0
)。 - 整个命令管道中的每个 "within-parenthesis" sub-pipeline 都对克隆执行一些操作。
- 每个
+write
操作将各自的结果保存到磁盘。
所以这里是基准测试的命令:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -resize 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
我的结果:
real 0m19.432s
user 0m18.214s
sys 0m1.897s
令我惊讶的是,这比使用 mpr:
!
9。使用 -scale
或 -sample
而不是 -resize
此替代方法很可能会加快您调整大小的速度 sub-operation。 但这可能会导致图像质量稍差(您必须验证,如果这种差异很明显)。
有关 -resize
、-sample
和 -scale
之间差异的一些背景信息,请参阅以下答案:
我也试过了:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -scale 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
我的结果:
real 0m16.551s
user 0m16.124s
sys 0m1.567s
这是迄今为止最快的结果(我将其与 +clone
变体相结合)。
当然,这种修改也可以应用于您的初始方法运行宁 4 个不同的命令。
10。通过在命令中添加 -depth 8
来模拟 Q8
构建。
我实际上并没有 运行 并测量这个,但完整的命令是。
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -scale 1024x -depth 8 \
-background white label:12345 -append \
+write d08-image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
此修改也适用于您最初的"I run 4 different commands"-方法。
11。按照 Mark Setchell
的建议,将其与 GNUparallel
相结合
如果您的整个工作流程允许这种并行化,这当然只对您适用且合理。
对于我的小基准测试来说,它是适用的。 对于您的网络服务,您可能一次只知道一项工作...
time for i in $(seq -w 1 100); do \
cat <<EOF
convert image.tiff \
\( -clone 0 -scale 1024x -depth 8 \
-background white label:12345 -append \
+write d08-image-labelled-pipel-run-${i}.jpg \) \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
EOF
done | parallel --will-cite
结果:
real 0m6.806s
user 0m37.582s
sys 0m6.642s
user
和real
时间明显的矛盾可以这样解释:
user
时间表示在 8 个不同的 CPU 核心上计时的所有时间滴答的总和。
从用户看手表的角度来看,速度要快得多:不到 10 秒。
12。摘要
选择您自己的喜好 -- 结合不同的方法:
通过构建更智能的命令管道可以获得一些加速(与当前图像质量相同) . 避免 运行ning 各种命令(其中每个
convert
都会导致一个新进程,并且必须从磁盘读取其输入)。 将所有图像处理打包到一个进程中。 使用 "parenthesized side processing"。 使用-clone
或mbr:
或mbc:
或什至组合这些。可以通过以性能换取图像质量额外获得一些加速: 您的一些选择是:
-depth 8
(必须在 OP 系统上声明)与-depth 16
(OP 系统上的默认值)-resize 1024
对比-sample 1024x
对比-scale 1024x
如果您的工作流程允许,请使用 GNU
parallel
。
一如既往,@KurtPfeifle 提供了一个非常有理由和解释的答案,他所说的一切都是可靠的建议,您最好仔细聆听并遵循。
虽然还有更多可以做的事情,但是我不能作为评论添加,所以我把它作为另一个答案,虽然它只是对 Kurt 的增强...
我不知道 Kurt 使用的输入图像大小,所以我制作了一张 3000x2000 并将我的 运行 时间与他的进行比较,看它们是否具有可比性,因为我们有不同的硬件。在我的机器上,单个命令 运行 需要 42 秒,流水线命令 运行 需要 36 秒,所以我想我的图像大小和硬件大致相似。
然后我使用 GNU Parallel 来 运行 并行作业 - 我认为您会在 Xeon 上从中受益匪浅。这是我所做的...
time for i in $(seq -w 1 100); do
cat <<EOF
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY -resize 1024x \
+write image-1024-pipel-run-${i}.jpg \) \
\( mpr:XY -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( mpr:XY -background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
EOF
done | parallel
如您所见,我所做的只是将需要 运行ning 的命令回显到 stdout 并将它们通过管道传输到 GNU Parallel。 运行 这样,在我的机器上只需要 10 秒。
我也曾尝试使用 ffmpeg
模仿功能,并想出了这个,这在我的测试图像上看起来非常相似 - 你的里程可能会有所不同。
#!/bin/bash
for i in $(seq -w 1 100); do
echo ffmpeg -y -loglevel panic -i image.tif ff-$i.jpg
echo ffmpeg -y -loglevel panic -i image.tif -vf scale=1024:682 ff-$i-1024.jpg
echo ffmpeg -y -loglevel panic -i image.tif -vf scale=200:200 ff-$i-200.jpg
done | parallel
在我的 iMac 上使用 3000x2000 image.tif 输入文件 运行 只需 7 秒。
我在 homebrew
下安装 ImageMagick libturbo-jpeg
失败得很惨。
我一直听到一些人说 GraphicsMagick(几年前的一个分支,从 ImageMagick 分支出来)比 ImageMagick 快得多。
所以我借此机会试了一下。因此我的第二个答案。
我执行了 运行 以下 4 个单独的 gm
命令循环。这使得结果与我的其他答案中记录的 4 个单独的 convert
命令相当。它发生在同一台机器上:
time for i in $(seq -w 1 100); do
gm convert image.tiff gm-${i}-image.jpg
gm convert gm-${i}-image.jpg -resize 1024x gm-${i}-image-1024.jpg
gm convert gm-${i}-image-1024.jpg -thumbnail 200x200 gm-${i}-image-thumb.jpg
gm convert -background white \
gm-${i}-image-1024.jpg label:12345 -append gm-${i}-image-labelled.jpg
echo "GraphicsMagick run no. $i ..."
done
结果次数:
real 1m4.225s
user 0m51.577s
sys 0m8.247s
这意味着:对于这个特定的工作,在这台机器上,我的 Q8
GraphicsMagick(版本是 1.3.20 2014-08-16 Q8
)速度较慢 64 秒 比我的 Q16
ImageMagick(版本是 6.9.0-0 Q16 x86_64 2014-12-06
)需要 50 秒 每个 100 运行 秒。
当然,这个简短的测试及其结果绝不是万无一失的声明。
您可能会问:这台机器及其 OS 在进行每次测试时还做了什么?哪些其他应用程序同时加载到内存中? etc.pp,你是对的。 -- 但您现在可以自由地 运行 您自己的测试。您可以做的一件事是为两个测试提供几乎相同的条件:运行 它们同时在 2 个不同的终端 windows!)
我忍不住用 libvips 尝试这个基准测试。我使用了这两个脚本:
#!/bin/bash
convert image.tiff \
\( -clone 0 -scale 1024x -depth 8 label:12345 -append \
+write d08-image-labelled-pipel-run-.jpg \) \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-.jpg \) \
null:
和libvips using the Python interface:
#!/usr/bin/python3
import sys
import pyvips
image = pyvips.Image.thumbnail("image.tiff", 1024)
txt = pyvips.Image.text("12345")
image = image.join(txt, "vertical")
image.write_to_file(f"{sys.argv[1]}-labelled.jpg")
image = pyvips.Image.thumbnail("image.tiff", 200)
image.write_to_file(f"{sys.argv[1]}-thumb.jpg")
然后在带有 IM 6.9.10-86 和 vips 8.11 的 3000 x 2000 RGB tiff 图像上,两者都带有 libjpeg-turbo,我看到:
$ /usr/bin/time -f %M:%e parallel ../im-bench.sh ::: {1..100}
78288:1.83
$ VIPS_CONCURRENCY=1 /usr/bin/time -f %M:%e parallel ../vips-bench.py ::: {1..100}
59512:0.84
所以 libvips 的速度大约是原来的两倍,而且使用的内存更少。
(我最初是在 2015 年发布的。我在 2021 年使用当前 libvips 的代码对其进行了更新,并在现代 PC 上重新运行,因此与上面的原始版本相比,处理速度有了巨大的提高)