1

4枚の写真を撮り、それらを複製して8枚の小さな写真を1枚に作成するスクリプトがあります。このスクリプトは、出力に背景も追加します。

これが私が期待するものです:

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

うまく機能するコードがあります。ただし、複数の一時イメージを保存する必要があります。

そこで、スクリプトのコマンドをマージして、画像の保存操作を 1 つだけ取得できるかどうかを調べていました。目標は、スクリプトをより速く完了することです。

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

まず、これを言わせてください:入力画像が非常に大きい場合、個別のコマンドを 1 つの ImageMagick コマンド チェーンに入れることで、処理時間を大幅に節約できます。ただし、中間結果イメージの書き込みと読み取りの必要性をスキップすることで、ディスク I/O 時間を節約できます。

最初のモンタージュを実現するために、コードでは 2 つの異なるmontageコマンドと 1 つのコマンドを使用します。convert最後に、1 つmontageを使用して前の結果を背景に配置します。

頭のてっぺんから、最初の 3 つのコマンドを 1 つのコマンドに結合する方法をすぐに思いつくことができます。中間結果を背景に配置する最後のモンタージュ ステップは、簡単に正しく行うことができず、時間の節約にもなりません。したがって、私は今のところそれを開いたままにします。

残念ながら、ソース画像へのリンクを提供していません。この質問に答えるために、私は自分のものを作成しなければなりませんでした。また、私の回答の有効性を実証するのにも役立ちます。

4 つの 800x600 ピクセル サイズの PNG を作成するために、コマンド ラインで少しの PostScript コードを指定して Ghostscript を使用しました。

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

最初の 2 つのコマンドをマージします。

以下は、単一のコマンドを考え出すための私の最初のショットでした。最初の 2 つのコマンドが出力するのと同じ結果が得られるはず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:

2 番目のコマンドの後の中間結果 (左) と比較した、私のコマンドの結果 (右) を次に示します。

 

それで、私予想していたことが 1 つ起こりました: 各画像の間に青いスペーサーがあります。デバッグ上の理由から色を付けました。これは、色変数をnone(透明) に設定することで修正できます。

予想していなかったものと、結果の画像を開いた後に初めて発見したものf.png

  • 私の背景は透明ではなく白でした。これは、適切な場所に追加することで修正できます-background none

  • 列内の個々の画像の間隔が狭すぎて、15 ピクセルしかありません。これはline.png、OP の中間ファイルの間隔が 15 ピクセルではなく 30 ピクセルであるためです。-geometry +0+15列を作成するための彼のパラメーターは、各画像の上部と下部に15montageピクセルを追加します。私のコマンド(の代わりに使用)では、同じ効果を持つ追加の設定は許可されていません。しかし、これはコマンドにスペーサーを追加することで修正できます。convert ... -appendmontage-geometryxc:{_col1}

最初の 3 つのコマンドをマージします。

というわけで、次の繰り返しです。OPからの3番目のコマンドの効果を統合します。これは+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:

ここでも、私予想していたことが 1 つ起こりました。

  • 2 つの列の間の赤いスペーサーは、列の間にではなく右側にありました。+append-edを取得した最後の 2 つの画像を交換することで、これを修正できます。+swapこれは、適切な場所に演算子を追加することで実行できます。

  • また、最初の列の画像間の間隔と同じことが、列間の間隔にも適用されます。これを 2 倍にする必要があります。

  • +append-ed 列の左右 に同じスペース (45 ピクセル) が追加されていないことは、現時点では気にしません。

したがって、ここにもう 1 つの繰り返しがあります。

_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、3 番目のコマンドの後に OP コードによって作成された中間体です。
  • 左側は、私のコマンドで作成されたイメージ モンタージュです。

 

