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 を使って、ようやく実現しました。
すっきり。
ツールやライブラリを見付けたり、どう使うかを考えたりすのも、
問題解決の為の、開発や発明の内だったりしますね。
コメント