![見出し画像](https://assets.st-note.com/production/uploads/images/110847970/rectangle_large_type_2_8caf6346c0efe0388d6abb5107a3c341.jpeg?width=1200)
撮影情報(Exif)をPythonで読み込む
はじめに
Exifは撮影に使用したカメラやレンズ、F値や露光時間、ISO感度などといった撮影情報を写真データに埋め込むための規格です。この規格により埋め込まれた撮影情報をExif情報と呼んだりします。
PythonのPillow(PIL)というライブラリを使用することで、写真データからこのExif情報を抽出することができます。
ちまたでは_getexifを使った抽出方法がよく紹介されていますが、今回はgetexifを使った抽出方法について紹介します。
PILのバージョンは9.1.1で検証しています。(バージョンによって挙動が違う場合あるので注意が必要です)
よく紹介される抽出方法(_getexif)
ググるとよく出てくるのは、以下のやり方です。
from PIL import Image
path = 'hoge.jpg'
im = Image.open(path)
im._getexif()
この_getexifメソッドを使ったやり方で何ら問題ないですが、
_(アンダースコア)から始まる名前のメソッドは、基本的に外部から使われることを想定していないプライベートなメソッドになります。
Pillowにはgetexifというパブリックなメソッドも用意されているので、
今回はこちらを使ったexif情報の抽出方法を紹介しようと思います。
getexifメソッド
getexifを使う場合コードはこちらになります。
from PIL import Image
with Image.open(path) as im:
exif = im.getexif()
exif_dict = exif.get_ifd(0x8769) # Exif情報の取得
gps_dict = exif.get_ifd(0x8825) # GPS情報の取得
![](https://assets.st-note.com/img/1689406602690-jd11t6ZHiI.png?width=1200)
キーが番号になっていて、どれが何を示しているかわかりませんが、
このあと意味の分かる名前に置換します
こちらの情報を参考にしました。
getexifでうまく情報が取れないという投稿者に対して、こうすれば取得できるという例を開発者が示しています。
exifの仕様について、私も詳細まで把握していないですが、
以下のサイト見ると上記のコードの意味がなんとなく分かるかと思います。
Exifの構造について
Exifのタグ番号について
https://www.vieas.com/exif23.html
0x8769が撮影情報Exifデータへアクセスするためのタグ番号
0x8825がGPSデータへアクセスするためのタグ番号
タグ番号を名前に置き換える
先ほどの処理だけだと、辞書のキーがタグ番号になっていて、ぱっと見何が何だかわかりません。
Pillowにはタグ番号とタグ名称の辞書(TAGS)がありますので、それを使って番号を名称に置換します。
![](https://assets.st-note.com/img/1689406891916-waOgeYEFWg.png?width=1200)
やり方はいろいろあると思いますが、pandasを使った置換が楽なので紹介します。
from PIL.ExifTags import TAGS,GPSTAGS
exif_sr = pd.Series(exif_dict).rename(TAGS)
gps_sr = pd.Series(gps_dict).rename(GPSTAGS)
![](https://assets.st-note.com/img/1689406161811-hVbnjCrdRe.png?width=1200)
pandas.Seriesに変換したのち、renameメソッドでindexを置換します。
また、to_dictメソッドを使って辞書に戻すこともできます。
まとめ
from PIL import Image
from PIL.ExifTags import TAGS,GPSTAGS
def load_exif(path):
ifd_dict = {}
with Image.open(path) as im:
exif = im.getexif()
ifd_dict["Exif"] = exif.get_ifd(0x8769)
ifd_dict["GPSInfo"] = exif.get_ifd(0x8825)
return ifd_dict
path = 'hoge.jpg'
ifd_dict = load_exif(path)
exif_sr = pd.Series(ifd_dict["Exif"]).rename(TAGS)
gps_sr = pd.Series(ifd_dict["GPSInfo"]).rename(GPSTAGS)
Pillowのgetexifメソッドを使って、写真の撮影情報を取得する方法を紹介しました。
今後は、複数の写真ファイルに対してPandas.DataFrameで扱いやすい形にする方法について紹介していきたいと思います。
追記(2023/10/14)
まとめに書いた方法だと、一部情報が取れないので、exifの情報をすべて取れるよう修正しました。
from PIL import Image
from PIL.ExifTags import TAGS,GPSTAGS
def load_exif(path):
ifd_dict = {}
with Image.open(path) as im:
exif = im.getexif()
# {タグID: 値}の辞書を{タグ名: 値}の辞書に変換
ifd_dict["Zeroth"] = {TAGS[tag_id]: value for tag_id, value in exif.items() }
ifd_dict["Exif"] = {TAGS[tag_id]: value for tag_id, value in exif.get_ifd(0x8769).items()}
ifd_dict["GPSInfo"] = {GPSTAGS[tag_id]: value for tag_id, value in exif.get_ifd(0x8825).items()}
return ifd_dict,exif
path = 'hoge.jpg'
ifd_dict = load_exif(path)
主な修正点としては、ifd_dict["Zeroth"]に0th IFDという種類の情報を追加したことです。これにはカメラのモデル名やメーカー名などが入っています。ついでにタグ番号・タグ名の置換方法もpandasを使わない方法にしました。
得られるifd_dictの中身は以下のような形です。
{'Exif': {'BrightnessValue': 8.571875,
'ColorSpace': 1,
・・・・・・・・・・・・中略・・・・・・・・・・・・
'Sharpness': 0,
'WhiteBalance': 0},
'GPSInfo': {'GPSDateStamp': '2023:02:25',
'GPSDifferential': 0,
・・・・・・・・・・・・中略・・・・・・・・・・・・
'GPSTimeStamp': (6.0, 15.0, 53.0),
'GPSVersionID': b'\x02\x03\x00\x00'},
'Zeroth': {'DateTime': '2023:02:25 15:15:52',
'ExifOffset': 376,
・・・・・・・・・・・・中略・・・・・・・・・・・・
'YCbCrPositioning': 2,
'YResolution': 350.0}}
また、今回の修正内容に関することや、大量の画像のexifを読み込んで表形式にまとめる方法を以下の記事に書いたので、こちらも参照いただければと思います。