今欠けているのは、1 つのコマンドにまとめた個々の操作の内訳を含む説明です。

  • \(:
    これにより、一部の画像の「横向き」処理が開始されます。この横方向の処理の結果は、メインの ImageMagick プロセスに再び挿入されます。バックスラッシュはシェルが解釈しないようにするために必要です。
  • \)
    横向き処理を終了します。
  • -size 15x15:
    次に塗りつぶすキャンバスのサイズを設定します。
  • xc:${_col1}:
    指定した色でキャンバスを塗りつぶします。 xc:は単に のエイリアスですがcanvas:、入力する方が高速です。
  • -clone 0:
    これにより、ロードされた画像スタックに現在ある最初の画像のコピーが作成されます。この場合は のコピーですt1.png-clone 1コピーt2.png-clone 2コピーt3.pngなど -clone、または+clone横方向の処理チェーン内で最適に機能するため、前述の と の\(使用\)
  • -append:
    この演算子は、現在ロードされているすべての画像を垂直方向に追加します。この場合、これらはt1.png, ...の 4 つのコピーですt4.png
  • +duplicate:
    この演算子は に似てい+cloneます。現在ロードされている画像スタックの最後の画像をコピーします。この場合、最後の画像 (横方向のパイプライン内に残っているのは 1 つだけ) は、前の-append操作の結果です。この操作により、赤いスペーサーで区切られた 4 つの画像の最初の列が作成されました。
  • +append:
    この演算子は、現在ロードされているすべての画像を水平方向に追加します。現在、3 つのイメージがあります。-append操作の結果、 によって作成されたコピー+duplicate90x90サイズ変更されたxc:キャンバスです。
  • +swap:
    このオペレーターは、現在ロードされているスタックの最後の 2 つのイメージを交換します。
  • +write:
    このオペレーターは、現在ロードされているスタックからすべてのイメージを書き出します。複数の画像がある場合、これらの画像は指定された名前で書き込まれますが、番号が追加されます。これは、複雑な ImageMagick コマンドをデバッグするための優れたツールです。+write操作が終了した後、以前に読み込まれた画像がすべてスタックに残るため、これは素晴らしいことです。これらのイメージは変更されず、処理を続行できます。ただし、これで終了です。この場合は続行しません。したがって、横方向のプロセスを で閉じ\)ます。
  • null:
    横方向のプロセスを閉じたので、ImageMagick は結果の画像を横方向からメイン パイプラインに再び入れます。+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 の最初の 3 つのコマンドを 100 回繰り返してループで実行しました。次に、自分のコマンドも100回実行しました。

結果は次のとおりです。

  • OP 最初の 3 つのコマンド、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 ピクセルのキャンバスを 1 つ作成し、これを画像スタックに配置します。

  3. 3 番目に、そのキャンバスの追加のコピーを 7 つ作成します。スタックには 12 個の画像があります。4 つの入力ファイル、1xc:つの -canvas、および 7 つのキャンバスのコピーです。

  4. 次に、一連の-insert N操作を使用します。この-insert N操作は、画像スタックの順序を操作します。スタックから最後の画像を削除し、画像のインデックス位置に挿入しますN-insert一連の操作が開始されると、スタックには 4 つの画像 ( 、t1t2t3)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 ピクセルなので、最初のコマンドと同じ間隔になります。

  5. 次に、同様の横方向の処理が行われます。今回+cloneは、前の-append操作の結果に対して、水平スペーサーを作成します。

  6. 最後に、 (前のプロセスと横方向のプロセス+appendの結果に作用する) が最終結果 を作成します。-appendf4.png

この最後のコマンドのベンチマークを行ったところ、各コマンドを 100 回繰り返して次の結果が得られました。

  • 更新前の最初のコマンド、100 回: 48.3 秒
  • 更新後の私の最後のコマンドの説明、100 回: 46.6 秒

したがって、これらの数値を信頼したい場合、速度の向上はそれほど顕著ではなく、約 3% 向上しています。

(ベンチマークの競争をより公平にするために、同じマシンで両方のループを並行して実行したことに注意してください。並行して実行することにより、両方が同じ CPU と I/O 負荷に対処する必要があり、これにより発生する可能性があります。それ自体だけでなく、マシン上で同時に発生している他のプロセスによっても。)

于 2015-04-19T16:09:22.277 に答える
0

ここに別の答えがあります。マーク・セッチェルのアプローチからインスピレーションを得ました。ここで、新しいコマンド パイプラインを考え出します。違いは、私が...

  • ...パイプラインで 3 つmontageのコマンドのみを使用 (マークはパイプラインで 1秒montageと 2convert秒を使用)、そして私は
  • ...OPの3つの異なるコマンドの出力を正確に再現します。

入力として、Ghostscript コマンドによって作成された 4 つのイメージを別の回答で使用しましたt{1,2,3,4}.png

-texture適切な背景画像を作成するために、Mark のコマンドを使用しましたが、変更を加えました。OP のオペレーターが有意義に使用できるように、画像を少し小さくしました。

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

次に、3 つのコマンド/スクリプトすべてのベンチマークを行いました...

  1. ...シェルスクリプトとしてのOPコマンド、
  2. ...マークのアプローチは、上で概説したように修正されていますが、
  3. ...私の最初のアプローチは、背景画像を追加して修正されました。

ベンチマークのために、それぞれ 100 回繰り返しました。その際、各スクリプト/コマンドを別々のターミナル ウィンドウでほぼ同時に開始しました。テスト マシンの CPU には 4 つのコアがあります。そのため、コマンドが並行して実行されている場合、それぞれが同じ CPU 負荷と I/O 負荷を処理し、リソースを互いに競合させなければなりませんでした。これは、私が思いついた最も「公平な」アドホック パフォーマンス テストのセットアップです。

photo_test1.jpgまた、3 つのテスト ( 、ms+kp_test2.jpgおよびkp_test3.jpg) によって作成された出力ファイルがピクセル単位で (ほぼ) 同一であることも確認しました。出力が(JPEGの代わりに、OPが要求した方法で)PNGに切り替えられた場合、これらの小さな違いも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
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
システム 0m13.026s

約95秒。

元のコマンドと比較して約 72% の時間消費。

于 2015-04-19T20:48:09.010 に答える