ImageMagick/GraphicsMagick:如何在一个步骤中合并/合成多个(3+)图像而无需临时文件?

ImageMagick/GraphicsMagick:如何在一个步骤中合并/合成多个(3+)图像而无需临时文件?

我试图创建多个(两个或更多)具有不同文本/字体/大小的小图像,然后在一个步骤中将它们覆盖在不同位置的单个源图像上(无需自己创建临时命名文件)。

这是我到目前为止所尝试的:

  • 创建单个文本并使用管道和 miff 流将其合并到某个位置(有效,但我需要多张图像)

    gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- \
    | gm composite -geometry +10+10 miff:- source.tif out.tif
    
  • 创建并合并第一个图像,存储输出临时文件,加载临时文件并将其合并到源(有效,但必须写入两次对gm所需临时文件的调用,并且必须手动删除)

    gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- \
    | gm composite -geometry +10+10 miff:- source.tif tmp.tif ; \
    gm convert \
      -background transparent \
      -fill grey \
      -font Calibri \
      -size 200x50 \
      -pointsize 12 \
      -gravity SouthEast \
      label:'small text' \
      miff:- \
    | gm composite -geometry +300+100 miff:- tmp.tif out.tif
    
  • 创建多个文本并将它们合并到一个位置,其中仅计算最后一个位置并将其应用于所有文件(不起作用,仅覆盖第一个文本)

    { gm convert \
      -background transparent \
      -fill black \
      -font Calibri \
      -size 300x100 \
      -pointsize 36 \
      -gravity SouthEast \
      label:'large text' \
      miff:- ; \
      gm convert \
      -background transparent \
      -fill grey \
      -font Calibri \
      -size 200x50 \
      -pointsize 12 \
      -gravity SouthEast \
      label:'small text' \
      miff:- ; } \
    | gm composite miff:- -geometry +10+10 miff:- -geometry +300+100 source.tif -geometry +0+0 out.tif
    
  • 使用( ... )语法,将“text1.tif”和“text2.tif”替换为gm convert大括号中的命令(在任何情况下都不起作用,也许我用错了?)

  • 使用flatten而不是composite (确实有效,但所有文件放置在同一位置而不是不同的位置,也会破坏多页图像)
  • append通过单个管道使用多个文件(我需要它们重叠,而不是并排)

是否可以将三个或更多图像相互组合,以便每个图像在输出图像上具有不同的位置,而无需保存和加载临时文件?或者,如何在写入重叠图像之前设置它们的 z 顺序?如果可能的gm 1.3.25bash 4.3,但我对可行的替代方案持开放态度。

答案1

从最初的请求中,我了解到您需要在一次拍摄中合并 3 个图像(1 个来自文件,2 个生成),因为您当时只能使用 2 个图像。

我还了解您不希望将临时数据存储在磁盘上。最快的解决方案是使用“tmpfs”,如 @nominal-animal 之前的答案中所建议的。

但更进一步,可以避免创建临时文件并基本上一次性完成所有处理。我找到的解决方案是使用“命名管道”,它们是特殊文件,不会将数据存储到磁盘,甚至会暂停管道上的任何传输,直到接收部分也准备好为止。我不会使用您提供的完整命令(仅命令、输入和输出),请随意填写您需要的参数。

# First it's necessary to have the pipes created, one time operation and they can be reused over and over.
mknod /tmp/pipe1 p
mknod /tmp/pipe2 p
mknod /tmp/pipe3 p

# generate the 2 images and send each to a pipe 
gm convert ... miff:- > pipe1 &
gm convert ... miff:- > pipe2 &
# merge the source image to the 1st generated image
gm composite ... source.tif pipe1 miff:- > pipe3 &
# finally merge the result to the 2nd generated image
gm composite ... pipe3 pipe2 out.tif

看起来操作只需 4 步即可完成,但实际上所有操作都会一次性完成,无需将任何内容写入磁盘。

以下是它的工作原理以及为什么这样编写命令:

  • 生成第一个图像的命令将开始处理请求,但输出必须进入命名管道。因为此时没有从管道中读取任何内容,所以输出将停止等待接收。该进程不会退出或显示任何错误,因此为了继续,它被置于后台。
  • 第二张图片相同,只是管道不同。
  • 源图像和第一个管道中的数据的合并将导致它从管道 1 读取并释放第一个生成命令,但因为它的输出进入另一个管道,所以必须等待最后一步。
  • 合并来自 Pipe3 和 Pipe2 的数据将完成操作并生成所需的输出文件并释放前 2 个命令。

您可以将管道视为不是文件的临时文件,只需记住将一些东西放在后台,否则脚本将等待您的 CTRL+C。每个命令必须单独放在后台!如果在前面的命令完成后生成输出,将它们放入这样的脚本中,则没有问题。

一个很好的功能是,您可以在后台首先启动任意 3 个命令,然后正常启动最后一个命令,因为所有 4 个命令都依赖于来自管道的数据。此外,只需添加更多管道即可轻松扩展到多个命令。

我看到有一个限制,你不能并行使用相同的管道,如果你需要,你必须为并行运行的每个实例使用不同的管道组,否则会出现意外:)顺序处理没有​​这样的问题。

从性能角度来看,我不知道这是否比在“tmpfs”上创建临时文件更快,因此您需要通过在实际场景中测试来找出答案。为此,您必须有足够的内存来容纳所有 4 个进程及其内存中的数据。 (这在很大程度上取决于您处理/生成的图像的大小)。

答案2

这不是对所提出问题的答案,而只是一个旁白,表明原始问题的前提,即“临时文件...必须手动删除”,是完全错误的。

(我在可能的情况下发布了这个“答案”,注意到这个前提是错误的,让OP和其他人可以随意使用临时文件。在许多情况下,使用临时文件使脚本更加健壮并且更易于维护。)

如果 Bash 脚本需要使用一个或多个临时文件,我总是使用以下方法:

#!/bin/bash
work="$(mktemp -d)" || exit 1
trap "cd / ; rm -rf '$work'" EXIT

这使用mktemp帮助程序创建临时工作目录(在 中/tmp/)。如果mktemp不可用,或者无法创建目录,则脚本将中止。

为了自动删除工作目录及其可能包含的任何文件,我们设置了一个EXIT陷阱。每当 shell 退出时,就会触发这个陷阱,即 bash 执行指定的命令;无论是正常退出还是由于错误。

trapEXIT命令更改到根目录(只是为了确保当前工作目录不在临时目录内),然后删除整个子树。请注意,由于该命令位于双引号内,因此$work在设置陷阱时对其进行评估;这意味着即使您work稍后在脚本中更改变量的值,陷阱仍然会删除原始临时目录。

尽管陷阱更改了当前工作目录,但它不会在陷阱之外产生任何影响——无论是在其他地方的脚本中,还是对其他进程——因为当前工作目录是特定于每个进程的,并且当前的 bash 进程将trap命令执行后立即退出。

因此,当脚本完成时,在 下创建的任何临时文件$work(例如"$work/1""$work/result"等)以及临时目录本身都会被自动删除。$work

相关内容