BashスクリプトとImageMagickで画像処理を自動化する方法
ライターの仕事は言葉だけではありません。特にテクニカルライティングでは、技術やプロセスを伝えるために大量のスクリーンショットを扱うことがよくあります。さらに、公開プラットフォームごとに画像フォーマットやファイルサイズなど、異なる要件が課されることも少なくありません。
ITコンサルタント兼システムエンジニアとして、私はクライアントへの納品物として数多くの技術文書を作成してきました。当時はMicrosoft Word(.doc)形式が求められるのが一般的でした。文書はコンテンツを追加するたびにあっという間に肥大化していきます。初期のスクリーンショットはビットマップ(.bmp)形式が主流で、ファイルサイズが非常に大きくなりがちでした。サーバーへのOSインストール手順を説明する文書などは、巨大なファイルになってしまうこともありました。
画質を読み取り可能なレベルに保ちながら画像を縮小するのは、骨の折れる作業でした。ビットマップはJPEG、のちにPNGへと変換可能になりましたが、オフィススイートをLibreOfficeに切り替えた後も、画像編集は依然として課題でした。幸い、現在のスクリーンショットツールの多くは、PNGのように軽量なフォーマットで保存してくれます。
Opensource.comでは、記事に使用する画像に対して一定の制限を設けています。そこで私は、記事用の画像を準備するためのシンプルな3ステップの手法を確立しました。1つ目はステージングの工夫です。ウィンドウのサイズ変更やフォントの調整などを行います。残りの2つは非常に反復的な作業でした。それは「画像が600ピクセルの幅制限を超えないことの確認」と「枠線(ボーダー)の追加」です。
prepimg.shスクリプト
こうしたタスクを自動化するために、prepimg.shというBashスクリプトを作成しました。このスクリプトでは、ImageMagickスイートに含まれる2つのツールを使用しています。
画像のリサイズ
スクリーンショットユーティリティは、Screenshot-20210923222312.pngのような汎用的な名前で、Picturesディレクトリに画像を保存します。prepimg.shスクリプトは、このディレクトリ内のファイルのピクセル幅をチェックし、制限を超えているものをリサイズします。ここでは、ImageMagickスイートのidentifyコマンドを使って画像の幅(%w)を取得します。
$ identify -format %w Screenshot-20210903202655.png
1217
取得した幅の値は変数Wに代入され、600ピクセルという上限値との比較に使われます。上限値自体は変数$MAXWIDTHで自由に設定できます。幅がMAXWIDTHを超えていると判定された場合は、ImageMagickのもうひとつのツールconvertを呼び出して画像の幅を縮小します。以下は、スクリプト内の画像処理関数の抜粋です。
if [ "$W" -gt "$MAXWIDTH" ]
then
[[ $VERBOSE -gt 0 ]] && echo "${1} is ${W} - reducing"
convert -resize "${MAXWIDTH}" \
"${SCREENSHOTS}"/"${1}" \
"${READY}"/"${1}"
...
画像は必要に応じて縮小され、$READY変数で定義された別のディレクトリに保存されます。このとき、画像は実際には598ピクセルと、わずかに小さめにリサイズされます。これは、次に説明する枠線の追加分のスペースを確保するためです。
画像への枠線の追加
画像によっては、Webページの背景に溶け込んでしまうことがあります。これは、画像の端までの前景色がサイトの背景色と同じになっているために起こります。以下がその例です。

枠線なしの画像。(CC BY-SA 4.0)
上の画像を見ると、画像の端がどこにあるのかまったく分かりません。この問題は白に限った話ではなく、各サイトが採用しているテーマカラーによって起こり得ます。背景が赤で画像の端も赤ならば、同様の問題が発生します。私のスクリプトでは、convertツールを使ってこの問題を解決しています。-borderオプションを付けると、各画像ファイルに1ピクセル幅の枠線が追加されます。このオプションだけでも十分ですが、-bordercolorオプションで枠線の色も指定しています。以下はその例です。
convert -bordercolor black -border 1 Screenshot-20210903202655.png
下は同じ画像に枠線を付けたものです。ぐっと見栄えが良くなりましたね。

枠線付きの画像。(CC BY-SA 4.0)
画像の完成
スクリプトではforループを使ってスクリーンショットディレクトリを走査し、ファイルごとにprocess_img関数を呼び出します。この関数が幅の調整と枠線の追加の両方を担当します。完全版のコードでは、「ファイルが本当に画像かどうかの確認」などのサニティチェックも行っています。
process_img() {
# ファイルが画像かどうかを検証し、寸法を取得する
if file "${SCREENSHOTS}"/"${1}" | grep -qE 'image|bitmap'; then
[[ $VERBOSE -gt 0 ]] && echo "${1} is an image"
W=$(identify -format %w "${SCREENSHOTS}"/"${1}")
else
echo "File ${SCREENSHOTS}/${1} is not an image."
W=0
fi
# リサイズと枠線の追加
if [ "$W" -gt "$MAXWIDTH" ]
then
[[ $VERBOSE -gt 0 ]] && echo "${1} is ${W} - reducing"
convert -resize "${MAXWIDTH}" \
-bordercolor $BORDER \
-border 1 \
"${SCREENSHOTS}"/"${1}" \
"${READY}"/"${1}"
else
convert -bordercolor $BORDER \
-border 1 \
"${SCREENSHOTS}"/"${1}" \
"${READY}"/"${1}"
fi
}
スクリプトの最後のステップでは、処理済みのファイルを$READY変数で指定されたReadyというサブディレクトリに保存します。こうすることで、元のファイルはそのまま残り、後で再利用できます。
使い方
prepimg.shには、引数とその使い方を表示するヘルプ機能も備わっています。
$ prepimg.sh -h
prepimg.sh Version 0.7 - written by Alan Formy-Duval
prepimg.sh [OPTIONS]
--verbose, -v Be verbose
--directory, -d Screenshot directory (default: /home/alan/Pictures/Screenshots)
--ready, -r Ready directory (default: /home/alan/Pictures/Screenshots/Ready)
--border, -b Border color (default: black)
主なオプションは以下の通りです。
--verbose, -v:詳細なログを出力する--directory, -d:スクリーンショットディレクトリを指定(デフォルト:/home/alan/Pictures/Screenshots)--ready, -r:処理済み画像の保存先ディレクトリを指定(デフォルト:/home/alan/Pictures/Screenshots/Ready)--border, -b:枠線の色を指定(デフォルト:black)
コードとまとめ
本記事で紹介したコードは抜粋であり、疑似コードとして捉えてください。完全なスクリプトは筆者のGitリポジトリで公開されています。
ImageMagickスイートは、画像を操作するための強力なツール群です。今回の小さなスクリプトに組み込んだ機能以外にも、さまざまなことが実現できます。ライターのJim Hallも最近、ImageMagickを使って画像をリサイズする方法について記事を執筆しています。
Bashスクリプトでも、CやPythonといった他の言語でも、自動化は強力な味方になります。ほんの少しのコードを書くだけで、多くの手間や悩みが解消され、時間をより有意義に使えるようになる――それを私は何度も経験してきました。
-
自宅プリンターで両面印刷を自動化!シンプルなBashスクリプトの作り方
私の自宅にはレーザープリンターがあります。Hewlett Packard LaserJet Pro CP1525nwカラープリンターはやや古いモデルですが、何年もの間、安定してカラー印刷をこなしてくれる頼れる働き者です。数年前にRaspberry Piをプリントサーバーとして導入し、家庭内ネットワークに接続しました。 このLaserJetはホームオフィスにとって大きな戦力です。昨年会社を立ち上げて以来、クライアントとの打ち合わせやワークショップ、研修会用の資料印刷には、ずっとこの小さなレーザープリンターにお世話になってきました。 ただ、ひとつだけ不満があります。このプリンターは片面印刷にしか対
-
実用的な5つの例で学ぶBashスクリプト入門チュートリアル
Unix SedやUnix Awkシリーズと同様に、本シリーズではBashスクリプティングに関する記事を複数回にわたって公開します。実用的なサンプルを交えながら、Bashスクリプティングのテクニックを幅広く解説していきます。 シェルとは、ユーザーが入力したコマンドを解釈して実行するプログラムです。コマンドはユーザーが直接入力するか、「シェルスクリプト」と呼ばれるファイルから読み込まれます。 ユーザーからの入力を直接読み取る場合、そのシェルは対話型(インタラクティブ)シェルと呼ばれます。 一方、ファイルからコマンドを読み込んで実行する場合は非対話型(ノンインタラクティブ)シェルと呼ばれます。この