まず、これを言わせてください:入力画像が非常に大きい場合、個別のコマンドを 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 ... -append
montage
-geometry
xc:{_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
操作の結果、 によって作成されたコピー+duplicate
、90x90
サイズ変更された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
このコマンドのアーキテクチャは少し異なります。
まず、すべての画像を読み込みます。それ-flop
は彼らであり、それは-resize
彼らです。
次に、15x15 ピクセルのキャンバスを 1 つ作成し、これを画像スタックに配置します。
3 番目に、そのキャンバスの追加のコピーを 7 つ作成します。スタックには 12 個の画像があります。4 つの入力ファイル、1xc:
つの -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
f4.png
この最後のコマンドのベンチマークを行ったところ、各コマンドを 100 回繰り返して次の結果が得られました。
- 更新前の最初のコマンド、100 回: 48.3 秒
- 更新後の私の最後のコマンドの説明、100 回: 46.6 秒
したがって、これらの数値を信頼したい場合、速度の向上はそれほど顕著ではなく、約 3% 向上しています。
(ベンチマークの競争をより公平にするために、同じマシンで両方のループを並行して実行したことに注意してください。並行して実行することにより、両方が同じ CPU と I/O 負荷に対処する必要があり、これにより発生する可能性があります。それ自体だけでなく、マシン上で同時に発生している他のプロセスによっても。)