首先,让我这样说:
如果您的输入图像非常大,则将单独的命令放入一个 ImageMagick 命令链中,您只会节省大量的处理时间。
但是,您可以通过跳过写出和读入中间结果图像的需要来节省磁盘 I/O 时间。
您的代码使用两个不同的montage 命令加上一个convert 命令来实现第一个蒙太奇。
最后使用montage 将之前的结果放在背景上。
从我的脑海中,我可以快速想出一种将前三个命令组合成一个命令的方法。
将中间结果放在背景上的最后一个蒙太奇步骤并不容易正确,而且很可能也不会节省太多时间。
因此,我暂时将其保持打开状态。
很遗憾,您没有提供任何指向源图像的链接。
为了回答这个问题,我必须创建自己的。
它们还可以用来证明我的答案的有效性。
为了创建四个 800x600 像素大小的 PNG,我在命令行中使用了 Ghostscript 和一些 PostScript 代码:
for i in 1 2 3 4 ; do
gs -o t${i}.png \
-g800x600 \
-sDEVICE=pngalpha \
-c "0.5 setgray" \
-c "0 0 800 600 rectfill" \
-c "1 0 0 setrgbcolor" \
-c "3 setlinewidth" \
-c "10 10 780 580 rectstroke" \
-c "0 setgray" \
-c "/Helvetica-Bold findfont" \
-c "560 scalefont setfont" \
-c "230 60 moveto" \
-c "(${i}) show " \
-c "showpage" ;
done
然后我首先用我的图像测试了你的代码。
这是 OP 命令的结果。结果是完整的,包括背景图像上的蒙太奇来自我自己的股票(更新),使用受 Mark Setchell 回答启发的命令创建:
convert -size 200x200 xc:gray +noise gaussian background.png
合并前两个命令:
以下是我为提出单个命令而进行的第一次尝试。
它应该与您输出line.png 的前两个 命令实现相同的结果。
我已经知道它在某些方面不会完全按预期工作,但我仍然尝试过。
我尝试了它以查看命令的其他位置是否会显示我没有预期的问题。
不用担心,完整的最终代码的解释将在答案的末尾。
阅读完整答案后,您可以尝试弄清楚以下命令的工作原理:
_col1=blue ;
_col2=red ;
convert t*.png -flop -resize 487x296\! \
\( -size 15x15 \
-clone 0 xc:${_col1} \
-clone 1 xc:${_col1} \
-clone 2 xc:${_col1} \
-clone 3 \
-append \
+write f.png \
\) null:
这是我的命令的结果(右)与第二个命令之后的中间结果(左)的比较:
所以,我曾预料到的一件事发生了:每张图片之间都有一个蓝色间隔。
出于调试原因,我对其进行了着色。
这可以通过将颜色变量设置为none(透明)来解决。
我没有预料到的事情,我是在打开生成的图像f.png后才发现的:
我的背景是白色而不是透明。
这可以通过在正确的位置添加-background none 来解决。
我的列中单个图像的间距太窄,只有 15 个像素。
这是因为在 OP 的中间文件line.png 中,间距不是 15 像素,而是 30 像素。
他的参数-geometry +0+15 用于创建列的montage 确实在每个图像的顶部和添加了15 个像素。
我的命令(使用convert ... -append 而不是montage)不允许使用具有相同效果的其他-geometry 设置。
但他可以通过在我的命令中添加更多 xc:{_col1} 间隔来修复。
合并前三个命令:
所以这是下一次迭代。
它集成了来自 OP 的第三个命令的效果。
这是通过添加+duplicate 来复制第一个创建的列来实现的。
然后它添加+append 以附加重复的列水平(-append 将垂直这样做):
_col1=blue ;
_col2=red ;
convert t*.png -flop -resize 487x296\! \
\( -size 15x15 \
-background none \
xc:${_col1} \
-clone 0 xc:${_col1} xc:${_col1} \
-clone 1 xc:${_col1} xc:${_col1} \
-clone 2 xc:${_col1} xc:${_col1} \
-clone 3 \
xc:${_col1} \
-append \
+duplicate \
-size 45x45 xc:${_col2} \
+append \
+write f2.png \
\) null:
我曾预料到的一件事又发生了:
两列之间的红色垫片位于右侧,而不是位于两列之间。
我们可以通过交换获得+append-ed 的最后两个图像来解决这个问题。
这可以通过在正确的位置添加+swap 运算符来完成。
此外,与第一列中的图像间间距相同的事情将应用于列之间的间距:我必须加倍。
我现在不在乎没有将相同的空间(45 像素)添加到左侧和右侧的 +append-ed 列中。
所以这里还有一个迭代:
_col1=red ;
_col2=blue ;
convert t*.png -flop -resize 487x296\! \
\( -background none \
-size 15x15 \
xc:${_col1} \
-clone 0 xc:${_col1} xc:${_col1} \
-clone 1 xc:${_col1} xc:${_col1} \
-clone 2 xc:${_col1} xc:${_col1} \
-clone 3 \
xc:${_col1} \
-append \
+duplicate \
-size 90x90 xc:${_col2} \
+swap \
+append \
+write f3.png \
\) null:
结果如下:
- 右边是第三条命令后OP代码创建的中间
photos.png。
- 左边是我的命令创建的图像蒙太奇。
现在缺少的是对我打包到单个命令中的各个操作的分解的解释:
-
\( :
这会打开一些图像的“横向”处理。
然后将这种横向处理的结果再次插入到主 ImageMagick 进程中。
反斜杠是必需的,因此 shell 不会尝试解释它们。
-
\) :
这将关闭横向处理。
-
-size 15x15 :
这将设置接下来要填充的画布的大小。
-
xc:${_col1} :
这用指定的颜色填充画布。
xc: 只是 canvas: 的别名,但打字速度更快。
-
-clone 0 :
这将创建当前在加载的图像堆栈中的第一个图像的副本。
在这种情况下,它是t1.png 的副本。
-clone 1 复制 t2.png、-clone 2 复制 t3.png 等。
-clone 或 +clone 在横向处理链中工作得最好,因此前面解释过使用 \( 和 \)。
-
-append :
此运算符垂直附加所有当前加载的图像。
在这种情况下,这些是 t1.png、...t4.png 的 4 个副本。
-
+duplicate :
此运算符类似于+clone。
它复制当前加载的图像堆栈中的最后一个图像。
在这种情况下,最后一个图像(并且仅剩下一个在横向管道内)是先前-append 操作的结果。
此操作创建了第一列 4 张图像,由红色垫片隔开。
-
+append :
该运算符水平地附加所有当前加载的图像。
目前共有三个图像:-append 操作的结果、由+duplicate 创建的副本以及90x90 大小的xc:-canvas。
-
+swap :
此运算符交换当前加载堆栈上的最后两个图像。
-
+write :
此运算符从当前加载的堆栈中写出 所有 图像。
当有多个图像时,它将使用给定的名称写入这些图像,但附加一个数字。
它是调试复杂 ImageMagick 命令的好工具。
这很棒,因为在+write 操作完成后,之前加载的图像都保留在堆栈上。
这些图像保持不变,并且可以继续处理。
但是,我们现在已经完成,在这种情况下不会继续。
因此,我们使用\) 关闭侧向进程。
-
null :
现在我们关闭了 sideway 进程,ImageMagick 再次将 sideway 生成的图像放入主管道。
请记住,+write 没有完成处理,它向磁盘写入了一个文件,该文件旨在成为 中间 结果。
在主管道中,现在还有原来的t1.png ...t4.png加上侧边处理的结果。
但是,我们不会对他们做任何事情。
我们将来自+write 的中间结果作为我们的最终结果。
但是convert 命令现在希望看到一个输出文件名。
如果它没有看到,它会抱怨并向我们显示一条错误消息。
因此,我们告诉它注销所有已加载并从堆栈中丢弃所有图像。
为此,我们使用null: 作为输出文件名。
(如果您喜欢冒险,请使用 out.png 作为文件名而不是 null:。
你会看到 ImageMagick 实际上创建了多个out-0.png、out-1.png、...out-3.png 文件名。
你会发现out-4.png和f.png一样,out-{0,1,2,3}.png和输入图片一样。 --
您也可以将null: 替换为-append output.jpg,然后看看会发生什么...)
更新
现在是速度比较...
对于第一个粗略的基准测试,我确实在一个循环中运行了 OP 的前三个命令,并进行了 100 次迭代。
然后我也运行了自己的命令 100 次。
结果如下:
-
OP 前三个命令,100 次:61.3 秒
-
我的单个命令替换了这些,100 次:48.9 秒
所以我的单个命令比来自 OP 的原始命令节省了大约 20% 的时间。
鉴于我的磁盘 I/O 性能可以假设为与旋转硬盘相比相当快(测试系统具有 SSD),合并命令的速度增益(避免过多的临时文件写入/读取) 在磁盘较慢的系统上可能会更加明显。
为了检查命令的一点重新架构(没有那么多加载的图像在最后被简单地丢弃,如null: 输出文件名所示)是否会获得更多改进,我也试过这个:
convert t*.png -flop -resize 487x296\! \
-background none \
-size 0x15 \
xc:red \
-duplicate 7 \
-insert 0 \
-insert 2 \
-insert 3 \
-insert 5 \
-insert 6 \
-insert 8 \
-insert 9 \
-append \
\( +clone -size 45x0 xc:blue +swap \) \
+append \
f4.png
这个命令的架构有点不同。
首先,它会加载所有图像。它-flops 他们和它-resizes 他们。
其次,它会创建一个 15x15 像素的画布,然后将其放置在图像堆栈上。
第三,它会创建该画布的 7 个额外副本。
现在堆栈上有 12 个图像:
4 个输入文件、1 个xc:-canvas 和 7 个画布副本。
-
然后,它使用一系列-insert N 操作。
-insert N 操作操纵图像堆栈的顺序。
它从堆栈中删除最后一个图像并将其插入图像索引位置N。
当-insert系列操作开始时,堆栈上有4个图像(t1、t2、t3、t4),加上8个“间隔符”(s)。
这是他们原来的顺序:
index: 0 1 2 3 4 5 6 7 8 9 10 11
image: t1 t2 t3 t4 s s s s s s s s
我以某种方式选择了索引号,以便在所有 -insert N 操作完成后,从上面的原始订单更改的新订单是:
index: 0 1 2 3 4 5 6 7 8 9 10 11
image: s t1 s s t2 s s t3 s s t4 s
由于所有的间隔 s 都是 15 像素宽,这实现了与我的初始命令相同的间距。
接下来,会发生类似的横向处理:这次是 +clone 先前 -append 操作的结果并创建水平间隔。
最后,+append(对前一个 -append 和 sideway-process 的结果进行运算)创建最终结果 f4.png。
当我对最后一条命令进行基准测试时,每个命令重复 100 次后得到以下结果:
-
我更新前的第一个命令,100 次: 48.3 秒
-
更新后我的上一条命令解释了 100 次: 46.6 秒
所以速度提升不是那么显着,如果你想相信这些数字,大约会提高 3%。
(应该注意,我确实在同一台机器上并行运行了两个循环,以在基准测试比赛中创造更多的公平性。
通过并行运行,它们都必须处理相同的 CPU 和 I/O 负载,这可能是由它们自己以及机器上同时发生的其他进程引起的。)