ImageMagickの使い方サンプル

書いた人: lpre_ys

前置き

中の人がtktk時によく使っている、ImageMagickのコマンドについての解説ドキュメントです。
magic-potの為だけに入れるのは勿体ないツールなので、ぜひ使ってみてください。

以下のコマンドについてご質問等ございましたら、お気軽にどうぞ。

動作環境

中の人は、WSLという、Windows上でLinux(Ubuntu)が動くヤツをつかって開発しています。
序盤の、1~2行で終わるコマンドの場合、Windowsでもコピペで動きますが、複数行にわたるコマンドは書き替えが必要になります。
書き替えが必要なコマンドに対しては、初回に限り、Windows版のコマンドも載せていますが、全てのコマンドには対応しておりません。
Windows版のコマンドが無い物については、ご自身で書き換えてご利用ください。

事前準備

一部コマンドでは、中間画像を生成しています。
それらは邪魔なので、作業フォルダ直下にある、tmpフォルダに生成するようにしています。事前に用意しておいてください。

その他、必要とする画像は、全て作業フォルダ直下に置いてある想定です。

また、コマンドは全て、作業フォルダに入ってから実行しています。

画像の結合

エフェクト画像の結合

こんな感じで、エフェクト用の連番画像が用意されていた場合、次のコマンドで、1発でツクール2k用のエフェクト画像に結合できる。
※パレットの並び替えは、ImageMaigckには無理なので、読み込み時に透明色を指定するなり、magic-potで一括変換するなりしてください。

エフェクト-in

エフェクト-in

コマンド、結果

magick montage v7_effect_* -geometry +0+0 -tile 5x png8:v7_effect.png
エフェクト-out

エフェクト-out

フォント周りのエラーが出ますが、無視して問題ありません。

-tile 5x で、入力画像を、横5枚で並べて結合、という指示。
右端、出力画像名の前のpng8:は、インデックスカラーPNGで保存、という意味です。

顔グラ画像の結合

エフェクト同様、連番画像を用意しておけば、次の画像で合成可能。

コマンド

magick montage face_* -geometry +0+0 -tile 4x4 -resize 192x face.png

顔グラの場合、減色は別ソフトでやるので、インデックスカラーの指定は行わない。

顔グラは、2倍 or 4倍のサイズで作画することが多いので、リサイズもこのタイミングで行っている。
-resize 192xで、横幅192に縦横比を保ってリサイズ、という指示。

あとは、別ソフトで減色しておわり。

画像の分割

エフェクト画像

こんな感じのエフェクト画像を、1コマずつバラしたい場合

エフェクト-out

エフェクト-out

コマンド、結果

magick v7_effect.png -crop 96x96 v7_effect.png
エフェクト-crop

エフェクト-crop

-cropオプションの引数として、1コマの画像サイズを指定する。

ファイル名はには、自動的でハイフンと連番が付与される。
連番とファイル名の区切りを_にしたい、など、出力ファイル名のフォーマットを変える場合は下記のように、%dを使うと、そこに連番が入ったファイル名で生成される。

magick v7_effect.png -crop 96x96 v7_effect_crop_%d.png
エフェクト-crop2

エフェクト-crop2

歩行グラ

歩行グラのように、縦横のサイズが違う画像も、もちろん可能

コマンド

magick 歩行グラ.png -crop 24x32 output.png

-cropに限らず、ImageMagickのコマンドでは、縦横は「横x縦」という表記になります。

少し特殊な画像結合

余白を下に足して縦に結合する

1コマずつ切り出した、下記のような画像元に

次のような、ツクールの「ピクチャの移動」で切り替わったように見せるため、縦長の画像を作るには。
※スペースの都合上、縦に縮めて表示しています。Chromeの場合、右クリック->新しいタブで画像を開く、とすると原寸表示できます。

コマンド、結果

magick mogrify -background '#00FF00' -gravity north -extent 100x240 revive-*.png
magick revive-*.png -append revive-v.png

※入力元画像を上書きします!

2つのコマンドにわけて実行しています。

100が画像の横幅です。
画像サイズに合わせて変更してください。

-gravity northが、余白生成時に、元画像をどの位置に置くかを表しています。
north(北)なので、上側。
この指定を忘れると、画像が中央になってしまいます。

余白を右に足して横に結合する

先の例を、次のように、横長に作りたい場合

