写真(JPEG, RAW)のExif情報を一覧にして、かつ重複(現像前のRAWと現像後の複数のJPEGなど)を削除して、Excelファイルに書き出すPythonコード。割と適当。
import os
import re
from glob import glob
from tqdm import tqdm
import pandas as pd
import subprocess
import re
os.chdir(os.path.dirname(os.path.abspath(__file__)))
def get_exif(img_path):
""" ExifToolを用いて、画像ファイルの全Exif(辞書型)を取得する """
prog = r"D:\Tools\exiftool\exiftool.exe"
result = subprocess.run(f'{prog} "{img_path}"',
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
exif_dict = dict()
for line in result.stdout.splitlines():
sp = line.split(":", maxsplit=1)
key = re.sub(r"\s+$", "", sp[0])
val = re.sub(r"^\s+", "", sp[1])
exif_dict[key] = val
return exif_dict
def main(images, exif_list):
""" 画像ファイルリストを受け取り、Exif情報をdfとして返す """
# key=path, value=listの辞書を作成して、あとからdfに変換する
df_dict = dict()
for img_path in tqdm(images):
exif_dict = get_exif(img_path)
data = []
for key in exif_list:
# 出力したいExifだけをdataに格納する
if key in exif_dict.keys():
data.append(exif_dict[key])
else:
data.append("-")
df_dict[img_path] = data
# DataFrameに変換する
df = pd.DataFrame.from_dict(df_dict, orient="index")
df.columns = exif_list
return df
def remove_dup(df):
""" 重複するデータを削除する(現像前と現像後など) """
recpl = re.compile("[A-Z_][A-Z0-9]{2}[[A-Z0-9_]\d{4}")
# 1ループ目でbasenameと優先度、取得する
for idx in df.index:
# 現像後"20210101_DP2M0001_2.jpg"みたいなファイル名から
# "DP2M0001"の部分だけを取得する
res = recpl.search(idx)
if res:
basename = res.group()
df.at[idx, "basename"] = basename
lens_name = str(df.at[idx, "Lens ID"])
if idx[-4:].lower() == ".jpg":
# rawとjpgがあった場合、jpgを優先する
priority = 100
else:
priority = 0
if lens_name.lower() != "nan" and lens_name != "-":
# レンズ名の入っている方を優先
priority += 10
df.at[idx, "priority"] = priority
else:
# basenameが取れなかった場合、削除
df = df.drop(index=idx)
# 2ループ目で、重複するデータを削除する
for idx in tqdm(df.index):
# 既に削除済みの可能性があるため判定
if idx in df.index:
basename = df.at[idx, "basename"]
date = df.at[idx, "Date/Time Original"]
# ファイル名(拡張子除く)と日付が重複するデータを取得
df_temp = df[((df["Date/Time Original"]==date) & (df["basename"]==basename))]
# df_temp = df[(df["Date/Time Original"]==date)]
if df_temp.shape[0] > 0:
# priorityで降順ソート
df_temp_sort = df_temp.sort_values("priority", ascending=False)
for idx2 in df_temp_sort.index.to_list()[1:]:
# priorityが最大のものだけを残して削除
df = df.drop(index=idx2)
# 重複判定のためだけに作った列を削除
df = df.drop(columns=["basename", "priority"])
return df
if __name__ == "__main__":
# 対象とする画像ファイルリスト
files = glob(r"D:\Photo\**\*.jpg", recursive=True)
files += glob(r"D:\Photo\**\*.raf", recursive=True)
files += glob(r"D:\Photo\**\*.arw", recursive=True)
files += glob(r"D:\Photo\**\*.pef", recursive=True)
files += glob(r"D:\Photo\**\*.x3f", recursive=True)
# 出力したいExif情報リスト
exif_list = ["Date/Time Original",
"Exposure Time",
"F Number",
"ISO",
"Focal Length",
"Focal Length In 35mm Format",
"Exposure Compensation",
"Make",
"Camera Model Name",
"Lens ID",
"Lens Model"]
# いったん全画像ファイルのExifをdfとして取得
print("get exif data")
df = main(files, exif_list)
# 重複するデータを削除
print("remove duplicated data")
df = remove_dup(df)
# Excel形式で出力
df.to_excel("photo.xlsx")
print("finished")
ちなみに、外部プログラムとしてExifTool.exeを1回ずつキックしているので、処理は遅い。
ライブラリPILでもレンズ名以外は取れるし、ライブラリPyexiv2でも一部のレンズ名は取れて、これらはExifToolよりも数十倍速いが、ExifToolが一番多くの情報を取れる。
ライブラリPyExifToolを使うとExifTool.exeを開きっぱなしにしてPipeでファイルを渡せるので、外部プログラムとして1回ずつExifTool.exeを起動するよりも10倍近く速く、同じ情報が取れるのだが、なんか安定しない(時々取得できない)。なので、仕方なしに外部プログラムを1回ずつキックしている。ださい。
遅い。15,000枚弱でちょうど1時間かかった。