EXIFデータの読み出し

 それでは本題に入りましょう。今回もデスクトップにphotoディレクトリを用意し、そこに画像ファイルを入れてあります。これがカレントディレクトリになります。cd ~/Desktop/photoとコマンド入力すればOKです。

前回は画像形式を変換しましたが、その際に指定した-sオプションがありました。-sオプションは画像ファイルのプロパティ(key:キー)の名前(name)と値(value)の設定になります。これはEXIF情報も含まれます。ただし、全てのEXIF情報が取得できるようではなさそうです。プレビュー.appで画像のEXIF情報を表示すると以下のようになります。

sipsコマンドでは-gオプションの後にallを指定するとEXIF情報を取得できます。JPEG形式など他の画像形式でもEXIF情報があれば表示できます。

sips -g all IMG_0001.HEIC

整形されたデータではなくXML形式のデータを取得する場合は以下のようになります。

sips -g allxml IMG_0001.HEIC

sipsコマンドで取得できるEXIF情報は非常に限定的であるため、GPSを利用した位置情報やカメラレンズの絞りなどを取得して処理することはできません。回転方向に合わせてsips処理の不具合(第18回の講座を参照)を解消するというのも現状では難しそうです。バージョンアップで変わる可能性もありますので、とりあえず期待して待つことにしましょう。待つよりも文句言った方がいいのかもしれませんが。(sipsコマンドで各種EXIFデータを取得できる方法があれば知りたいところです)

とは言え少ないEXIF情報であっても役立てることはできます。例えば機種名に合わせて処理を変える場合を考えてみましょう。スマートフォンは機種によって解像度が異なるため、画像のリサイズや形式変換などを機種に合わせて行いたい場合があります。機種名はmodelという名前のプロパティで保存されています。

まず、EXIF情報から機種名だけをピックアップ(抽出)してみましょう。iPhoneだとHEIF形式の拡張子がHEICで、Androidだと小文字のheicです。両方の拡張子のファイル内のEXIF情報から機種名を出力(表示)するには{ }を使って指定します。これまでも何度も使ってきたので説明は不要でしょう。

sips -g model *.{HEIC,heic} 

・出力結果

/Users/weed/Desktop/photo/IMG_0001.HEIC
  model: iPhone SE (2nd generation)
/Users/weed/Desktop/photo/IMG_0002.HEIC
  model: iPhone SE (2nd generation)
/Users/weed/Desktop/photo/IMG_0003.HEIC
  model: iPhone SE (2nd generation)
/Users/weed/Desktop/photo/IMG_0004.HEIC
  model: iPhone SE (2nd generation)
/Users/weed/Desktop/photo/IMG_0005.HEIC
  model: iPhone SE (2nd generation)
/Users/weed/Desktop/photo/20210706_112412.heic
  model: SC-51A
/Users/weed/Desktop/photo/20210706_112416.heic
  model: SC-51A
/Users/weed/Desktop/photo/20210706_112430.heic
  model: SC-51A
/Users/weed/Desktop/photo/20210706_112453.heic
  model: SC-51A

sipsだとモデル名とともにファイル名(ファイルパス)まで出力されてしまいます。オプションを見てもファイルパスだけを出力する機能はなさそうです。ということで、この出力結果から特定の機種名の行を抜き出すにはgrepを使います。この場合、|(パイプ)でつなげればOKです。

sips -g model *.{HEIC,heic} | grep 'SC-51A'

ここで困るのは処理する対象は指定した機種名(model)なのにsipsで処理するのに必要なのはファイル名(ファイルパス)だということです。この場合でもgrepが使えます。
grepには検索でマッチした前後の行も出力するオプションがあります。マッチした行の前は-B、後は-Aオプションです。オプションの後に表示したい行数を指定します。
今回はmodel名の前がファイルパスになっているので-B 1とするとファイルパスと機種を含む行が出力されます。

sips -g model *.{HEIC,heic} | grep 'SC-51A' -B 1

・出力結果

/Users/weed/Desktop/photo/20210706_112412.heic
  model: SC-51A
