やりたいこと
インスタに写真をアップロードするにあたり正方形にしたい。
アスペクト比を維持したまま正方形にすると余白が生じるが、なんか寂しいのでExif情報(カメラやレンズ、撮影時の設定など)を入れてそれっぽくしたい。
方針
0.NEF(NikonのRAWファイル)を現像してTIFFで保存する。
1.インスタは長辺1024pxが良いらしいので、バカでかい画像を縮小する。
2.1024pxの正方形になるよう短辺に黒い余白を付ける。
3.TIFFからExif情報を取得して余白に文字を挿入する。
4.JPEGで保存する。
TIFFをドラッグアンドドロップすると1~4を処理して同じフォルダにインスタにアップできるJPEGが生成されるようにしたい。
1~4の処理はpythonにやらせるが、ドラッグアンドドロップでpythonを呼び出すためにWindowsのバッチファイルを中継させる。
これだと単ファイルしか対応できないので複数ファイルを処理するためには小細工が必要。
@echo off python exif.py %~f1
準備
ローカルでpythonを動かせる環境を構築する。
ソースと同じフォルダにExif情報取得用のexiftool.exeをダウンロードして置いておく。
(piexifだとレンズ情報が取得できなかった)
処理
ファイルパス準備
args = sys.argv fn_dir = os.path.dirname(args[1]) fn_without_ext = os.path.splitext(os.path.basename(args[1]))[0]
ドラッグアンドドロップされたファイルのフルパスはargs[1]に読み込まれる。
フォルダ(フルパス)とファイル名(拡張子なし)に分離しておく。
画像読み込み
im_tif = Image.open(args[1]) w, h = im_tif.size
TIFFを読み込み、元の画像サイズを取得しておく。
画像描画
im_sq = Image.new(im_tif.mode, (1024, 1024), (0, 0, 0))
resize_rate = 1024 / (w if w > h else h)
im_tif_resize = im_tif.resize(size=(round(w * resize_rate), round(h * resize_rate)), resample=Image.BICUBIC)
w, h = im_tif_resize.size
im_sq.paste(im_tif_resize, (int((1024 - w) / 2), int((1024 - h) / 2)))
if w < h:
im_sq = im_sq.rotate(270)
1024pxの黒い正方形を作成する。
アスペクト比を維持したまま縮小するため、長辺が1024pxになるような縮小率を計算し、resize()で縮小する。resampleにはPhotoshopで縮小するときと同じバイキュービック法を指定する。
リサイズ後の画像サイズを取得し、黒い正方形の上下中央に配置する。
縦写真の場合、右側の余白に首を左に90度傾けて読めるように文字を配置するため、画像を時計回りに90度回転しておく。
Exif情報取得
cmd = 'exiftool -j -s -LensID -Model -DateTimeOriginal -ExposureTime -FNumber -ISO -FocalLength ' + args[1] json = json.loads(subprocess.check_output(cmd, shell=True).decode()) pattern = re.compile(r'^(\d+):(\d+):(\d+) ') result = pattern.search(json[0]['DateTimeOriginal'])
exiftool.exeを実行するコマンドを準備する。
jオプションでJSONで返すよう指定し、取得したいタグを書いていく。タグ一覧はオプション無し(exiftool ファイル名)で実行したら確認できるが、それで表示されるタグ名はオプションと異なることがあるため注意(jオプションで返ってくるタグ名が使用できる)。
subprocessでコマンドを実行し、結果をデコード、さらにjsonとして変数に格納する。
撮影日時はなぜか「YYYY:MM:DD」形式なので、「YYYY/MM/DD」形式にするためにそれぞれの数字を分解しておく。
Exif情報描画
def add_text(left, top, text):
draw = ImageDraw.Draw(im_sq)
draw.text((left, top), text, (64, 64, 64), font=ImageFont.truetype(【フォントのフルパス】, 15))
add_text(21, 954, result.group(1) + '/' + result.group(2) + '/' + result.group(3))
add_text(21, 971, json[0]['Model'] + ', ' + json[0]['LensID'].replace('Zoom-', '').replace('Nikkor', 'NIKKOR'))
add_text(21, 988, json[0]['ExposureTime'] + ', F' + str(json[0]['FNumber']) + ', ISO ' + str(json[0]['ISO']) + ', ' + json[0]['FocalLength'])
テキストを描画するための関数を用意する。フォントのフルパスはWindowsの場合「\」を使用するが、エスケープとして認識されるため「\\」とする。
1段目に日付を入れる。さっきキャプチャした数字を使う。
2段目にカメラ、レンズの情報を入れる。一部のレンズで「Zoom-」が付加されていたが、公式HPの製品名には入っていないため取り除く。「Nikkor」の表記も同様。
3行目にSS、絞り、ISO感度、焦点距離を入れる。
保存
if w < h:
im_sq = im_sq.rotate(90)
im_sq.save(fn_dir + '\\' + fn_without_ext + '_exif.jpg', quality=95)
縦写真の場合元の向きに戻し、元ファイル名に「_exif」を付加して最高品質のJPEGで保存する。
ソース
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont
import sys
import os
import re
import subprocess
import json
def add_text(left, top, text):
draw = ImageDraw.Draw(im_sq)
draw.text((left, top), text, (64, 64, 64), font=ImageFont.truetype(【フォントのフルパス】, 15))
args = sys.argv
#ファイルパス取得
fn_dir = os.path.dirname(args[1])
fn_without_ext = os.path.splitext(os.path.basename(args[1]))[0]
# 画像サイズ取得
im_tif = Image.open(args[1])
w, h = im_tif.size
# キャンバス準備
im_sq = Image.new(im_tif.mode, (1024, 1024), (0, 0, 0))
resize_rate = 1024 / (w if w > h else h)
im_tif_resize = im_tif.resize(size=(round(w * resize_rate), round(h * resize_rate)), resample=Image.BICUBIC)
w, h = im_tif_resize.size
im_sq.paste(im_tif_resize, (int((1024 - w) / 2), int((1024 - h) / 2)))
if w < h:
im_sq = im_sq.rotate(270)
# Exif情報描画
cmd = 'exiftool -j -s -LensID -Model -DateTimeOriginal -ExposureTime -FNumber -ISO -FocalLength ' + args[1]
json = json.loads(subprocess.check_output(cmd, shell=True).decode())
pattern = re.compile(r'^(\d+):(\d+):(\d+) ')
result = pattern.search(json[0]['DateTimeOriginal'])
add_text(21, 954, result.group(1) + '/' + result.group(2) + '/' + result.group(3))
add_text(21, 971, json[0]['Model'] + ', ' + json[0]['LensID'].replace('Zoom-', '').replace('Nikkor', 'NIKKOR'))
add_text(21, 988, json[0]['ExposureTime'] + ', F' + str(json[0]['FNumber']) + ', ISO ' + str(json[0]['ISO']) + ', ' + json[0]['FocalLength'])
if w < h:
im_sq = im_sq.rotate(90)
im_sq.save(fn_dir + '\\' + fn_without_ext + '_exif.jpg', quality=95)