sprite-h

sprite-h

コマンド

magick mogrify -background '#00FF00' -gravity west -extent 320x16 revive-*.png
magick revive-*.png +append revive-h.png

16が画像の縦幅です。
画像サイズに合わせて変更してください。

+append と、appendオプションを+にする事により、入力された複数の画像を、横に結合できます。

今回は、画像を左寄せにして余白を作りたいので、-gravity westを指定しています。

画像に描画する

ここから、コマンドが複雑になるため、Windowsのコマンドプロンプトでは動作しなくなります。
初回のみ、Windows版のコマンドと、書き替えが必要な個所について説明を入れておきますので、以後、必要であればご自身で書き換えてご利用ください。

シンプルな黒文字画像

こんな感じの、シンプルに、黒で文字が書かれた画像を作るコマンド

テキスト1

テキスト1

事前準備

フォントは、フリーフォントのPixelMplusの12ポイントの物を利用します。

※MSゴシックだとドットフォントの利用が上手く出来なかったので、似たような代替フォントで対応。

インストールは下記サイトからどうぞ。
http://itouhiro.hatenablog.com/entry/20130602/font

コマンド

Linux(Bash)
magick -size 200x200 xc:#00FF00 \
  -font '/mnt/c/Windows/Fonts/PixelMplus12-Regular.ttf' -pointsize 12 +antialias \
  -fill '#000000' -gravity north -annotate 0x0 'テスト文字\n改行はこう書くと出来る' \
  -trim text1.png
Windows(コマンドプロンプト)
magick -size 200x200 xc:#00FF00 ^
  -font "c:/Windows/Fonts/PixelMplus12-Regular.ttf" -pointsize 12 +antialias ^
  -fill "#000000" -gravity north -annotate 0x0 "テスト文字\n改行はこう書くと出来る" ^
  -trim text1.png

Windows向けに書き換える場合、下記の対応が必要になります

解説

フォント指定がポイント。

フルパスで指定しておけば、間違いなく動きます。 また、フォントに限らず、描画系は+antialiasで、アンチエイリアスをOFFにできます。

センタリングではなく、左寄せにしたい場合は、-gravity north-gravity northwestに変えれば出来る。

ツクールっぽい、グラデーションに影がついた文字画像を作るコマンド

こういう文字画像を作る

テキスト2

テキスト2

事前準備(グラデ画像の作成)

ツクールのシスグラを基に、下記のような画像を作っておく。色は使いたい色で。

文字色

文字色

このとき、画像の縦幅を13pxにしておくと、改行有のテキストでもいい感じになります。
違うフォントだったら、うまいこといい感じに調整してみてください。

コマンド

magick -size 200x260 xc:#00FF00 \
  -font '/mnt/c/Windows/Fonts/PixelMplus12-Regular.ttf' -pointsize 12 +antialias \
  -fill '#1d1d1e' -gravity north -annotate 0x0+1+1 'テスト文字\n改行はこう書くと出来る' \
  -fill 'textcolor.png' -gravity north -annotate 0x0 'テスト文字\n改行はこう書くと出来る' \
  -trim text2.png

初期キャンバス(200x260)の大きさは、文字がはみ出ない程度の大きさなら何でもよい。

ただし、初期キャンバスの縦幅は、13(textcolor.pngの縦幅)の倍数にしておくこと。
そうしないと、グラデーションがずれる。

背景に画像を指定した文字画像を作る

背景を、単色ではなく画像にする。

ツクールのウィンドウのように、繰り返し敷き詰めるタイプと、拡大表示するタイプの2種紹介。

事前準備

背景画像にしたい画像を用意しておく

今回は、サンプルとしてこんな小さな画像を用意

背景1

背景1

コマンド

繰り返し版
magick -size 200x260 xc:transparent \
  -font '/mnt/c/Windows/Fonts/PixelMplus12-Regular.ttf' -pointsize 12 +antialias \
  -fill '#1d1d1e' -gravity north -annotate 0x0+1+1 'テスト文字\n改行はこう書くと出来る' \
  -fill 'textcolor.png' -gravity north -annotate 0x0 'テスト文字\n改行はこう書くと出来る' \
  -trim tmp/text.png
magick tmp/text.png -compose dst-over -size 200x260 tile:test-bg.png -composite text3-1.png
拡大版

