こうかの雑記

こうかの雑記

昔の懐かしいこと、ubuntuのこと、その他いろいろ

Pythonで写真ファイル名の一括変更

 デジカメやスマートフォンで撮影した写真は、基本的にはその日の内にPCに取り込んでモニター画面で見るようにしています。と同時にPCに保存しています。
 ところでデジカメから取り出した写真をPCの保存フォルダーに移す時「既に同じ名前のファイルがあります」といった意味のメッセージと共に、中途半端にコピーの途中で中断されてしまうことがあります。私のスマートフォンで撮影した写真の場合はファイル名が"IMG_20181025_121522.jpg"といった形のために、このようなファイル名の衝突は起きません。
 しかしデジカメの場合は起こり得ます。デジカメのファイル名の採り方も同じように撮影日時を使ってくれていれば良かったのですが、現実にはそのようになっておらず、連番方式になっています。(私のデジカメは古いので最近のデジカメはどうか知りません。)

 我が家の場合、わたしたち夫婦がそれぞれデジカメ(同じメーカ)を持っていますし、一つに合わせると衝突しまくることが確実です。なので現状はカメラ毎に保存フォルダーをメモリーカード単位で分けています。
 できたら一箇所で保存したいものです。

 ということで写真のファイル名を一括で日付を含んだ名前に置き換えるpythonスクリプトを作ってありました。作ったのは何年か前ですが、今回少し手を加えて操作性を改善してみました。

 このスクリプトを使うと、指定フォルダー内に存在する"JPG"の拡張子を持ち、EXIF情報を持つファイルを一気に名前変更出来ます。
 EXIF情報というのは写真に関する情報で、撮影日時だけでなくシャッター速度、露出時間、レンズ絞りなど多くのじょうほうを持っています。トリミングしたりして加工した写真ではEXIF情報は失われるようです。
 このEXIF情報にどんな項目があるかについては"TIFF Tag Reference, Exif Tags"
https://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
を参照ください(英語です。)

 という訳で次の様な簡単な仕様でスクリプトを書きました。
 今回はEXIF情報から撮影日時を取り出して使っています。

 簡単にスクリプトの使い方を書きます。
1) 空の作業用フォルダーを用意し、ここにデジカメのメモリカードから写真ファイルをコピーします。
2) 当スクリプトを実行します。
  準備した作業用フォルダーを選択指定します。

  次にファイルの先頭につける任意の文字列を指定します。1文字でも良いです。
  ここでCancelボタンをクリックすると処理を中断します。
  何も指定しなければ省略時解釈として"A"になります。指定した文字列を先頭にし、その後に年月日_時分秒を付けてファイル名とリネームします。
  例)"A20191217_012035.jpg"

f:id:koukaforest:20191214151929p:plain

端末画面

  端末画面に変更前ファイル名と変更後ファイル名、そして変更数が表示されます。
  EXIFを持たない画像の場合は、その旨のメッセージとファイル名を表示します。
  基本的にカメラからコピーしたばかりの写真なのでEXIF無しはない筈です。

3) 変更数を確認します。
  2度実行した場合、変更前と変更後のファイル名が同じになるので変更はされません。
4) 作業フォルダーから保存したいフォルダーにコピーします。
  この時にはファイル名衝突は起こらない筈です。

 尚、取り出した時間が秒までなので、一秒以内に数枚撮られる連写をした場合は衝突すると思いますが、対応していません

 利用は自己責任でお願いします。

 

スクリプト
 EXIF情報取り出し部分について「Life with Python」の中の記事「Python Tips:画像の Exif データを取得したい」https://www.lifewithpython.com/2014/12/python-extract-exif-data-like-data-from-images.html に少しだけ手を加えて利用させて頂いています。

 

#! /usr/bin/env python3
# coding: utf-8
"""
    p_rename.py
    デジカメファイル名の変更(〜.JPGを文字+日付時間.JPGに)

    参考にしたウェブページ
    https://www.lifewithpython.com/2014/12/python-extract-exif-data-like-data-from-images.html 
"""
# ライブラリ読み込み
import os, tkinter, tkinter.filedialog, tkinter.messagebox
import tkinter.simpledialog as simpledialog
import glob

from PIL import Image
from PIL.ExifTags import TAGS

# 関数の定義 01
def get_exif_of_image(file):
    """
    Get EXIF of an image if exists.
    指定した画像のEXIFデータを取り出す関数
    @return exif_table Exif データを格納した辞書
    """
    im = Image.open(file)
    exif_table = {}
    # Exif データを取得
    # 存在しなければそのまま終了 空の辞書を返す
    try:
        exif = im._getexif()
    except AttributeError:
        return exif_table
        print('debug')
    # タグIDそのままでは人が読めないのでデコードして
    # テーブルに格納する
    exif_table = {}
    try:
        for tag_id, value in exif.items():
            tag = TAGS.get(tag_id, tag_id)
            exif_table[tag] = value
    except AttributeError:
        print('ExifデータがないJPG:',file)
        
    return exif_table

# => Exif 情報を格納した辞書
#    Exif 情報がない場合には空の辞書

def get_date_of_image(file):
    """Get date date of an image if exists
    指定した画像の Exif データのうち日付データを取り出す関数
    @return yyyy:mm:dd HH:MM:SS 形式の文字列
    """
    # get_exif_of_imageの戻り値のうち
    # 日付データのみを取得して返す
    exif_table = get_exif_of_image(file)
    if exif_table == {}:    # Exif データが無かった場合
        datetime = 0
    else:
        datetime = exif_table.get("DateTimeOriginal")
    return datetime

def main():
    print('p_rename.py 指定ディレクトリの写真を一括renameする')
    id = ''
    message_text = ''
    root = tkinter.Tk()
    root.withdraw()
    tkinter.messagebox.showinfo('p_rename.py',
    '指定フォルダーの写真の名前を付け替えます。フォルダーを選択してください!')
    dir = tkinter.filedialog.askdirectory()
    os.chdir(dir)
    files = glob.glob( '*.JPG')+glob.glob( '*.jpg')
    if files == []:
        message_text = dir + 'に写真がありません'
    else:
        id = simpledialog.askstring("Input Box", "撮影者識別文字を入れて下さい")
        if id == None:
            id = ''
        else:
            if id == '':
                id = 'A'
    if id == '':
        if message_text == '':
            message_text = '中止しました'
    else:
        print('撮影者識別文字:',id)
        cnt = 0
        for f in files:
            dt = get_date_of_image(f)
            if dt != 0:
                newf = id + dt[0:4] + dt[5:7] + dt[8:10] + '_' + dt[11:13] + dt[14:16] + dt[17: ] + '.JPG'
                if f != newf:
                    print(f, dt, '→ ', newf)
                    os.rename(f, newf)
                    cnt = cnt +1
        message_text = '変換数='+ str(cnt)

    tkinter.messagebox.showinfo('p_rename.py', message_text)
    root.destroy()
    
if __name__ == '__main__':
    main()