Python3でJPEG画像のExifを取得し、回転方向を修正する

ちょっと古いPCや安物のデバイスでJPEGを表示させようとすると、上下が反転していたりします。Exifに記録されている回転情報を反映していない場合に起きる現象なのですが、今回はPython3でこれを修正してみます。

もくじ:

はじめに

カメラで撮影した画像ファイルは、単純に正置方向に画素を並べているのではなく、
カメラが検出した方向をメタデータ(Exif)に記録しておき、人間向けには後でビューワーなどで表示を変換することを想定しています。

古いPCなど、Exifを読まないような環境ではExifにどんな値が入っていても正置方向に記録されているものとみなしてしまいます。ですので、要はExifに書かれている回転情報をもとに修正してしまえばいい、ということですね。

いくつかアプローチはあるようですが、比較的扱いやすいのはPillowモジュールで変換する方法です。画像処理の機械学習などにつかうモジュールで、Exifの編集機能(書き込み)はできませんが、取得・全削除はできるようになっています。
Python3のPillowはPython2のPILをフォークしたものらしいので、今回はPython3系に限定のお話です。
標準ライブラリにはない(Anacondaには同梱)ので、ない場合はpipなどでインストールします。

今回のサンプルの全容は後ほど。サンプルを見た方が早いと思いますが、以下に解説しておきます。

Exifの取得

特定のディレクトリ以下にあるJPEGファイルを探し、各ファイル毎にPillowを使ってExifを取得します。
よくあるのはPIL.Imageオブジェクトの_getexif()メソッドでdictとして取得する方法です。命名から解釈するとpublicなメソッドとして使ってほしくない意図が見えますが、まあPillow本来の使い方からは外れているのでしょう。

タグ名はエンコードされており、ここではPIL.ExifTags.TAGを使ってデコードしています。
また、取得できるものを全て取得してしまうと出力がやたらと長くなってしまうので、一部のユーザ情報のタグ["MakerNote", "UserComment"]はスキップしています。

必須ではありませんが、あとでJSONファイルとして保存したいのでbytes型は各要素を辿って文字列にフォーマットしています。

手元の環境で試したところ、こんな感じのJSONが取得できました。
機種によって違いがありますが、GPSの情報やカメラの提供元などのメタデータが記録されていることがわかります。肝心の回転情報は"Orientation"タグ(範囲は1~8)です。

画像の回転・反転

Exifに記録されている回転情報("Orientation"タグ)を表す番号と方向はこんな感じの対応になっています。厳密には、単純な回転のみではなく、反転(鏡像)を含めたパターンが定義されています。

PillowではImage.transpose()メソッドで修正することができるので、その引数(PIL.Image...)を"Orientation"タグに応じてセットし、正置方向に変換します。
長くなるので引数trans_は定数で書いています。

Image.open()メソッドで画像ファイルを開き、編集した後に同様にImage.save()メソッドでExifを除いた画像本体を保存できます。Pillowには特定のExifタグのみを編集する機能はありません。
もともと正置の画像やExifが付いていない画像は処理しません。

サンプル

今回のサンプルスクリプトです。
実行時の引数に応じて、Exifの抽出・保存と修正の処理を実行します。
検証した環境はWindows 10 Pro, Python3.6, Pillow 5.1.0でした。

おわり。