拡大版は、テキスト画像のサイズを把握しておく必要がある。
今回は、131x26になりました。

実際はシェルなりnode.jsなりで、画像サイズの取得も自動化して使っています。

magick -size 200x260 xc:transparent \
  -font '/mnt/c/Windows/Fonts/PixelMplus12-Regular.ttf' -pointsize 12 +antialias \
  -fill '#1d1d1e' -gravity north -annotate 0x0+1+1 'テスト文字\n改行はこう書くと出来る' \
  -fill 'textcolor.png' -gravity north -annotate 0x0 'テスト文字\n改行はこう書くと出来る' \
  -trim tmp/text.png
magick tmp/text.png -compose dst-over \( test-bg.png -resize 131x26! \) -composite text3-2.png

画像にツクールのボーダーを付ける

普通の画像に、ツクールのウィンドウみたいなボーダーを付ける。

今回は、"in.png"という名前で、下記のような画像を用意。
画像の縦横の大きさを調べておく。(今回の場合、150x100)

ボーダーin

ボーダーin

出力結果は下記を目指す。

ボーダーout

ボーダーout

事前準備(system-border.pngの用意)

ツクールのシスグラ(system.png)から、枠の部分(最上段、左から2番目)だけの画像を作り
さらにそこから、枠線以外を透過した画像を作っておく。

このとき、画像が入る部分は透明に、ツクール利用時に透過したい部分は、透過色(この例の場合真緑)にしておくのがポイント。
今回の場合、四角の1ドットはツクール利用時に透過させたいので、真緑にしています。

ボーダーsystem

ボーダーsystem

コマンド

画像ベースのフレーム(ボーダー)生成は、コマンド1発で出来ないので、1箇所ずつ地道に合成している。

magick system-border.png -repage 60x60+14+14 -crop 20x20 tmp/border.png
magick in.png \
  \( tmp/border-1.png -resize 150x6! \) -gravity north -composite \
  \( tmp/border-7.png -resize 150x6! \) -gravity south -composite \
  \( tmp/border-3.png -resize 6x100! \) -gravity west -composite \
  \( tmp/border-5.png -resize 6x100! \) -gravity east -composite \
  tmp/border-0.png -gravity northwest -composite \
  tmp/border-2.png -gravity northeast -composite \
  tmp/border-6.png -gravity southwest -composite \
  tmp/border-8.png -gravity southeast -composite \
  out.png

ざっくり説明するとこんな感じ

いちいち縦横計算するの面倒なので、中の人は下記のようなシェルスクリプトを用意しておいて──
※実際に使っているスクリプトなので、ファイルパス等は上記とは変わっています

#!/bin/bash

TARGET=$1

set `magick identify -format '%[width] %[height]' ${TARGET}`
WIDTH=$1
HEIGHT=$2


magick system-border.png -repage 60x60+14+14 -crop 20x20 +repage tmp/border.png
magick -size ${WIDTH}x${HEIGHT} xc:#00FF00 \
  -composite ${TARGET} -gravity center \
  -composite \( tmp/border-1.png -resize ${WIDTH}x6! \) -gravity north \
  -composite \( tmp/border-7.png -resize ${WIDTH}x6! \) -gravity south \
  -composite \( tmp/border-3.png -resize 6x${HEIGHT}! \) -gravity west \
  -composite \( tmp/border-5.png -resize 6x${HEIGHT}! \) -gravity east \
  -composite tmp/border-0.png -gravity northwest \
  -composite tmp/border-2.png -gravity northeast \
  -composite tmp/border-6.png -gravity southwest \
  -composite tmp/border-8.png -gravity southeast \
  -composite ${TARGET}

下記のコマンド1発で、ボーダーが付くようにしてたりします。(上書き)

sh border.sh in.png

応用-ツクールのメッセージウィンドウ風画像

「画像に描画する」の項目をまとめて応用すると、ツクールのメッセージウィンドウ風のテキスト画像も、コマンドで生成できるようになります。

本編でよく使ってるコレとか、全部自動生成です。

メッセージ風

メッセージ風

コマンド

実際は、文字の内容は外部ファイルから入力したり、画像サイズはスクリプトで取得したりして、数十個まとめて一気に生成してます。
※上の例みたいな感じで、node.jsを使ったスクリプトが組んである。入力はyamlを使用。

