"BOKU"のITな日常

62歳・文系システムエンジニアの”BOKU”は日々勉強を楽しんでます

Pythonで、写真データからExif情報を取り出して遊んでみる

 スマホやデジカメで撮影した画像ファイルには、Exifと呼ばれる付加情報が埋め込まれているものがあります。

今回は、それを取り出して、遊んでみます。

f:id:arakan_no_boku:20200707230113p:plain

 

Exif情報とは

 

Exifは、Exchangeable image file format(エクスチェンジャブル・イメージ・ファイル・フォーマット)の略で、「エグジフ」もしくは「イグジフ」と読みます。

写真用のメタデータを含む画像ファイルフォーマットのことで、JPEGTIFFJPEG XR(HD Photo)などの画像形式が対応しています。

 

 

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情報を記録しないフォーマットになっているようです。

ということで。

以前、使った自分のスマホでとった情報をみてみます。

f:id:arakan_no_boku:20200722223002p:plain

結果はこんな感じです。

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

 主な結果のTAGの日本語訳です。

Exifタグ 意味
ApertureValue 絞り
BrightnessValue 輝度
ColorSpace 色空間情報
ExposureBiasValue 露出補正値
ExposureMode 露出モード
ExposureProgram 露出プログラム
ExposureTime 露出時間
Flash フラッシュ有無
FlashPixVersion 対応フラッシュピックスバージョン
FNumber F値
FocalLength 焦点距離
FocalLengthIn35mmFilm 35mm 換算焦点距離
ImageLength 画像サイズ
ImageWidth 画像幅
SensingMethod センサー方式
ShutterSpeedValue シャッタースピード
WhiteBalance ホワイトバランス

他の項目はこちらにタグ情報の一覧があったのでリンクだけ貼っておきます。

cachu.xrea.jp

 

GPS情報で撮影場所もわかる場合があります

 

スマホGPS情報の埋め込みが許可されているなど、いろいろ条件がそろう必要がありますが、exifGPS情報が埋め込まれている場合があります。

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マップの検索欄にコピーして検索してみます。

f:id:arakan_no_boku:20200723004334p:plain

おお。

ばっちり正解です。

まさしく撮影した場所のあたりにバルーンがたってます。

スマホで撮った写真をへたに共有すると、撮影場所までばれてしまうのですね。

まあ、撮影した風景をどこだったか忘れた場合とかは助かるでしょうが、注意が必要だなと、つくづく思います。

今回はこんなところで。

ではでは。