Bash関数: ImageMagickで楽に画像をトリミングする

Bash関数: ImageMagickで楽に画像をトリミングする

Intro

Win + Shift + S複数回まったく同じ領域を切り抜くのは難しい……。
そうだ、ImageMagickを使おう!

TL; DR

この記事では、ImageMagickを使って画像をトリミングするBash関数を作成する方法を紹介する。この関数を使うことで、画像の切り抜きの手間を減らすことができる。

Tweet

画像切り抜きを感じさせる(?)自然風景 drawn by StableDiffusion

今回のカバー画像は久々に起動したStable Diffusionで作成した。長方形のcropingは表現できていないが、雰囲気が好きだったので採用した

  • Prompt for StableDiffusion: A beautiful natural landscape, with a portion of the scene appearing as if it has been cropped and slightly shifted in a rectangular frame, creating an effect reminiscent of image editing. The overall style should be realistic and visually stunning, capturing the beauty of nature while incorporating the creative element of digital manipulation. No text.
  • PromptのPrompt for ChatGPT: 「美しい自然の風景で、画像編集のようにその世界の一部が長方形で切り抜かれてずれている」を生み出すpromptを英語で、promptらしく書きなおして

ImageMagick

ImageMagickのマスコット

ImageMagickの紹介記事に先んじる形になったので、簡単にその紹介をしておこう。

  • ImageMagickは画像編集のためのCUIツール
  • 画像のリサイズ、トリミング、合成、変換などが可能
  • 画像処理の自動化やバッチ処理に向いている
  • コマンドとしてはconvertを用いる (ImageMagickはいずこへ)

Tweet

最後の1つ以外はGCopilotのタブ入力だけで作成したが、なかなか便利じゃないか。というか、ツールの紹介はこのくらいの熱量でも許されそうな気がする

Crop Image with ImageMagick

今回は画像のトリミング(croping)、すなわち切り抜きについて扱う。
多くの場合、cropingは内容に着目して行われるので、GUIツール (たとえばペイントやWin+Shift+S)が適している。

だが、複数回にわたって同じ座標での切り出しが必要な場合や、切り抜き座標が指定されている場合は、ImageMagickの利用が有用だろう。

実際、pure ImageMagickで画像をcropingしたい場合、以下のコマンドを実行する。

1
convert input.jpg -crop ${width}x${height}+${x}+${y} output.jpg

これで、input.jpgxからx+widthyからy+heightの範囲を切り抜いてoutput.jpgとして保存することができる。ちなみに原点は左上で、x軸とy軸の正の向きはそれぞれ右と下だ。

下の画像の場合は、x=100, y=100, width=200, height=200として切り抜いている。

画像のcroping

座標読み取り

まずは基準となる座標の読み取りを行う。
なんらかのソフトを経由することになるが、実はWindowsのペイントで事足りる
カーソルの座標が左下に表示される

Windowsのペイントで(画像では見えていないが)カーソルの座標を読み取る

不満点

では、何が不満なのか。
それは、わざわざ引き算を経由する必要があることだ。

先の手順に従えば、切り抜きたい領域の左上と右下の座標がまず取得され、その後に幅と高さを計算する必要がある。具体的には以下の通りだ。

  1. 左上の座標 (x1, y1)、右下の座標 (x2, y2) を取得
  2. width = x2 - x1、高さ height = y2 - y1 を計算
  3. convertにx1, y1, width, heightを渡す

無論、大した計算ではないのだが、ことあるごとに計算するのも面倒である。
そこで、どうせ機械に切り抜き処理させるなら、この引き算の処理も任せてしまおうというわけだ。

解決策: 作成されたScript

~/.bash_aliases
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
alias cropImage="_CropImage"
function _CropImage(){
input_file="$1"
x1=${2:-0}
y1=${3:-0}
x2=${4:-1000}
y2=${5:-1000}
height=$(($y2 - $y1))
width=$(($x2 - $x1))
output_file="crop_${input_file/ /_/}"
if [[ ! -f $input_file ]]; then
echo "File not found: $input_file"
return -1
fi
if [[ -f "$output_file" ]]; then
echo "File already exists: $output_file"
return -1
fi
convert "$input_file" -crop ${width}x${height}+${x1}+${y1} "$output_file"
}

Info

お察しの通り、これはImageMagickに渡す前に引き算を挟んでいるだけだ。
ただ、このようにシンプルな追加処理すら、通常のGUIソフトウェアでは不可能なことが多い

ここまでシンプルに改善できるのは、CUIの大きな利点といえるだろう。

今回のScriptの技術的な解説 by ChatGPT

上記Scriptの技術的な解説 by ChatGPT

このBashスクリプトは、画像を指定した範囲で切り抜く(クロップする)ためのツールを提供します。以下に、このスクリプトの各部分をBash未経験者向けにわかりやすく説明します。

スクリプト全体の概要