--
/Users/weed/Desktop/photo/20210706_112416.heic
  model: SC-51A
--
/Users/weed/Desktop/photo/20210706_112430.heic
  model: SC-51A
--
/Users/weed/Desktop/photo/20210706_112453.heic
  model: SC-51A

ただ、この段階でも必要なファイルパスだけを抜き出せていません。そこで、さらに|でつなげてgrep '/'とします。

sips -g model *.{HEIC,heic} | grep 'SC-51A' -B 1 | grep '/'

これで指定した機種名で撮影した画像のファイルパスを得ることができました。

シェルスクリプトでJPEG変換処理する

 ファイルパスを得ることができれば後は簡単です。sipsコマンドのパラメーターにファイルパスを渡すだけです。が、sipsはパイプによるファイルパスを期待通りに処理してくれないようです。こんな時は、やはりシェルスクリプトの出番です。
 シェルスクリプトでは渡されたファイルパスの数だけ繰り返しsipsコマンドの処理を行えばよいことになります。繰り返し処理を行うwhileを使って「while read filepath」とするとfilepath変数にgrepで検索されマッチしたファイルパス名が順次入ります。後はsipsで処理するファイルパスの部分を$filepathとすれば期待通りの処理が行われます。
 実際のシェルスクリプトは以下のようになります。なお、変換されたJPEG画像はjpgimgディレクトリに保存されますが、このディレクトリが存在しない場合に備えてシェルスクリプトの先頭でmkdir ./jpgimgとしてディレクトリを作成しています。すでに該当するディレクトリがある場合は何も起こりません。もちろんエラーなども発生しません。なお、以下のスクリプトを改良してパーミッション(権限)が絡むような場所にディレクトリを作成したり、変換後のファイルを保存する場合は注意が必要です。パーミッションで許可されていないとディレクトリは作成できませんし、ファイルも保存できません。

 作成したスクリプトもカレントディレクトリにしてあるデスクトップのphotoディレクトリ内に保存しておきます。(改良して指定したディレクトリにある画像を処理するようにしてもよいでしょう)

#!/bin/bash
mkdir ./jpgimg
while read filepath
do
 sips -s format jpeg $filepath -o ./jpgimg
done

エディタ等で入力したら実行権限を付与しておきます。

chmod +x ./heiftojpeg.sh

このスクリプトは以下のようにして使用します。該当するファイルパスの画像をJPEG形式に変換します。

sips -g model *.{HEIC,heic} | grep 'SC-51A' -B 1 | grep '/' | ./heiftojpeg.sh

このシェルスクリプトは渡されたファイルパスをJPEG形式に変換しているだけなので、いろいろ応用できます。
ちなみに今回の場合は、たまたま機種名に/が含まれないので期待通りに動作しています。機種名に/が含まれると以下の処理で失敗してしまいます。エラーになるわけでなくファイルパスだけでなく機種名まで出力されてしまいます。

sips -g model *.{HEIC,heic} | grep 'SC-51A' -B 1 | grep '/'

こんな場合にも便利なのが正規表現です。ファイルパスは行頭が必ず/で始まりますが、機種名(model)は/からは始まりません。これを利用すると以下のように^をつけて行頭にマッチするようにします。これで、機種名に/が含まれていても期待通りの動作になります。

sips -g model *.{HEIC,heic} | grep 'SC-51A' -B 1 | grep '^/'    

正規表現は便利で奥が深いのですが、うまく使えば非常に便利で強力です。と定番の謳い文句を書いてみましたが、複雑に入り組んだ正規表現は返って問題となることがあります。せっかくリダイレクトやパイプによってコマンドを組み合わせて使用できるのですから、それぞれのコマンドの便利なところだけを利用させてもらうのがよいのではないかと思います。また、該当するコマンドがなければシェルスクリプトや自作コマンドで対処すればよいのです。

ということで、また次回。

著者 仲村次郎
いろいろな事に手を出してみたものの結局身につかず、とりあえず目的の事ができればいいんじゃないかみたいな感じで生きております。