今回の場合、テキストだけの画像が84x25になっていました。
最終的な画像サイズは、そこに余白分を足したサイズにしています。

magick -size 200x260 xc:transparent -font /mnt/c/Windows/Fonts/PixelMplus12-Regular.ttf -pointsize 12 +antialias \
 -fill '#1d1d1e' -gravity north -annotate 0x0+1+1 'ホワイトハウス\n- 通用口 -' \
 -fill textcolor.png -gravity north -annotate 0 'ホワイトハウス\n- 通用口 -' \
 -trim tmp/text0.png

magick system-border.png -repage 60x60+14+14 -crop 20x20 +repage tmp/border.png
magick -size 108x37 xc:#00FF00 \( system-bg.png -resize 108x37! \) \
  -composite tmp/text0.png -gravity center \
  -composite \( tmp/border-1.png -resize 108x6! \) -gravity north \
  -composite \( tmp/border-7.png -resize 108x6! \) -gravity south \
  -composite \( tmp/border-3.png -resize 6x37! \) -gravity west \
  -composite \( tmp/border-5.png -resize 6x37! \) -gravity east \
  -composite tmp/border-0.png -gravity northwest \
  -composite tmp/border-2.png -gravity northeast \
  -composite tmp/border-6.png -gravity southwest \
  -composite tmp/border-8.png -gravity southeast \
  -composite text4.png

UI画像の自動生成

基本編

例えば、こういうグラデーションを使ったボタン画像があるとして。
※文字・透過色は後で合成する想定

ボタン1

ボタン1

下記コマンドで、赤いボタンを作ることが出来る。

コマンド・結果

magick button1.png -modulate 100,100,160 button1-1.png
ボタン1-1

ボタン1-1

解説

-modulate 100,100,160の部分で、各種レタッチソフトの「色相・彩度・明度」をやっている。
ただし、順番は、"明度、彩度、色相"の順番。

今回は、色相だけ赤寄りにずらしている。

引数を変えれば、下記のように違う色にしたり、グレースケールにしたりもできる。

magick button1.png -modulate 100,100,50 button1-2.png
ボタン1-2

ボタン1-2

magick button1.png -modulate 100,0,100 button1-3.png
ボタン1-3

ボタン1-3

合成モードを利用した、すこし複雑な合成

基本編で使ったボタン画像は、グラデーションの色相が統一されていたため、単純に色相をずらすだけでも、違和感無い画像が生成出来た。

ただし、下記のように、「明るい部分は黄色」「暗い部分を紫」に寄せたグラデーションの場合、色相をずらすだけでは意図した色味にならない。

ボタン2

ボタン2

例)基本編の最初のコマンドで、赤色・緑色に変更した画像

ボタン2-1, ボタン2-2

※わかりづらいが、光の色相も一緒にずれている

そこで、下記3つの画像を用意。
まず、ベース画像の色相のみを変更。
その後、各種レタッチソフトの「レイヤー合成」のように、合成モードを指定して重ね合わせることにより、どの色でも光が黄色く、影が紫に見えるような画像を生成する。

今回は、光を「オーバーレイ」、影を「乗算」で合成している。

コマンド

magick button3.png -modulate 100,100,160 \
  button3-l.png -compose overlay -composite \
  button3-s.png -compose multiply -composite \
  button3-1.png

結果

ボタン3-1

ボタン3-1

基本編の緑ボタンと同じように、modulateの色相を50にずらした、緑ボタンはこんな感じ。

ボタン3-2

ボタン3-2

単色グラデ、複数色グラデ(色相ずらし)、複数色グラデ(合成モード利用)の3つを並べるとこんな感じ。
一番右が、一番自然に見えませんか?

ツクール2000系で、疑似的に4倍の色数でピクチャ表示を行う

主にイベントカットで利用する、ピクチャの色数を強引に増やす方法。
※色数が多く、かつ、綺麗に表示したいピクチャ向け。

イベントカットのように、複数の色をグラデーションで表現している場合、256色ではどうしても色数が足りず、解りやすいディザやノイズが出てしまう。
そこで、4枚のピクチャを重ね合わせる事により、疑似的に4倍の色数のピクチャ表示を行う手法。

以下、比較画像。左が256色、右が4倍の1024色の画像となる。

256色 1024色

等倍ではわかりづらいが、帽子のあたりに顕著にディザが出ている。

