目次
Pythonで写真データからExif情報を参照・削除
Exifは、Exchangeable image file format(エクスチェンジャブル・イメージ・ファイル・フォーマット)の略で、「エグジフ」もしくは「イグジフ」と読みます。
写真用のメタデータを含む画像ファイルフォーマットのことで、JPEG、TIFF、JPEG XR(HD Photo)などの画像形式が対応しています。
Exif情報をPythonで取り出すサンプル
PythonのPILライブラリを使うと、Exif情報をとりだせます。
でも、JPEG形式の写真なら必ずExif情報が埋め込まれているわけでもなく、かつ、ファイルによって何が埋め込まれているかもバラバラです。
とりあえず、JPEG画像に埋め込まれているExif情報を一覧するクラスです。
from PIL import Image import PIL.ExifTags as ExifTags class ExifImage: def __init__(self, fname): # 画像ファイルを開く --- (*1) self.img = Image.open(fname) self.exif = {} if self.img._getexif(): for k, v in self.img._getexif().items(): if k in ExifTags.TAGS: self.exif[ExifTags.TAGS[k]] = v def print(self): if self.exif: for k, v in self.exif.items(): print(k, ":", v) else: print("exif情報は記録されていません。")
適当なJPEG画像をで試してみます。
作ったサンプルのお試しイメージ
Pythonのソースと同じフォルダに、test.jpgの名称で保存して実行します。
a = ExifImage("test.jpg") a.print()
まずは、インターネットから適当なJPEG画像を保存してやってみます。
上記を実行すると。
exif情報は記録されていません。
でした。
どうやら、exif情報を記録しないフォーマットになっているようです。
ということで。
以前、使った自分のスマホでとった情報をみてみます。
結果はこんな感じです。
ExifVersion : b'0220'
ShutterSpeedValue : (4322, 1000)
DateTimeOriginal : 2019:07:27 14:54:24
DateTimeDigitized : 2019:07:27 14:54:24
ApertureValue : (200, 100)
BrightnessValue : (0, 100)
MeteringMode : 2
FlashPixVersion : b'0100'
Flash : 0
FocalLength : (3570, 1000)
ColorSpace : 1
ExifImageWidth : 224
ExifInteroperabilityOffset : 746
FocalLengthIn35mmFilm : 0
SceneCaptureType : 0
SubsecTime : 033
SubsecTimeOriginal : 033
SubsecTimeDigitized : 033
ExifImageHeight : 224
ImageLength : 4160
Make : SG
Model : S1
SensingMethod : 0
Orientation : 1
YCbCrPositioning : 1
ExposureTime : (49995372, 1000000000)
XResolution : (72, 1)
YResolution : (72, 1)
FNumber : (200, 100)
SceneType : b'\x00'
ExposureProgram : 0
ISOSpeedRatings : 382
ResolutionUnit : 2
ExposureMode : 0
ImageWidth : 3120
WhiteBalance : 0
Software : Ralpha,9.0010.30, NORMAL 0, V010.0080.01, V001.0020.07
DateTime : 2019:07:27 14:54:24
ExifOffset : 254
主なExif情報TAGの日本語訳
一覧にしてみました。
Exifタグ | 意味 |
---|---|
ApertureValue | 絞り |
BrightnessValue | 輝度 |
ColorSpace | 色空間情報 |
ExposureBiasValue | 露出補正値 |
ExposureMode | 露出モード |
ExposureProgram | 露出プログラム |
ExposureTime | 露出時間 |
Flash | フラッシュ有無 |
FlashPixVersion | 対応フラッシュピックスバージョン |
FNumber | F値 |
FocalLength | 焦点距離 |
FocalLengthIn35mmFilm | 35mm 換算焦点距離 |
ImageLength | 画像サイズ |
ImageWidth | 画像幅 |
SensingMethod | センサー方式 |
ShutterSpeedValue | シャッタースピード |
WhiteBalance | ホワイトバランス |
Exif情報をPythonで削除
今更ながら、位置情報とかを含むExIf情報を確認する方法だけ紹介していたのも片手落ちだなと思うので、PythonでExIf情報を削除する方法だけ、さらっと載せておきます。
from PIL import Image img = Image.open('test.jpg') data = img.getdata() mode = img.mode size = img.size new_img = Image.new(mode, size) new_img.putdata(data) new_img.save('test_noexif.jpg')
PILを使って、画像を開き、画像本体のmodeとsizeとdataを取得し、それを使って新規作成したファイルに書き出す・・というだけです。
これでExIf情報はきれいになくなります。
Exif情報のGPS情報から経度・緯度を計算
スマホでGPS情報の埋め込みが許可されているなど、いろいろ条件がそろう必要がありますが、exifにGPS情報が埋め込まれている場合があります。
経度・緯度の計算
GPS情報は、GPSInfoというタグで取得できます。
ただ、GPS情報には分数形式で納められているので、Googleマップの検索条件に使える「経度(latitude)・緯度(longitude)」の形式にするには、ちょっと計算してやる必要があります。
そのあたりの処理を追加して、さきほどのクラスを拡張してみました。
from PIL import Image import PIL.ExifTags as ExifTags class ExifImage: def __init__(self, fname): # 画像ファイルを開く --- (*1) self.img = Image.open(fname) self.exif = {} if self.img._getexif(): for k, v in self.img._getexif().items(): if k in ExifTags.TAGS: self.exif[ExifTags.TAGS[k]] = v def print(self): if self.exif: for k, v in self.exif.items(): print(k, ":", v) else: print("exif情報は記録されていません。") def __conv_deg(self, v): # 分数を度に変換 d = float(v[0][0]) / float(v[0][1]) m = float(v[1][0]) / float(v[1][1]) s = float(v[2][0]) / float(v[2][1]) return d + (m / 60.0) + (s / 3600.0) def get_gps(self): if "GPSInfo" in self.exif: gps_tags = self.exif["GPSInfo"] else: gps_tags = {} gps = {} if gps_tags: for t in gps_tags: gps[ExifTags.GPSTAGS.get(t, t)] = gps_tags[t] latitude = self.__conv_deg(gps["GPSLatitude"]) lat_ref = gps["GPSLatitudeRef"] if lat_ref != "N": latitude = 0 - latitude longitude = self.__conv_deg(gps["GPSLongitude"]) lon_ref = gps["GPSLongitudeRef"] if lon_ref != "E": longitude = 0 - longitude return '{:.06f}'.format(latitude) + "," + '{:.06f}'.format(longitude) else: return "経度・緯度は記録されていません。"
ちょうど、東京の京成立石駅のあたりで、とった写真があったので、それをtest.jpgとして保存し実行してみました。
a = ExifImage("test.jpg") print(a.get_gps())
実行した結果で出力されたのは
35.738226, 139.848413
これをGoogleマップの検索欄にコピーして検索してみます。
おお。
ばっちり正解です。
まさしく撮影した場所のあたりにバルーンがたってます。
スマホで撮った写真をへたに共有すると、撮影場所までばれてしまうのですね。
まあ、撮影した風景をどこだったか忘れた場合とかは助かるでしょうが、注意が必要だなと、つくづく思います。
今回はこんなところで。
ではでは。