1

I have a script that takes 4 pictures and duplicates them to produce 8 small pictures in one. The script also adds a background to the output.

Here is what I expect:

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

I have a code that works well. But it needs to save multiple temporary images.

So I was looking if I could merge the commands of my script to get only one image saving operation. The goal is to make the script complete faster.

//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
4

3 回答 3

6

首先,让我这样说:如果您的输入图像非常大,则通过将单独的命令放入一个 ImageMagick 命令链中,您只会节省大量的处理时间。但是,您可以通过跳过写出和读入中间结果图像的需要来节省磁盘 I/O 时间。

您的代码使用两个不同的montage命令加上一个convert命令来实现第一个蒙太奇。最后,您使用 onemontage将先前的结果放在背景上。

从我的脑海中,我可以快速想出一种将前三个命令组合成一个命令的方法。将中间结果放在背景上的最后一个蒙太奇步骤并不容易正确,而且很可能也不会节省太多时间。因此,我暂时将其保持打开状态。

不幸的是,您没有提供任何指向源图像的链接。为了回答这个问题,我必须创建自己的。它们还可以用来演示我的答案的有效性。

为了创建四个 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。这可以通过+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:

结果如下:

  • 右边是photos.png第三条命令后由 OP 代码创建的中间体。
  • 左边是我的命令创建的图像蒙太奇。

 

现在缺少的是对我打包到单个命令中的各个操作的细分的解释:

  • \(
    这会打开一些图像的“横向”处理。然后将这种横向处理的结果再次插入到主 ImageMagick 进程中。反斜杠是必需的,因此 shell 不会尝试解释它们。
  • \)
    这将关闭横向处理。
  • -size 15x15
    这将设置接下来要填充的画布的大小。
  • xc:${_col1}
    这用指定的颜色填充画布。 xc:只是 的别名canvas:,但输入起来更快。
  • -clone 0
    这将创建当前在加载的图像堆栈中的第一个图像的副本。在这种情况下,它是t1.png. -clone 1复制t2.png-clone 2复制t3.png-clone+clone在横向处理链中工作得最好,因此前面解释了使用\(and \)
  • -append:这个操作符垂直
    附加所有当前加载的图像。在这种情况下,这些是, ...的 4 个副本。t1.pngt4.png
  • +duplicate:
    这个运算符类似于+clone. 它复制当前加载的图像堆栈中的最后一个图像。在这种情况下,最后一个图像(并且仅在横向管道内剩余的一个)是前一个-append操作的结果。该操作创建了 4 个图像的第一列,由红色垫片隔开。
  • +append:此操作符水平
    附加所有当前加载的图像。当前有三个图像:操作的结果、由 创建的副本和大小的-canvas。-append+duplicate90x90xc:
  • +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

这个命令的架构有点不同。

  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   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 像素宽,这实现了与我的初始命令相同的间距。

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

  6. 最后,+append(它对前一个-append和侧向过程的结果进行操作)创建最终结果,f4.png

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

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

所以速度提升并不是那么显着,如果你想相信这些数字,大约会提高 3%。

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

于 2015-04-19T16:09:22.277 回答
1

由于我没有您的图像或纹理或尺寸,因此我将向您展示类似的东西以供您适应...

制作一些输入图像:

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通过另一个管道作为 a 写入下一个命令。最后的命令读取 8 个组合的小图像并将它们放在背景上,居中。

如果图像的右侧列应该是反射的左列-很难从显示扁平黑框的糟糕示例中分辨出来-您可能需要将命令的第二行从

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

  convert -background none :- -size 15 xc:none \( -clone 0 -flop \) +append png: | 
于 2015-04-19T16:06:43.157 回答
0

这是另一个答案。它从 Mark Setchell 的方法中汲取灵感。在这里,我提出了一个新的命令管道。不同的是,我...

  • ...在管道中仅使用 3 个montage命令(马克在他的管道中使用了 1montage和 2 converts),而我
  • ...准确再现 OP 的 3 个不同命令的输出。

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

为了创建合适的背景图像,我使用了 Mark 的命令,但经过修改:我使图像稍微小了一点,以便-textureOP 的运算符可以有意义地使用:

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

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

  1. ...作为 shell 脚本的 OP 命令,
  2. ...马克的方法,尽管如上所述进行了修改,
  3. ...我最初的方法,通过添加背景图像进行了修改。

对于基准测试,我确实每次重复运行 100 次。这样做时,我大致同时在单独的终端窗口中启动每个脚本/命令。测试机上的 CPU 有 4 个核心。因此,当命令并行运行时,它们必须各自处理相同的 CPU 和 I/O 负载,相互竞争资源。这是我能想到的最“公平”的临时性能测试设置。

我还确认了 3 个测试( 和 )创建的输出文件在photo_test1.jpg像素ms+kp_test2.jpgkp_test3.jpg(几乎)是相同的。如果输出切换到 PNG(而不是 JPEG,OP 要求的方式),那么这 3 种方法之间的这些小差异也会消失。

结果如下:

使用原始帖子 (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
系统 0m14.340s

实时大约 133 秒。

以此作为 100% 的时间消耗。

受马克回答启发的修改命令管道:

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
系统 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
系统 0m13.026s

大约 95 秒。

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

于 2015-04-19T20:48:09.010 回答