256色4倍 1024色4倍

原理

下記のように、似た色ごとに4つにわけたピクチャを用意し、それぞれ透過ありで4枚同時に重ねて表示している。

合成前1 合成前2

合成前3 合成前4

※このゲーム内でも同じ画像を使ってます。

この画像生成に、ImageMagickを利用しています。

ImageMagickコマンド

コマンドだけでは1発で終わらせられないので、分割して記載。

入力画像は、リサイズのみ終わっており、色自体はフルカラーの状態で用意(lr_top-base.png)

ベース画像

ベース画像

まずは、4色に減色した画像の生成。

magick lr_top-base.png +dither -colors 4 tmp/lr_top.png
4色

4色

続いて、減色後の画像から、使用している色情報を取得

magick convert tmp/lr_top.png -format %c histogram:info:

コマンド実行結果

49279: ( 53, 88, 83) #355853 srgb(53,88,83)
 8137: ( 60, 55, 73) #3C3749 srgb(60,55,73)
 7814: (122,118,128) #7A7680 srgb(122,118,128)
11570: (198,189,180) #C6BDB4 srgb(198,189,180)

上記で取得したカラーコードを使って、画像の指定した色「以外」を削除するためのマスク画像を作成。

magick tmp/lr_top.png -fill transparent -opaque '#355853' matte:tmp/mask.matte
magick tmp/mask.matte -negate tmp/mask.png
マスク画像

マスク画像

元のフルカラー画像に、上記のマスク画像をもとに、ツクール上の透過色(真緑)を合成。

magick -size 320x240 xc:#00FF00 \( tmp/mask.png -alpha copy \) \
  -alpha Set -composite -fill transparent -opaque '#FFFFFF' PNG32:tmp/tmp.png
magick lr_top-base.png tmp/tmp.png -composite lr_top-1.png

後は、残り3色に対しても同様の手順で画像を生成し、最後にお好きな減色ツールで減色すれば、4倍色用ピクチャの完成。

シェル版

上記を基に、自動で4倍色用ピクチャを生成するシェルスクリプトは下記。
IN_DIRに入れた画像が、まとめて変換されるようになっている。
参考までにどうぞ。

リサイズと減色も面倒なので、このスクリプト上で行うようになっている。

#!/bin/bash

IN_DIR=4xcut/in/
OUT_DIR=4xcut/out/
RESIZED_DIR=4xcut/resized/
TMP_DIR=4xcut/tmp/

rm ${OUT_DIR}*.png ${RESIZED_DIR}*.png ${TMP_DIR}*.png

for img in ${IN_DIR}*.png; do
  filename=`basename "${img}"`
  magick ${img} -resize 320x -filter Lanczos2 -unsharp 0x0.75+0.5+0.25 ${RESIZED_DIR}${filename}
  magick ${RESIZED_DIR}${filename} +dither -colors 4 ${TMP_DIR}${filename}

  i=1
  magick convert ${TMP_DIR}${filename} -format %c histogram:info: | while read -r line;
  do
    hex=`echo $line | sed -e "s/^[^#]*\(#......\).*$/\1/"`;
    magick ${TMP_DIR}${filename} -fill transparent -opaque ${hex} matte:${TMP_DIR}mask.matte
    magick ${TMP_DIR}mask.matte -negate ${TMP_DIR}mask.png
    magick -size 320x240 xc:#00FF00 \( ${TMP_DIR}mask.png -alpha copy \) -alpha Set -composite -fill transparent -opaque '#FFFFFF' PNG32:${TMP_DIR}tmp.png
    magick ${RESIZED_DIR}${filename} ${TMP_DIR}tmp.png -composite ${OUT_DIR}${filename%.*}-${i}.png
    i=$((i + 1))
  done;

done

pngquant 255 ${OUT_DIR}*.png --ext .png --force --speed=1

あとがき

中の人が、tktkの時に使っているコマンドは、だいたい上記を応用したりなんやかんやしてる物になります。

とにかく強力なツールなので、ぜひ、使ってみてください。

私自身、ImageMagickは、毎度毎度ドキュメントを漁りながら、試行錯誤して使っている状況ですので、もっとシンプルな記述方法があったりするかもしれません。
あくまでも一例として、参考にしていただければ幸いです。

2020.5.某日 lpre_ys