繁体   English   中英

ImageMagick:将多个“蒙太奇”命令组合为一个以提高性能

[英]ImageMagick: combine multiple 'montage' commands into one to gain performance

我有一个脚本,可以拍摄4张图片并将其复制以一张生成8张小图片。 该脚本还将背景添加到输出中。

这是我期望的:

http://img11.hostingpics.net/pics/831624stack.png

我有一个运行良好的代码。 但是它需要保存多个临时图像。

因此,我一直在寻找是否可以合并脚本的命令以仅进行一次图像保存操作。 目的是使脚本更快地完成。

//Make image smaller:
    convert /home/pi/images/*.png -resize 487x296 -flop \
      -set filename:f "/home/pi/imagesResized/%t%p" '%[filename:f]'.png

//Make one column from 4 images:
    montage /home/pi/imagesResized/*.png -tile 1x4 \
      -geometry +0+15 -background none  /home/pi/line.png

//Duplicate to two columns:
    montage /home/pi/line.png /home/pi/line.png -tile 2x1 \
      -geometry +45+0 -background none /home/pi/photos.png

//Add the background image:
    suffix=$(date +%H%M%S)
    montage /home/pi/photos.png -geometry +20+247 \
         -texture /home/pi/data/background_photo.png \
          /home/pi/photo_${suffix}.jpg

首先,我这样说:如果输入图像很大,将单独的命令放入一个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命令的结果。 结果是完整的,包括 从我自己的股票 已更新 )中 提取 的背景图像上的蒙太奇图像 该图像是由马克·谢彻尔的答案启发而创建的:

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:

这是我的命令(右)的结果与第二个命令(左)之后的中间结果的比较:

所以,有一件事我预料的事情发生了:有每个图像之间的间隔蓝色。 我出于调试原因对其进行了着色。 可以通过将color变量设置为none (透明)来解决此问题。

没有想到的事情,只有在打开结果图像f.png后才发现的f.png

  • 我的背景是白色而不是透明。 可以通过在正确的位置添加-background none来解决此问题。

  • 我在各列中各图像的间距太窄,只有15个像素。 这是因为在OP的中间文件line.png中,间距不是15像素,而是30像素。 他的montage创建列的参数-geometry +0+15确实在每个图像的顶部底部添加了15个像素。 我的命令(使用convert ... -append而不是montage )不允许使用其他具有相同效果的-geometry设置。 但是可以通过在我的命令中添加更多的xc:{_col1}分隔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运算符来完成。

  • 同样,与第一列中的图像间间距相同的事物将应用于两列之间的间距:我必须将其加倍。

  • 此刻,我不会在意向左和向右+append append-列添加相同的空间(45像素)。

因此,这里还有一个迭代:

_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进程中。 反斜杠是必需的,因此外壳程序不会尝试解释它们。
  • \\)
    这将关闭横向处理。
  • -size 15x15
    这将设置接下来要填充的画布的大小。
  • xc:${_col1}
    这将使用指定的颜色填充画布。 xc:只是canvas:的别名,但是键入速度更快。
  • -clone 0
    这将创建当前在已加载图像堆栈中的第一张图像的副本。 在这种情况下,它是t1.png的副本。 -clone 1t2.png-clone 2t3.png-clone+clone工作最好内部侧向处理链,因此前面所解释的使用\\(\\)
  • -append
    此运算符垂直附加当前所有已加载的图像。 在这种情况下,这是t1.png ,... t4.png的4个副本。
  • +duplicate
    该运算符类似于+clone 它复制当前加载的图像堆栈中的最后一个图像。 在这种情况下,最后一个图像(并且在横向管道中仅剩下一个)是先前-append操作的结果。 此操作已创建第一列的4张图像,并用红色的分隔符隔开。
  • +append
    此运算符水平附加所有当前加载的图像。 当前有三张图像: -append操作的结果,由+duplicate创建的+duplicate以及大小为90x90xc: -canvas。
  • +swap
    该运算符交换当前加载堆栈上的最后两个图像。
  • +write
    该操作符从当前加载的堆栈中写出所有图像。 当有多个图像时,它将使用给定的名称写这些图像,但是要附加一个数字。 这是调试复杂ImageMagick命令的好工具。 很棒,因为在完成+write操作之后,先前加载的图像将全部保留在堆栈中。 这些图像保持不变,并且处理可以继续。 但是,我们现在已经完成,在这种情况下将不会继续。 因此,我们使用\\)关闭横向过程。
  • null
    现在,我们关闭了sideway过程,ImageMagick将结果图像从sideway再次放入主管道。 请记住, +write并没有完成处理,而是将一个文件写入了磁盘,这是中间结果。 t1.png道中,现在仍然有原始的t1.png ... t4.png以及横向处理的结果。 但是,我们不会对他们采取任何措施。 我们将+write的中间结果作为最终结果。 但是convert命令现在期望看到一个输出文件名。 如果没有看到它,它将抱怨并向我们显示错误消息。 因此,我们告诉它注销所有已加载的内容,并丢弃堆栈中的所有图像。 为此,我们使用null:作为输出文件名。

    (如果您喜欢冒险,请使用out.png作为文件名而不是null: 。您将看到ImageMagick实际上创建了多个out-0.pngout-1.png ,... out-3.png文件名。您将发现out-4.pngf.png相同, out-{0,1,2,3}.png与输入图像相同-您也可以将null:替换为-append output.jpg ,然后看看会发生什么...)

更新资料

现在进行速度比较...

对于第一个粗略的基准测试,我确实以100次迭代的循环运行了OP的前三个命令。 然后我也执行了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

该命令的体系结构有所不同。

  1. 首先,它加载所有图像。 -flop它们,并-resize它们的大小。

  2. 其次,它创建一个15x15像素的画布,然后还将其放置在图像堆栈上。

  3. 第三,它将创建该画布的其他7个副本。 现在,堆栈上有12张图像:4个输入文件,1个xc: -canvas和7个画布副本。

  4. 然后,它使用一系列-insert N操作。 -insert N操作可操纵图像堆栈的顺序。 它从堆栈中删除最后一张图像,并将其插入图像索引位置N -insert系列操作开始时,堆栈上有4个图像( t1t2t3t4 ),外加8个“间隔符”( s )。 这是他们的原始顺序:

     index: 0 1 2 3 4 5 6 7 8 9 10 11 image: t1 t2 t3 t4 ssssssss 

    我以某种方式选择了索引号,以便在所有-insert N操作完成之后,从原始顺序上方更改后的新顺序为:

     index: 0 1 2 3 4 5 6 7 8 9 10 11 image: s t1 ss t2 ss t3 ss t4 s 

    由于所有的间隔件s是15个像素宽,这实现相同的间隔作为我的初始命令。

  5. 接下来,发生类似的横向处理:这次+clone上一个-append操作的结果并创建水平间隔符。

  6. 最后, +append (对上一个-appendf4.png处理的结果进行运算)创建最终结果f4.png

当我对这最后一条命令进行基准测试时,对于每个命令100次重复,我得到以下结果:

  • 更新前我的第一个命令,共100次: 48.3秒
  • 我的最后一条命令在更新后进行了100次解释: 46.6秒

因此,速度增益并不是那么显着,如果您想相信这些数字,速度大约可以提高3%。

(应该注意,我确实在同一台机器上并行运行两个循环,以在基准测试竞争中创造更多的公平性。通过并行运行,它们都必须处理相同的CPU和I / O负载,这可能会导致本身以及计算机上同时发生的其他进程。)

由于我没有您的图像,纹理或大小,因此我将向您展示一些类似的内容,以使您适应...

制作一些输入图像:

convert -size 500x400 xc:black 1.png
convert -size 500x400 xc:red 2.png
convert -size 500x400 xc:green 3.png
convert -size 500x400 xc:blue 4.png

和背景纹理:

convert -size 2000x2000 xc:gray +noise gaussian background.png

现在按照您的要求执行操作,但没有将中间文件存储到磁盘:

montage [1234].png -background none -tile 1x4 -geometry +0+15 miff:- |
  convert -background none :- -size 15 xc:none -clone 0 +append png: | 
  convert -gravity center background.png :- -composite z.png

在此处输入图片说明

第一行将这4张图像依次排列,然后将合并结果以MIFF (多图像文件格式)通过管道发送到下一个命令。 第二个命令从管道读取图像,并附加一个15像素宽的“分隔符”,然后复制图像的第一列(带有clone ),并作为PNG通过另一个管道写入下一个命令。 最后的命令读取组合的8张小图像,并将它们放在背景的中央。

如果图像的右手栏应该是左手栏反映出的-从您的糟糕示例中很难看出哪个显示了扁平的黑框-您可能需要将命令的第二行更改为

  convert -background none :- -size 15 xc:none -clone 0 +append png: | 

  convert -background none :- -size 15 xc:none \( -clone 0 -flop \) +append png: | 

这是另一个答案。 它从Mark Setchell的方法中获得启发。 在这里,我提出了一个新的命令管道。 不同的是,我...

  • ...在管道中仅使用3个montage命令(Mark在其管道中使用了1个montage和2个convert s),并且我
  • ...精确地再现OP的3个不同命令的输出。

作为输入,我在另一个答案t{1,2,3,4}.png使用了Ghostscript命令创建的四个图像。

为了创建合适的背景图像,我使用了Mark的命令,但对其进行了修改:我将图像缩小了一点,以便OP中的-texture运算符可以有意义地使用:

convert -size 200x200 xc:gray +noise gaussian background.png

然后我对所有三个命令/脚本进行了基准测试,...

  1. ... OP命令作为shell脚本,
  2. ...马克的方法,尽管如上所述,
  3. ...我最初的方法,并添加了背景图片。

为了进行基准测试,我每个都进行了100次重复。 这样做时,我几乎同时在一个单独的终端窗口中启动了每个脚本/命令。 测试机上的CPU有4个内核。 因此,当命令并行运行时,它们必须分别处理相同的CPU和I / O负载,相互竞争资源。 这是我可以想到的最“公平”的临时性能测试设置。

我还确认了这3个测试( photo_test1.jpgms+kp_test2.jpgkp_test3.jpg )创建的输出文件在kp_test3.jpg方向上(几乎)相同。 如果将输出切换到PNG(而不是JPEG,而是OP要求的方式),则这三种方法之间的微小差异也将消失。

结果如下:

使用原始帖子(OP)中的4条命令编写脚本:

mkdir ./imagesResized
time for i in {1..100}; do
    convert t*.png -resize 487x296\! -flop                   \
      -set filename:f "./imagesResized/%t%p" '%[filename:f]'.png    
    montage ./imagesResized/*.png -tile 1x4 -geometry +0+15  \
            -background none line.png    
    montage line.png line.png -tile 2x1 -geometry +45+0      \
            -background none photos.png

    montage photos.png -geometry +20+247                     \
            -texture background.png                          \
             photo_test1.jpg    
done

结果:

真正的2m13.471s
用户1m54.306s
SYS 0分14.340秒

实时大约133秒。

以此为100%的时间消耗。

修改后的命令管道受Mark的回答启发:

time for i in {1..100}; do
  montage t[1234].png -resize 487x296\! -flop -background none \
         -tile 1x4 -geometry +0+15 miff:-                      \
    | montage :- -background none -clone 0 -tile 2x1 -geometry +45+0 miff:- \
    | montage :- -geometry +20+247 -texture background.png ms+kp_test2.jpg ;
done

结果:

真正的1m50.125s
用户1m32.453s
sys 0m16.578s

大约110秒。

与原始命令相比,耗时约83%。

我的原始命令现在完成了,缺少背景合成:

time for i in {1..100}; do
    convert t*.png -flop -resize 487x296\! \
          -background none     \    
          -size 0x15           \    
           xc:white            \    
          -duplicate 7         \    
          -insert 0            \    
          -insert 2            \    
          -insert 3            \    
          -insert 5            \    
          -insert 6            \    
          -insert 8            \    
          -insert 9            \    
          -append              \    
          \( +clone -size 90x0 xc:white +swap \) \ 
          +append              \    
          -transparent white   \
           miff:-              \    
    | montage :- -geometry +65+247 -texture background.png kp_test3.png
done 

结果:

真正的1m34.786s
用户1m20.595s
sys 0m13.026s

大约95秒。

与原始命令相比,耗时约72%。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM