🖼️

pillowを使って画像ファイル(png, jpg)にメタデータを保存する

2024/12/28に公開

執筆日

2024/12/27

概要

画像のキャプションをあらかじめファイルにメタデータとして保存しておきたいときに、pillowライブラリで簡単に対応する形式のメタデータ保存できることを知ったのでメモ書きです。画像の説明を生成AIなどで作ったときに、そのキャプションを検索対象にするサービスを実装するのであればデータベースで管理するのがいいですが、そこまでリッチな構成が必要ない、画像とキャプションが同じところにあればいいなというときに使えそうです。(GUIアプリで画像クリックorホバーでキャプションが表示されるとか)

依存ライブラリインストール

pip install pillow # テスト時 ver. 10.4.0
pip install piexif # jpgの場合に必要

スクリプト

png

日本語1800文字(+100回改行)の長めのキャプションを追加しても問題なく保存・読み取りができました。データの増加は6KB程度でした(日本語で使われる文字が基本3Bytesなので改行コードと合わせてそのまま増えている感じですね)

add_png_metadata.py
from PIL import Image
from PIL import PngImagePlugin

# メタデータのインスタンス作成
# add_text(key, value)で追加
metadata = PngImagePlugin.PngInfo()
metadata.add_text("caption", "test caption")
test_long_caption = "長いキャプション情報を追加するテスト\n" * 100
metadata.add_text("long_caption", test_long_caption)

# 画像読込
image_path = "image.png"
img = Image.open(image_path)

# メタデータを付与して保存
output_path = "image_with_metadata.png"
img.save(output_path, pnginfo=metadata)

# メタデータの取得
# .infoで辞書形式で取得できる
img = Image.open(output_path)
print("caption:", img.info["caption"])
print("long caption:", img.info["long_caption"])

jpg

jpgはexifという形式のメタデータを持ちます。exifは撮影情報やGPS情報を保存することを目的として固定されたフィールドを持ちますが、キャプションを付けたければUserCommentというフィールドが使えます。ただ、exif多くのフィールドを持つせいかpillowだけで気の利いた実装がされているわけではなく、コメントを自分でエンコードしたり、別のライブラリを使ってexif形式のメタデータを作ったりしなければなりません。(TIFFやWebPも同じくexifを使えますが今回は省略)
pngと同じ長文キャプションを保存してみたところデータが4KB減ったので元々あったメタデータを消してしまっているかも……。必要なメタデータが既にある場合は気を付けてください。

add_jpg_metadata.py
from PIL import Image
import piexif

# 画像を開く
image = Image.open("test.jpg")

long_caption = "長いキャプション情報を追加するテスト\n" * 100
long_caption = long_caption.encode("utf-8")

# Exifデータの追加
exif_dict = {
    "Exif": {
        # piexif.ExifIFD.UserComment: "This is a custom comment with some details.".encode("utf-8"),
        piexif.ExifIFD.UserComment: long_caption,
    }
}
exif_bytes = piexif.dump(exif_dict)

# 保存
image.save("test_metadata.jpg", exif=exif_bytes)

# 画像を開く
image = Image.open("test_metadata.jpg")

# Exifデータの取得
exif_dict = piexif.load(image.info["exif"])
print(exif_dict["Exif"][piexif.ExifIFD.UserComment].decode("utf-8"))

補足

pngはpillowを使うと特に簡単に扱えて便利ですが、さすがに日本語テキストを直接書き込んでいるわけではなくutf-8でbytesにエンコードしたり、png形式のためのデータ変換を行ったりしています。PngInfoのクラス関数がそこまで複雑なことをしているわけではありませんが、非常に気の利いた実装になっていて温もりを感じます。もしライブラリを使わずに自分でメタデータ付与のコードを書いてみたい場合はtEXt、zTXt、iTXtチャンクなどを調べてみてください。

参考

https://pillow.readthedocs.io/en/stable/PIL.html#pngimageplugin-pnginfo-class

ヘッドウォータース

Discussion