如何加速复杂的图像处理?

How to speed up a complex image processing?

每个用户都可以上传 100 张 TIFF(黑白)图像。

流程要求:

  1. tif 转换为 jpg

  2. 将图像调整为 xx。

  3. 将图片裁剪为 200 像素。

  4. 添加文字水印。

这是我的 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。两种方法

基本上,可以通过两种不同的方式或结合使用两种方式来应对这一挑战:

  1. 尽可能聪明地构造你的命令。
  2. 以 speed-up 换取质量损失。

接下来的几节将讨论这两种方法。

1。检查您拥有的是哪个 ImageMagick:'Q8'、'Q16'、'Q32' 或 'Q64'?

首先,检查您的确切 ImageMagick 版本和 运行:

convert -version

如果您的 ImageMagick 在其版本字符串中有 Q16(甚至 Q32Q64,这是可能的,但太过分了!): 这意味着,所有 ImageMagick 的内部函数都将所有图像视为具有 16 位(或 32 或 64 位)通道深度。 这为您提供了更好的图像处理质量。 但与 Q8 相比,它还需要双倍内存。 所以同时也意味着性能下降。

因此:您可以测试通过切换到 Q8-build 可以获得哪些性能优势。 (Q 是 ImageMagick 构建支持的 'quantum depth' 的符号。)

尽管如此,您将付出可能的 Q8 性能提升和质量损失。 只需检查 Q8 相对于 Q16 获得的速度,以及您遭受的质量损失。 然后决定你是否可以忍受这些缺点...

在任何情况下,Q16 将使用两倍于每个图像的 RAM 来处理,并且 Q32 将再次使用两倍于 Q16 的内存量。 这与输入文件中看到的实际 bits-per-pixels 无关。 16 位图像文件在保存时也会比 8 位图像文件占用更多磁盘 space。

Q16Q32 需要更多内存,您始终必须确保有足够的内存。 因为 超过 你的物理内存将是一个非常糟糕的消息。 如果更大的 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 可以同时打开的最大文件数。
  • memorymapareadisk 资源限制以字节为单位定义。 对于 设置 它们为不同的值,您可以使用 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.
  • 的文件

它的主要优势体现在...

  1. ...处理非常大的图像,或者当
  2. ...对 "opertion pipelines".
  3. 中的同一张图像应用多项操作

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 个不同的步骤组成:

  1. 将 TIFF 转换为 JPEG。
  2. 将 JPEG 图像调整为 xx(??什么值??)
  3. 将 JPEG 裁剪为 200 像素。
  4. 添加文字水印。

请通过阅读您的代码片段判断我是否理解正确:

  • 您有 1 个输入文件,一个 TIFF。
  • 您需要 2 个最终输出文件:
    1. 1 张 JPEG 缩略图,大小为 200x200 像素;
    2. 1个标签为JPEG,宽度为1024像素(高度保持输入TIFF的纵横比);
    3. 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 操作。 它的工作方式是这样的:

  1. convert 从磁盘读取一次输入 TIFF 并将其加载到内存中。
  2. 每个 -clone 0 运算符都会复制第一个加载的图像(因为它在当前图像堆栈中具有索引 0)。
  3. 整个命令管道中的每个 "within-parenthesis" sub-pipeline 都对克隆执行一些操作。
  4. 每个 +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

userreal时间明显的矛盾可以这样解释: user 时间表示在 8 个不同的 CPU 核心上计时的所有时间滴答的总和。

从用户看手表的角度来看,速度要快得多:不到 10 秒。

12。摘要

选择您自己的喜好 -- 结合不同的方法:

  1. 通过构建更智能的命令管道可以获得一些加速(与当前图像质量相同) . 避免 运行ning 各种命令(其中每个 convert 都会导致一个新进程,并且必须从磁盘读取其输入)。 将所有图像处理打包到一个进程中。 使用 "parenthesized side processing"。 使用 -clonembr:mbc: 或什至组合这些。

  2. 可以通过以性能换取图像质量额外获得一些加速: 您的一些选择是:

    1. -depth 8(必须在 OP 系统上声明)与 -depth 16(OP 系统上的默认值)
    2. -resize 1024 对比 -sample 1024x 对比 -scale 1024x
  3. 如果您的工作流程允许,请使用 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 上重新运行,因此与上面的原始版本相比,处理速度有了巨大的提高)