このスクリプトを使うと、画像ファイルの一部を新しいファイルとして切り抜くことができます。cropImageというコマンドを使って、元の画像ファイルと切り抜きたい範囲を指定します。例えば、cropImage input.jpg 10 10 100 100とすると、input.jpgの(10,10)から(100,100)の範囲を切り抜いた新しい画像ファイルが作成されます。

詳細な説明

1. aliasの設定

1
alias cropImage="_CropImage"
  • alias (エイリアス): aliasコマンドを使って、新しいコマンドを定義します。ここでは、cropImageというコマンドを定義しており、実際には_CropImageという関数を呼び出します。
  • 目的: cropImageという簡単な名前で画像を切り抜く関数を呼び出せるようにしています。

2. 関数の定義

1
function _CropImage(){
  • function (関数): 関数は、一連のコマンドをまとめて名前を付けて定義するものです。ここでは、_CropImageという関数を定義しています。

3. 入力引数の取得

1
2
3
4
5
input_file="$1"
x1=${2:-0}
y1=${3:-0}
x2=${4:-1000}
y2=${5:-1000}
  • 引数: 関数は呼び出すときに引数を受け取ることができます。$1, $2, $3, $4, $5は関数に渡された引数を表します。
    • $1: 入力ファイル名
    • $2: 切り抜き範囲の左上のx座標(デフォルトは0)
    • $3: 切り抜き範囲の左上のy座標(デフォルトは0)
    • $4: 切り抜き範囲の右下のx座標(デフォルトは1000)
    • $5: 切り抜き範囲の右下のy座標(デフォルトは1000)

4. 切り抜き範囲の計算

1
2
height=$(($y2 - $y1))
width=$(($x2 - $x1))
  • 計算: $(())は算術演算を行うための構文です。ここでは、切り抜き範囲の高さと幅を計算しています。

5. 出力ファイルの名前を生成

1
output_file="crop_${input_file/ /_/}"
  • 文字列操作: ${input_file/ /_/}は、入力ファイル名の中の空白をアンダースコアに置き換えています。これにより、出力ファイル名に空白が含まれないようにします。
  • 出力ファイル名: crop_を接頭辞として付けた新しいファイル名を生成します。

6. 入力ファイルの存在チェック

1
2
3
4
if [[ ! -f $input_file ]]; then
echo "File not found: $input_file"
return -1
fi
  • 存在チェック: [[ ! -f $input_file ]]は、指定されたファイルが存在しない場合に真となります。
  • エラーメッセージ: ファイルが存在しない場合にエラーメッセージを表示し、関数を終了します。

7. 出力ファイルの存在チェック

1
2
3
4
if [[ -f "$output_file" ]]; then
echo "File already exists: $output_file"
return -1
fi
  • 存在チェック: [[ -f "$output_file" ]]は、出力ファイルが既に存在する場合に真となります。
  • エラーメッセージ: 出力ファイルが既に存在する場合にエラーメッセージを表示し、関数を終了します。

8. 画像の切り抜き

1
convert "$input_file" -crop ${width}x${height}+${x1}+${y1} "$output_file"
  • ImageMagickのconvertコマンド: convertはImageMagickという画像処理ライブラリに含まれるコマンドです。このコマンドを使って画像を切り抜きます。
    • "$input_file": 入力ファイル
    • -crop ${width}x${height}+${x1}+${y1}: 切り抜き範囲の指定(幅、高さ、左上のx座標、左上のy座標)
    • "$output_file": 出力ファイル

まとめ

このスクリプトは、指定した画像ファイルの一部を切り抜いて新しいファイルとして保存するためのツールです。以下の手順で動作します:

  1. cropImageというコマンドを定義。
  2. 関数_CropImageを呼び出して、画像の切り抜き処理を行う。
  3. 入力ファイルの存在チェックや出力ファイルの名前生成、切り抜き範囲の計算を行う。
  4. 最後にconvertコマンドを使って画像を切り抜く。

このスクリプトを使うことで、簡単に画像の一部を切り抜いて新しいファイルとして保存することができます。

作成の直接のきっかけ

ちなみに、VSCodeのススメ執筆が、直接の作成のきっかけだった。
が、結局採用されていない。正直gifアニメーションの方が明らかに伝わりやすかった。
まあ、このBash関数作成の良い機会になったということで。

切り抜き前後の画像は下記の通り。まったく同じ領域を切り抜けており、比較がきれいにできていることが伝わるだろう。なお、下記の画像比較はスマホで正常に動作しなかった。(そのうち記事にしようと思ってたのに、ケチがついたなぁ)

切り抜き前

切り抜き後

まとめ

今回は、ImageMagickを使って画像をトリミングするBash関数を作成した。
この関数を使うことで、画像切り抜きの手間を減らすことができる。

Closing

CUIはコスパのいい改善が多い。常に試してみる視点を持とう

コメント