よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

【Pillow】画像ファイルからExif情報を削除する

今回は、Pythonを使って画像ファイルからExif情報を削除する。

こちらの画像を使う。


昨年2018年に熊本県山鹿市にある県立装飾古墳館にてスマホで撮影したものです。

熊本県立装飾古墳館
玉名市から菊池川を登って行くと古墳群があり、その一角に立つナイスな資料館。オススメ!



さて、Exif情報の削除用モジュールやコマンドなど便利なツールが色々あるようですが、ここでは画像処理モジュールのPillowを使って行います。
モジュールのインストールが必要です。

pip install pillow



参考
【Pillow】スマホで画像情報EXIFの取得テスト - よちよちpython
【Pillow】Pythonで画像のサイズと解像度変更 - よちよちpython



目次




実装




画像ファイルの容量を取得する関数


import os

def get_size(file_path):
    """
    (画像)ファイルの容量を取得する関数。

    引数:ファイルパス
    返り値:ファイルの容量
    """
    

    file_size = os.path.getsize(file_path)

    return file_size



画像ファイルからExif情報を取得する関数


from PIL import Image
from PIL.ExifTags import TAGS


# 関数の定義 01
def get_exif(file):
    """Get EXIF of an image if exists.

    指定した画像のEXIFデータを取り出す関数
    @return exif_table Exif データを格納した辞書
    """
    im = Image.open(file)

    # Exif データを取得
    # 存在しなければそのまま終了 空の辞書を返す
    try:
        exif = im._getexif()
    except AttributeError:
        return {}
    
    # タグIDそのままでは人が読めないのでデコードして
    # テーブルに格納する
    exif_table = {}
    for tag_id, value in exif.items():
        tag = TAGS.get(tag_id, tag_id)
        exif_table[tag] = value

    return exif_table



画像ファイルからExif情報を削除した画像を新規作成する関数


from PIL import Image

def del_exif(file_path):
    """
    画像ファイルからExif情報を削除した画像を
    カレントディレクトリに新規作成する関数。
    
    引数:画像のパス
    """

    with Image.open(file_path) as in_f:
        data = in_f.getdata()
        mode = in_f.mode
        size = in_f.size
    
    with Image.new(mode, size) as out_f:
        out_f.putdata(data)
        #新規ファイル名
        new_file = "delExif_" + file_path
        #新規画像保存
        out_f.save(new_file)



実行


最初に貼り付けた古墳の画像ファイル名をそのまま使う。
DSC_0240.JPGというファイル名になっています。

file_path = "DSC_0240.JPG"

print(get_size(file_path))
print("================")

print(get_exif(file_path))
print("================")

del_exif(file_path)
3181536
================
{'ExifVersion': b'0220', 'ComponentsConfiguration': b'\x01\x02\x03\x00', 'ShutterSpeedValue': (400, 100),

== 中略 ==
 \xd1\xf0\xb5\xfb\x1b\xa9\xdf\xc5\xce\x00'}
================



Exif情報削除後のファイルの容量とExif情報の取得


Exifは削除したので何も取得できない筈だけど、一応そのまま実行する。


delExif_file = "delExif_DSC_0240.JPG"

print(get_size(delExif_file))

print(get_exif(delExif_file))
814886



--------------------------------------------

AttributeErrorTraceback (most recent call last)

<ipython-input-11-bcb6c95d7d43> in <module>
      3 print(get_size(delExif_file))
      4 
----> 5 print(get_exif(delExif_file))


<ipython-input-2-b3120f369718> in get_exif(file)
     22     # テーブルに格納する
     23     exif_table = {}
---> 24     for tag_id, value in exif.items():
     25         tag = TAGS.get(tag_id, tag_id)
     26         exif_table[tag] = value


AttributeError: 'NoneType' object has no attribute 'items'



【追記】 画像の劣化について

コメントでhassylinさんとsenさん、かーなびさんから画像の劣化に関する指摘・ご意見を頂きました。この場を借りて御礼申し上げます。ありがとうございます。
画像の劣化につきまして当方はよく理解しておりません(配列がどのように変化するか、劣化が意味するところ、解像度、アルゴリズム等)。
自分のブログにアップしている画像は大抵うえの方法で軽くした上にサイズの縮小も行ったものを使っておりまして(アップロードと表示スピード向上目的のため)、気になるほどの画質の低下を個人的にはそこまで感じませんが、もし画質を下げずにEXIF情報だけを削除したい場合はコメント欄のsenさんとかーなびさんの推奨されている方法をご参考ください。



おわりに


Exif情報を削除しただけで、かなり画像の容量が小さくできる。(画像の劣化については上記の追記を参照)

削除後にExif取得したらエラーが出たので、tryとexceptとかで処理するのがいいですかね。

以上です。