pillow を使ってみましたが、しっくり来なかったので、pyexiv2 を使ってみました。
結果は、所望の事がすべて出来たので、今後はこれをメインに使います。
所望の事:
・一つのライブラリで実現できる。
※シンプルが一番。
・コードが簡単。
※難しいコードを書く必要があると面倒になって使わなくなる。
・Exif の詳細なメタデータの調査ができる。
※今回試した画像では、573項目が取得できました。
※pillow では私には実現できませんでした。
インストール
Python3 を使用しています。(64bit Ver: 3.8.5)
※pyexiv2 は、python 32bit 版には非対応なようです。64bit 版で使いましょう。
Jupyter lab を使って書いています。
pyexiv2 は、pip を使ってインストールしました。
pip install pyexiv2
Windows を使っていますので、コマンドプロンプトから実行しました。
Windows + R で「ファイル名を指定して実行」が起動します。
cmd と入力して OK をクリックするか、Enterキー を押せばコマンドプロンプトが起動します。
上記のコードを入力、あるいはコピペして Enter でインストールされます。
コード
import pyexiv2
filename = 'SP_00036.JPG'
img = pyexiv2.Image(filename)
metadata = img.read_exif()
# 辞書で返って来る
print(metadata)
# 特定のメタデータを指定して取り出す
print("------------------------------")
print("Filename:", filename)
print("DateTimeOriginal:", metadata['Exif.Photo.DateTimeOriginal'])
print("Copyright:", metadata['Exif.Image.Copyright'])
print("Artist:", metadata['Exif.Image.Artist'])
print("---------------")
print("Make:", metadata['Exif.Image.Make'])
print("Model:", metadata['Exif.Image.Model'])
print("LensModel:", metadata['Exif.Photo.LensModel'])
print("---------------")
# print("FocalLength:", metadata['Exif.Photo.FocalLength'])
# 700/10 と表示されてしまうので、文字列を数値化して計算
x = metadata['Exif.Photo.FocalLength'].split('/')
# なぜか floot(浮動小数点数値) になってしまったので、割り算をしてから再度 int に変換
y = int(x[0]) / int(x[1])
print("FocalLength:", int(y), "mm")
print("FocalLength(35mm):", metadata['Exif.Photo.FocalLengthIn35mmFilm'], "mm")
print("ExposureTime:", metadata['Exif.Photo.ExposureTime'])
# print("FNumber: F", metadata['Exif.Photo.FNumber'])
# 63/10 と表示されてしまうので、文字列を数値化して計算
x = metadata['Exif.Photo.FNumber'].split('/')
print("FNumber: F", int(x[0]) / int(x[1]))
print("ISO Speed:", metadata['Exif.Photo.ISOSpeedRatings'])
# print("ExposureBiasValue(APEX:EV):", metadata['Exif.Photo.ExposureBiasValue'])
# -3/10 と表示されてしまうので、文字列を数値化して計算
x = metadata['Exif.Photo.ExposureBiasValue'].split('/')
print("ExposureBiasValue:", int(x[0]) / int(x[1]), "EV")
print("------------------------------")
# 一覧表示
# 読み出したいメタデータの名称を調べる
# 何のタグか分かり易いように、値も出力
for x in metadata:
print(x, ":", metadata[x])
解説
コメントとコードを見れば一目瞭然かと。
それくらいシンプルですね。
抜粋して流れを解説します。
全てのメタデータ(Exif)を取得し、変数に格納
・import で pyexiv2 を呼び出す。
・写真を指定して、メタデータ(Exif) を読み出す。
※写真は、jupyter lab のファイル「hoge.ipynb」と同じ階層に置いてあります。
※Python のファイル「hoge.py」の場合でも同じ結果となります。
・変数 metadata に格納。
・辞書で返って来るようです。
print(変数) で読みだしたメタデータの全てが出力されます。
import pyexiv2 filename = 'SP_00036.JPG' img = pyexiv2.Image(filename) metadata = img.read_exif() # 辞書で返って来る print(metadata)
出力結果
{‘Exif.Image.ImageDescription’: ‘ ‘, ‘Exif.Image.Make’: ‘SONY’, ‘Exif.Image.Model’: ‘ILCE-7RM4’, ‘Exif.Image.Orientation’: ‘1’, ‘Exif.Image.XResolution’: ‘350/1’, ‘Exif.Image.YResolution’: ‘350/1’, ‘Exif.Image.ResolutionUnit’: ‘2’, ‘Exif.Image.Software’: ‘ILCE-7RM4 v1.10’, ‘Exif.Image.DateTime’: ‘2020:03:01 21:57:38’,・・・・・・・・・・・・・・
タグを指定して欲しいデータを抽出
改行もせずに表示したって、使い難いですよね。
タグを指定することで、必要な情報だけを取り出せます。
例として、ブログや SNS などで撮影情報を載せるシーンを想定してコードを書いてみました。
露出補正が「-3/10」と表示されたり、そのままでは使い難いタグもあります。
文字列で返って来ているので、数列に変換し、計算してから表示させました。
計算の仕方は、ヒントを後述。
# 特定のメタデータを指定して取り出す
print("------------------------------")
print("Filename:", filename)
print("DateTimeOriginal:", metadata['Exif.Photo.DateTimeOriginal'])
print("Copyright:", metadata['Exif.Image.Copyright'])
print("Artist:", metadata['Exif.Image.Artist'])
print("---------------")
print("Make:", metadata['Exif.Image.Make'])
print("Model:", metadata['Exif.Image.Model'])
print("LensModel:", metadata['Exif.Photo.LensModel'])
print("---------------")
# print("FocalLength:", metadata['Exif.Photo.FocalLength'])
# 700/10 と表示されてしまうので、文字列を数値化して計算
x = metadata['Exif.Photo.FocalLength'].split('/')
# なぜか floot(浮動小数点数値) になってしまったので、割り算をしてから再度 int に変換
y = int(x[0]) / int(x[1])
print("FocalLength:", int(y), "mm")
print("FocalLength(35mm):", metadata['Exif.Photo.FocalLengthIn35mmFilm'], "mm")
print("ExposureTime:", metadata['Exif.Photo.ExposureTime'])
# print("FNumber: F", metadata['Exif.Photo.FNumber'])
# 63/10 と表示されてしまうので、文字列を数値化して計算
x = metadata['Exif.Photo.FNumber'].split('/')
print("FNumber: F", int(x[0]) / int(x[1]))
print("ISO Speed:", metadata['Exif.Photo.ISOSpeedRatings'])
# print("ExposureBiasValue(APEX:EV):", metadata['Exif.Photo.ExposureBiasValue'])
# -3/10 と表示されてしまうので、文字列を数値化して計算
x = metadata['Exif.Photo.ExposureBiasValue'].split('/')
print("ExposureBiasValue:", int(x[0]) / int(x[1]), "EV")
print("------------------------------")
出力結果
------------------------------ Filename: SP_00036.JPG DateTimeOriginal: 2020:03:01 21:57:38 Copyright: @7716nosola Artist: @7716nosola --------------- Make: SONY Model: ILCE-7RM4 LensModel: FE 70-300mm F4.5-5.6 G OSS --------------- FocalLength: 70 mm FocalLength(35mm): 70 mm ExposureTime: 1/80 FNumber: F 6.3 ISO Speed: 2500 ExposureBiasValue: -0.3 EV ------------------------------
pillow で困ったのは、シャッター速度です。
「0.0125」と返って来ました。
「秒」ですね。
「1/分母」 と表記したい所です。
「600倍」すれば「分母」が求まりますが、「75」となります。
「1/75」と表示すれば良さそうに思いますが、実際は、「1/80」で撮影しています。
では末尾が「5」の場合は四捨五入すれば良いか?
いいえ、それでは「1/125」で撮影した場合に困ります。
その点、pyexiv2では、素直に「1/80」と表示してくれました。助かる~。
どんなタグがあるかを調べる
# 一覧表示
# 読み出したいメタデータの名称を調べる
# 何のタグか分かり易いように、値も出力
for x in metadata:
print(x, ":", metadata[x])
出力結果
Exif.Image.Make : SONY
Exif.Image.Model : ILCE-7RM4
Exif.Image.Orientation : 1
Exif.Image.XResolution : 350/1
Exif.Image.YResolution : 350/1
Exif.Image.ResolutionUnit : 2
Exif.Image.Software : ILCE-7RM4 v1.10
Exif.Image.DateTime : 2020:03:01 21:57:38
・・・・・・
改行されて表示されるので、所望のタグが見付け易くなりました。
詳細なメタデータが取得できる
取得されているデータのうち、「Exif.Image」とあるのは、一般的な Exif 情報ですね。
pyexiv2 の良い所は、更に詳細に、メーカー独自の Exif 情報も取得できる点です。
「Exif.Sony1」などとある部分がそうですね。
ボディやレンズのシリアル番号まで取得されているようです。
Exif.Sony1.WhiteBalanceFineTune : 0
Exif.Sony1.WhiteBalance : 0
Exif.Sony1.SonyModelID : 375
Exif.Sony1.LensID : 65535
下記のような、不思議なコードで表示されているメタデータもあります。
Exif.Sony1.0x200b : 0
「ExifTool Tag Names」というサイトがありまして、ここで「Tag ID」を調べる事が出来ます。
Sony Tags
詳細に調べてありますね。素晴らしい。
上記のタグの「 0x200b 」を調べてみます。
■MultiFrameNoiseReduction
(may not be valid for RS100)
0 = Off
1 = On
255 = n/a
抽出したメタデータでは値が「0」となっているので、設定は「OFF」であった事が分かります。
Sony 提供のソフト「Imaging Edge」を使えばカメラの設定情報を調べられますが、複数の写真を調査したい場合には苦労します。
Python で取得できるなら、For ループを使うなどで、対処の仕様があります。気持ちが楽。
値が「0」でも項目が表示される、という点、大事ですよね。
ライブラリによっては、値に「0」が入っていると、表示を割愛されるようですが、
「0」は「値が無し」ではなく、「0(OFF)という設定状態」という場合もあるのですから。
その他 Exif についてのリンクなど
「ExifTool Tag Names」では、Sony 以外にも、メーカーの Tag ID が掲載されています。
有名どころを挙げると、下記のページが確認できました。すごい情報量ですね。
ExifTool Tag Names(TOP Page)
https://exiftool.org/TagNames/index.html
Canon Tags
https://exiftool.org/TagNames/Canon.html
Nikon Tags
https://exiftool.org/TagNames/Nikon.html
Panasonic Tags
https://exiftool.org/TagNames/Panasonic.html
Pentax Tags
https://exiftool.org/TagNames/Pentax.html
Ricoh Tags
https://exiftool.org/TagNames/Ricoh.html
Exif の意味そのものを調べる場合は、次のようなリンクが役立つかと。
■CIPA 提供の Exif 規格(PDF)
https://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf
■AWare Systems / TIFF Tag Search
https://www.awaresystems.be/imaging/tiff/tifftags/search.html
文字列を数値にする方法のヒント
露出補正が「-3/10」と表示されたりする事の対処。
下記では、F値を例にしています。
# メタデータのタイプ調査
# ストリング(文字列)となっている
a = metadata["Exif.Photo.FNumber"]
print('a=', a, type(a))
# a= 63/10 class 'str'
print('-------------------------')
# / でスプリット(分割)
# リストで返って来る
b = a.split('/')
print('b=', b, type(b))
# b= ['63', '10'] class 'list'
print('-------------------------')
# リストの要素を抜き出す(まだ文字列のまま)
c = b[0]
print('c=', c, type(c))
# c= 63 class 'str'
d = b[1]
print('d=', d, type(d))
# d= 10 class 'str'
print('-------------------------')
# int() に入れると、数値として扱われる
print('c=', c, type(int(c)))
# c= 63 class 'int'
print('d=', d, type(int(d)))
# d= 10 class 'int'
# int() に入れて計算してみる
print('割り算(63/10):', int(c) / int(d))
# 割り算(63/10): 6.3
print('-------------------------')
# まとまると
a = metadata["Exif.Photo.FNumber"]
b = a.split('/')
c = b[0]
d = b[1]
int(c) / int(d)
print('-------------------------')
# 短いコードで書いてみる
x = metadata["Exif.Photo.FNumber"].split('/')
print('F', int(x[0]) / int(x[1]))
# F 6.3
出力結果
a= 63/10 class 'str' ------------------------- b= ['63', '10'] class 'list' ------------------------- c= 63 class 'str' d= 10 class 'str' ------------------------- c= 63 class 'int' d= 10 class 'int' 割り算(63/10): 6.3 ------------------------- ------------------------- F 6.3
最後に
Exif を取得するのには、色々な方法がありますね。
長年、色々な方法でチャレンジしました。VBA、JavaScript、などなど。
どれも難易度が高すぎますし、所望のデータが取得されなかったり、かゆい所に手が届きません。
Python と pyexiv2 を使って、ようやく実現しました。
すっきり。
ツールやライブラリを見付けたり、どう使うかを考えたりすのも、
問題解決の為の、開発や発明の内だったりしますね。


コメント