プログラムでおかえしできるかな

Python、フェイジョア、日常のあれこれでお返し、元SEの隠居生活。

PillowでExif情報の撮影条件を取得【Python】

このエントリーをはてなブックマークに追加

Python の画像ライブラリ Pillow を使用すると、画像ファイルの Exif 情報を取得できます。
Exif 情報の中の撮影条件を取得するには一手間必要です。
その方法を解説します。

目次

◆画像ビューア アプリのサンプル画像

次の記事で紹介している画像ビューアで撮影条件を出力しています。
 📔 画像ビューアの作り方(Treeviewに画像と疑似チェックボックス)【Python】 🔗

▽画像のサムネイルと画像情報(撮影条件を含む)を表示します

ここでの撮影条件とは、カメラで撮影する時の絞りやシャッタースピードISO感度などの Exif 情報として記録されているものを指します。

◆画像の Exif 情報の取得方法

前述のアプリは Python で書かれています。
Python には Pillow と呼ばれる画像を扱うライブラリがあります。
Pillow の open() メソッドで画像を読み込み Image オブジェクトを作成します。
Image オブジェクトには Exif 情報を取得するメソッド getexif() があります。
実行すると Exif オブジェクトを返します。
Exif オブジェクトは collections.abc.MutableMapping ( 辞書のようなもの ) を継承しています。
 ※_getexif() メソッドは古いメソッド(戻り値は dict)

Exif オブジェクトはキー (TAGID) が数字なのでそのままでは読みずらく、理解しやすい文字に変換します。
変換には、Pillow パッケージの ExifTags モジュールにある TAGS を使用します。

▽タグの例

TAGID 略称 日本語の説明 英語の説明
256 ImageWidth 画像の幅 Image width
306 ModifyDate ファイル変更日時 File change date and time
34853 GPSInfoIFDPointer GPS タグ GPS IFD pointer

【処理】

  1. Exif のタグを取得 getexif()
  2. TAGS で読みやすい文に変換
  3. 改行でつないで表示用文字列を作成

【コード】

from PIL import Image, ImageTk          # Pillow
from PIL.ExifTags import TAGS, GPSTAGS  # Exifタグ情報

    # 画像の取得
    image1 = Image.open(file_name)

    # Exif情報の取得
    exif_dict = image1.getexif()
    exif = [TAGS.get(k, "Unknown")+ f": {str(v)}" for k, v in exif_dict.items()]
    exif_str = "\n".join(exif)

GPS情報の取得方法

GPS 情報は、Exif 情報のタグ ID 34853 が該当します。
その内容を取得するには、Exif オブジェクトの get_ifd() メソッドを使用します。
GPS 情報もキーが数字なので Exif 情報と同様に変換して読みやすくします。
こちらは、Pillow パッケージの ExifTags モジュールにある GPSTAGS を使用します。

【処理】

  1. GPS 情報を取得 get_ifd(34853)
  2. GPSTAGS で読みやすい文に変換
  3. 改行でつないで表示用文字列を作成

【コード】

    # GPS情報の取得
    gps_dict = exif_dict.get_ifd(34853)
    gps = [GPSTAGS.get(k, "Unknown") + f": {str(v)}" for k, v in gps_dict.items()]
    gps_str = "\n".join(gps)

◆撮影条件の取得方法

撮影条件は、Exif 情報の Exif タグに保存されています。
Exif タグは、タグ ID 34665 が該当します。
その内容を取得するには、Exif オブジェクトの get_ifd() メソッドを使用します。
Exif タグの情報も Exif 情報と同様に変換して読みやすくします。
こちらは、Pillow パッケージの ExifTags モジュールにある TAGS を使用しても可能なのですが、別途作成しました。

◇get_ifd()メソッドで取得できるタグ

GPS 情報や Exif タグ情報以外にも get_ifd() メソッドで取得できるタグが全部で4種類あります。

タグの種類 タグID(16進) タグID(10進) 内容
Exifタグ 0x8769 34665 撮影条件データなどの情報
GPSタグ 0x8825 34853 撮影場所に関する GPS 情報
互換性IFD 0xA005 40965 旧バージョンとの互換性のための情報
メーカーノート 0x927C 37500 機器メーカー毎の独自情報

Exifタグの中身

Exif タグの中には、絞りやシャッタースピードISO感度などの撮影条件があります。

【主な Exif タグの中身】

  • 露出時間(33434):カメラのシャッタースピードで見られる値
  • シャッタースピード(37377):露出時間の単位をAPEXにしたもの
    露出時間と同じ意味のもの
  • Fナンバー(33437):カメラの絞りで見られる値
  • 絞り値(37378):F ナンバーの単位を APEX にしたもの
    F ナンバーと同じ意味のもの

◇撮影条件に関するタグ

撮影条件のタグの名称を画像ビューアに表示するため、タグ番号と名称の辞書を作成しました。

Pillow の ExifTags.TAGS にも名称はあるのですが、日本語にしたかったのと出力する項目を選択したかったので別途、作成しました。

