ディレクトリの下に、縦横サイズが様々な画像や動画ファイルがあって、その中から特定の縦横サイズの画像や動画を探したい時に、これまでFolder Appからファイルを1個1個クリックしてプレビュー表示でサイズを確認していたのですが、数が多いととてもやってられない。画像の縦横ピクセル数、アスペクト比、向き(縦長、横長)などをコマンドラインでまとめて一覧表示できるといいなあと思ったので、シェルスクリプトを書きました。その作り方のメモです。出力サンプルはこんな感じです(下図)。
Image may be NSFW.
Clik here to view.
実行環境
- macOS Mojave 10.14
- bash 3.2.57
- ffmpeg 4.2.1
今回作ったシェルスクリプトsize.sh
は、指定したディレクトリ(省略時はカレント)の中の階層をfind
コマンドで辿って画像や動画ファイルを検索し、見つかった各ファイルについてffmpeg
コマンドの出力から画像の幅・高さなどの値を取り出し、結果を整形して標準出力に書き出します。
メインのループ処理
最初に、メインのループ処理の構造を示します。
while read f;do## ファイル f に対する処理をする#echo$f$dim$w$h$orien$r_w:$r_hdone< <(find -E"$imgdir"-type f -iregex'.*\.(jpe?g|png|mp4)')
find
が出力した画像ファイルのパス名を1行ずつread
で変数f
に読み込み、f
に対する処理(後述)をした後、リスト1行分のデータを組み立て、echo
で出力します。検索対象のファイルは、jpg, png, mp4としました。パス名に空白を含んでいても大丈夫です。
以下、ループ本体の処理をパーツに分けて説明します(リスト1〜5)。
画像サイズの取得
ffmpeg
コマンドは入力ファイルの情報を標準エラー出力に書き出すので、画像サイズなどの項目を含んでいる行をgrep
コマンドで取り出します。下図はmp4動画ファイルに対して実行した例です。着目するのは図で黄色く囲んだ箇所です。
Image may be NSFW.
Clik here to view.
動画の縦横ピクセル数は、Stream
項目(Video
を含む行)に表示されています。この例では1920x1080になっていて一見サイズが横長に見えますが、実は再生すると縦長サイズの動画です(スマホを縦にして撮った動画でした)。再生した時に回転して表示されるかどうかは、Side data
セクションのdisplaymatrix
項目にrotationの度数に関する情報があります。そこで、本スクリプトではdisplaymatrix
の行もチェックすることにします。
下に示したリスト1は、ffmpeg
の出力から画像の幅$w
、高さ$h
、および縦横の方向$orien
を取り出すまでのコードです。ffmpeg
の出力はこの後も使うので、対象行をgrep
した結果を変数v
に保存しておきます。縦横サイズの「数字x数字」のパターンは、grep -o
に正規表現を指定して取り出し、変数$dim
に保持します。なお、0xで始まる16進数表記とのマッチを避けるため、ここでは「x」の前にくる数字は2桁以上に指定しました。(幅1pxの画像ファイルはない前提)
v=$(ffmpeg -i"$f" 2>&1 |grep -E'Stream|displaymatrix:')dim=$(echo$v |grep -E-o'[0-9]{2,}x[0-9]+')w=${dim%x*}h=${dim#*x}orien=square
if[$w-lt$h];then
orien=vertical
elif[$w-gt$h];then
orien=horizontal
fi
次に、動画が縦横回転しているケースに対応するコードを追加します(リスト2)。リスト1で判定した動画の向き$orien
が横長horizontal
の場合、displaymatrix
項目をチェックし、rotation
に90度が指定されているときは、$orien
を縦長vertical
に変えて、$w
と$h
も入れ替えます。
if["${f##*.}"="mp4"]&&[$orien="horizontal"];then
rot=$(echo$v | grep-E-o'rotation of -?90\.00 degrees')if[-n"$rot"];then
orien=vertical
t=$ww=$hh=$tfi
fi
アスペクト比の計算
画像のアスペクト比を計算するには、幅と高さをその最大公約数で除算して求めます。リスト3は、最大公約数を求めるための関数です。こちらのサイトを参考にさせていただきました:TECH BOX アスペクト比の求め方
関数gcd()
に幅と高さのピクセル数を引数で渡すと、最大公約数が標準出力に表示されます。
function gcd(){if[$2-eq 0 ];then
echo$1return 0;fi
echo$(gcd $2$(($1%$2)))}
ターミナルに打ち込んで試してみましょう。
$ gcd 1920 1080
120
$
OK! ではスクリプトに組み込みます(リスト4)。最大公約数を変数$b
に、幅と高さに対する除算の結果をそれぞれ変数$r_w
、$r_h
に入れました。
b=$(gcd $w$h)r_w=$(($w/$b))r_h=$(($h/$b))
さらにもうひと手間かけます。考慮するケースとしては、
- 幅と高さが互いに素、つまり
$b
=1の場合 $b
で除算しても$r_w
と$r_h
がまだ大きな整数である場合。たとえば、画像サイズ2246x1340を上記の方法で計算すると比率が1123:670になるが、数字が大きすぎてピンとこない
これらのケースについては、短辺を1としたときの長辺の大きさを小数で表すことにします。ケース2について、整数比での表記はとりあえず1:1〜16:9までカバーできれば十分と思われるので、小数で表記するのは$r_w
または$r_h
が16より大きい場合を対象にします。
bashで小数の演算をするにはbc
コマンドが使えます。bc
への入力で、scale
に小数点以下の桁数を指定します(リスト5)。
if[$b-eq 1 ]||[$r_w-gt 16 ]||[$r_h-gt 16 ];then
if[$w-lt$h];then
r_w=1;r_h=$(echo"scale=3; $h/$w" | bc)else
r_w=$(echo"scale=3; $w/$h" | bc)r_h=1;fi
fi
スクリプト全体のソースコード
リスト1〜5を統合し、さらにコマンドライン引数の処理も加えたスクリプト全体のソースコードはこれです。
size.shのソースコード
#!/bin/bashimgdir=.if[$# -gt 1 ];then
echo"usage: $0 [ dir ]"exit 1;fi
if[$# -eq 1 ];then
imgdir=$1fi
function gcd(){if[$2-eq 0 ];then
echo$1return 0;fi
echo$(gcd $2$(($1%$2)))}while read f;do
v=$(ffmpeg -i"$f" 2>&1 |grep -E'Stream|displaymatrix:')dim=$(echo$v |grep -E-o'[0-9]{2,}x[0-9]+')w=${dim%x*}h=${dim#*x}orien=square
if[$w-lt$h];then
orien=vertical
elif[$w-gt$h];then
orien=horizontal
fi
if["${f##*.}"="mp4"]&&[$orien="horizontal"];then
rot=$(echo$v | grep-E-o'rotation of -?90\.00 degrees')if[-n"$rot"];then
orien=vertical
t=$ww=$hh=$tfi
fi
b=$(gcd $w$h)r_w=$(($w/$b))r_h=$(($h/$b))if[$b-eq 1 ]||[$r_w-gt 16 ]||[$r_h-gt 16 ];then
if[$w-lt$h];then
r_w=1;r_h=$(echo"scale=3; $h/$w" | bc)else
r_w=$(echo"scale=3; $w/$h" | bc)r_h=1;fi
fi
echo$f$dim$w$h$orien$r_w:$r_hdone< <(find -E"$imgdir"-type f -iregex'.*\.(jpe?g|png|mp4)')
参考
以下の記事を参考にさせていただきました。