(102) PILで写真に日付を入れる

投稿者: | 2018年9月19日

8,431 views

この記事は最終更新から 2342日 が経過しています。

1. やりたいこと

写真の右下に「1988/10/16」とかプリントされるあれを、写真画像に付けてみたい。

2. 実現手段

1) 日付は、写真画像に埋め込まれている EXIF情報から取得する。
2) 写真画像への日付の表示には PIL(Pillow)を使う。

3. やってみる

こんな画像でやってみる。2018年1月14日に山梨県の山中湖で撮影した写真だ。

Step 1 : EXIF情報を見てみる。

画像ファイルから読みだした EXIF情報を列挙してみるだけのプログラムを書いた。

from PIL import Image
from PIL import ExifTags

img = Image.open("img001.jpg")
exif = img._getexif()

for id, val in exif.items():
    tag = ExifTags.TAGS.get(id)
    print("%d : %s : %s" % (id, tag, val))

実行結果は以下の通り。(長いものは省略した)
この中で日付っぽいものを色付けしてみた。
緑色のコメントは、後から自分が付け足した。

271 : Make : xxxxx
272 : Model : xxxxx
274 : Orientation : 1
282 : XResolution : (72, 1)
283 : YResolution : (72, 1)
296 : ResolutionUnit : 2
305 : Software : xxxxx
306 : DateTime : 2018:01:14 15:11:41 # 画像ファイルの更新日
531 : YCbCrPositioning : 1
33434 : ExposureTime : (1, 934)
33437 : FNumber : (9, 5)
34850 : ExposureProgram : 2
34853 : GPSInfo : xxxxx
34855 : ISOSpeedRatings : 20
34665 : ExifOffset : 206
36864 : ExifVersion : b'0221'
36867 : DateTimeOriginal : 2018:01:14 15:11:41 # 撮影された日時
36868 : DateTimeDigitized : 2018:01:14 15:11:41 # 画像がデジタルデータ化された日時
37121 : ComponentsConfiguration : xxxxx
37377 : ShutterSpeedValue : (8298, 841)
37378 : ApertureValue : (2159, 1273)
37379 : BrightnessValue : (9533, 1036)
37380 : ExposureBiasValue : (0, 1)
37383 : MeteringMode : 5
37385 : Flash : 16
37386 : FocalLength : (399, 100)
37396 : SubjectLocation : (2015, 1511, 2217, 1330)
37500 : MakerNote : xxxxx
37521 : SubsecTimeOriginal : 425
37522 : SubsecTimeDigitized : 425
40960 : FlashPixVersion : b'0100'
40961 : ColorSpace : 65535
40962 : ExifImageWidth : 4032
40963 : ExifImageHeight : 3024
41495 : SensingMethod : 2
41729 : SceneType : xxxxx
41985 : CustomRendered : 2
41986 : ExposureMode : 0
41987 : WhiteBalance : 0
41989 : FocalLengthIn35mmFilm : 28
41990 : SceneCaptureType : 0
42034 : LensSpecification : xxxxx
42035 : LensMake : xxxxx
42036 : LensModel : xxxxx

画像に追加表示したいのは撮影日時なので DateTimeOriginal を使えばよいみたいだ。

Step 2 : 画像ファイルから DateTimeOriginal だけを取得する。

今度は関数にしてみた。

from PIL import Image

# EXIFから撮影日を取得
def get_datetime( img ):
    exif = img._getexif()
    for id, val in exif.items():
        if id == 36867:
            return val
    return ''

# 画像ファイルを開き、撮影日を取得し、結果を表示する。
img = Image.open("img001.jpg")
datetime = get_datetime(img)
print(datetime)

実行結果は以下の通り。

2018:01:14 15:11:41

Step 3 : 画像にテキストを追記する。

実は (99) OpenCV #4 : ガンマ補正で画像を見やすく調整 ですでにやっている。
これも PIL を使って実現している。

from PIL import Image, ImageDraw, ImageFont

# 画像をロード
img = Image.open("img001.jpg")

# 画像にテキスト「Hello!」を追記
obj_draw = ImageDraw.Draw(img)
obj_font = ImageFont.truetype("./ipagp.ttf", 30)
obj_draw.text((10, 10), "Hello!", fill=(255, 160, 0), font=obj_font)

# 画像表示
img.show()

こんな感じになった。
因みにフォントはフリーの IPAフォント を使わせていただいた。

ここで 2点問題がある。
1) 文字が見づらい。
2) 文字の表示位置は右下がよい。

まずは文字を見やすくするため、文字色と輝度差が大きい色で影を付けてみる。

from PIL import Image, ImageDraw, ImageFont

# 画像をロード
img = Image.open("img001.jpg")

# 画像にテキスト「Hello!」を追記
obj_draw = ImageDraw.Draw(img)
obj_font = ImageFont.truetype("./ipagp.ttf", 30)
obj_draw.text((11, 11), "Hello!", fill=(0, 0, 0), font=obj_font)
obj_draw.text((10, 10), "Hello!", fill=(255, 160, 0), font=obj_font)

# 画像表示
img.show()

次に、文字の表示位置を画像の右下にしてみる。

from PIL import Image, ImageDraw, ImageFont

# 画像をロード
img = Image.open("img001.jpg")

# テキスト表示位置を決定
pos_x = img.size[0] - 90
pos_y = img.size[1] - 40

# 画像にテキスト「Hello!」を追記
obj_draw = ImageDraw.Draw(img)
obj_font = ImageFont.truetype("./ipagp.ttf", 30)
obj_draw.text((pos_x+1, pos_y+1), "Hello!", fill=(0, 0, 0),     font=obj_font)
obj_draw.text((pos_x,   pos_y),   "Hello!", fill=(255, 160, 0), font=obj_font)

# 画像表示
img.show()

文字の表示位置は、フォントサイズと表示するテキストの長さに合わせて後で調整しよう。
※自動的に計算すればよいのだが、それは今回のテーマではないので…

Step 4 : 本題! 写真に日付を入れる。

今までの Step 1 ~ Step 3 の合わせ技で実現する。

from PIL import Image, ImageDraw, ImageFont

# EXIFから撮影日を取得
def get_datetime( img ):
    exif = img._getexif()
    for id, val in exif.items():
        if id == 36867:
            return val
    return ''

# 画像をロード
img = Image.open("img001.jpg")

# 画像の撮影日を取得
text_datetime = get_datetime(img)
text_date = text_datetime.split()[0]     # 時刻を切り離す。
text_date = text_date.replace(':','-')   # 年月日の区切り文字を変更

# テキスト表示位置を決定
pos_x = img.size[0] - 150
pos_y = img.size[1] - 30

# 画像にテキストを追記
obj_draw = ImageDraw.Draw(img)
obj_font = ImageFont.truetype("./ipagp.ttf", 24)
obj_draw.text((pos_x+1, pos_y+1), text_date, fill=(0, 0, 0),     font=obj_font)
obj_draw.text((pos_x,   pos_y),   text_date, fill=(255, 160, 0), font=obj_font)

# 画像表示
img.show()

イメージした通りになったかも。
※上記の処理中ではエラー発時を一切考慮していないので、必要に応じて追加実装しようと思う。

4. 応用編

画像の指定、色の指定、エラー処理など、汎用性、冗長性を持たせる。

(1) 画像ファイルを指定出来るようにする。

put_text.py

import argparse
from PIL import Image, ImageDraw, ImageFont

# EXIFから撮影日を取得
def get_datetime( img ):
    exif = img._getexif()
    for id, val in exif.items():
        if id == 36867:
            return val
    return ''

def exec_put_date( filepath ):
    # 画像をロード
    img = Image.open(filepath)

    # 画像の撮影日を取得
    text_datetime = get_datetime(img)
    text_date = text_datetime.split()[0]     # 時刻を切り離す。
    text_date = text_date.replace(':','-')   # 年月日の区切り文字を変更

    # テキスト表示位置を決定
    pos_x = img.size[0] - 150
    pos_y = img.size[1] - 30

    # 画像にテキストを追記
    obj_draw = ImageDraw.Draw(img)
    obj_font = ImageFont.truetype("./ipagp.ttf", 24)
    obj_draw.text((pos_x+1, pos_y+1), text_date, fill=(0, 0, 0),     font=obj_font)
    obj_draw.text((pos_x,   pos_y),   text_date, fill=(255, 160, 0), font=obj_font)

    # 画像表示
    img.show()

def main():
    # コマンドライン引数を取得
    parser = argparse.ArgumentParser()
    parser.add_argument('-f','--filepath',  required=True)
    args = parser.parse_args()
    # 画像に日付を埋め込み
    exec_put_date(args.filepath)

if __name__=="__main__": main()

Shellのコマンドラインから以下のように実行する。
-f オプションで処理対象画像のファイルパスを指定する。

python put_text.py -f img001.jpg

カテゴリー: PIL

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です