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