▽タグ情報の定数

    SHOOTING_CONDITIONS = {
        # 撮影条件に関するタグ from JEITA CP-3451C  表示したくないものはコメントにしてある
        33434:"露出時間", #  ExposureTime
        33437:"F ナンバー", #  FNumber
        34850:"露出プログラム", #  ExposureProgram
        34852:"スペクトル感度", #  SpectralSensitivity
        34855:"撮影感度", #  PhotographicSensitivity
        34856:"光電変換関数", #  OECF
        34864:"感度種別", #  SensitivityType
        34865:"標準出力感度", #  StandardOutputSensitivity
        34866:"推奨露光指数", #  RecommendedExposureIndex
        34867:"ISO スピード", #  ISOSpeed
        34868:"ISO スピードラチチュード yyy", #  ISOSpeedLatitudeyyy
        34869:"ISO スピードラチチュード zzz", #  ISOSpeedLatitudezzz
        # 37377:"シャッタースピード", #  ShutterSpeedValue APEX値
        # 37378:"絞り値", #  ApertureValue APEX値
        # 37379:"輝度値", #  BrightnessValue APEX値
        37380:"露光補正値", #  ExposureBiasValue APEX値
        # 37381:"レンズ最小F値", #  MaxApertureValue APEX値
        37382:"被写体距離", #  SubjectDistance
        37383:"測光方式", #  MeteringMode
        37384:"光源", #  LightSource
        37385:"フラッシュ", #  Flash
        37386:"レンズ焦点距離", #  FocalLength
        37396:"被写体領域", #  SubjectArea
        41483:"フラッシュ強度", #  FlashEnergy
        41484:"空間周波数応答", #  SpatialFrequencyResponse
        # 41486:"焦点面の幅の解像度", #  FocalPlaneXResolution
        # 41487:"焦点面の高さの解像度", #  FocalPlaneYResolution
        # 41488:"焦点面解像度単位", #  FocalPlaneResolutionUnit
        41492:"被写体位置", #  SubjectLocation
        41493:"露出インデックス", #  ExposureIndex
        41495:"センサ方式", #  SensingMethod
        # 41728:"ファイルソース", #  FileSource
        41729:"シーンタイプ", #  SceneType
        41730:"CFA パターン", #  CFAPattern
        41985:"個別画像処理", #  CustomRendered
        41986:"露出モード", #  ExposureMode
        41987:"ホワイトバランス", #  WhiteBalance
        41988:"デジタルズーム倍率", #  DigitalZoomRatio
        41989:"35mm 換算レンズ焦点距離", #  FocalLengthIn35mmFilm
        41990:"撮影シーンタイプ", #  SceneCaptureType
        41991:"ゲイン制御", #  GainControl
        41992:"撮影コントラスト", #  Contrast
        41993:"撮影彩度", #  Saturation
        41994:"撮影シャープネス", #  Sharpness
        41995:"撮影条件記述情報", #  DeviceSettingDescription
        41996:"被写体距離レンジ", #  SubjectDistanceRange 
    }

【処理】

  1. Exif タグを取得 get_ifd(34665)
  2. シャッタースピードだけ分数に変換(ただし0.3以下の時)
  3. キー(タグ ID)でソート
  4. 作成した SHOOTING_CONDITIONS で読みやすい文に変換(存在するものだけを出力)
  5. 改行でつないで表示用文字列を作成

【コード】

       # exifタグ情報の取得
        pvtag_dict = exif_dict.get_ifd(34665)
        if 33434 in pvtag_dict and  pvtag_dict.get(33434) < 0.3: # シャッタースピードだけ分数に変換ただし0.3以下の時
            pvtag_dict[33434] = str(Fraction(pvtag_dict.get(33434)))
        pvtag = sorted(pvtag_dict.items())  # キーでソート。結果はタプルのリスト
        pvtag = [ImageOp.SHOOTING_CONDITIONS.get(k) + f": {str(v)}" for k, v in pvtag if k in ImageOp.SHOOTING_CONDITIONS]  # SHOOTING_CONDITIONSに存在するものだけ(撮影条件)
        pvtag_str = "\n".join(pvtag)

Pythonでの分数表示

標準ライブラリfractionsモジュールを使うことで分数を扱うことができます。

使用にはインポートが必要です。from fractions import Fraction

【コンストラクタの例】

  • Fraction(1, 3)⇒1/3
  • Fraction(0.25)⇒1/4
  • Fraction('2/5')⇒2/5

文字列へ変換する場合は、str(a) とすると 1/4 の形の文字列になります。

◆さいごに

最初に画像ビューアを作った時、Exif 情報を出力すればデジカメの撮影条件も出力されていると思っていました。

GPS 情報だけが特別と思い、そこだけ注意していました。

月の写真を撮った後、どういう設定で撮ったかを確認するために自分で作った画像ビューアを起動しました。
その時に撮影条件が出力されていないことに気が付きました。

Exif の規格も良く知らないし、Pillow の仕様も読み切れないし、撮影条件の取得方法を見つけるのに苦労しました。

この記事で、他の方が苦労せずに済むと幸いです。


あわせて読みたい 撮影条件を表示する画像ビューアを使いたいならこちらの記事で提供しています。
画像ビューアの作り方も紹介しています。
📔 画像ビューアの作り方(Treeviewに画像と疑似チェックボックス)【Python】 🔗

◇ご注意

本記事は次のバージョンの下で動作した内容を基に記述しています。

◇免責事項

ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。

◆参考

投稿: