[Python] 特定のフォルダに入っている画像ファイルを撮影日時で一括リネーム

# はじめに

皆様、携帯（スマホ）のカメラで撮影した写真の管理はどうされていますか？
私の場合は、ある程度撮ったらPCにコピーし、スマホからは消す、ということをして、なるべくスマホに大量のデータを残さないようにしています。
しかし、写真をスマホから消してしまうと、ファイル名の連番が戻ってしまい、PCにコピーしようとしたときに同じ名前のファイルができてしまって面倒なことになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204084/2112a184-2998-cf77-9ae5-034b842756a5.png)

ということで、ファイル名被りの問題を解決するため、PCにコピーした画像ファイルを撮影日時を使って一括リネームする方法をご紹介します。

# 検証環境

- Cygwin (Windows 10 20H2 Home)
- Python 3.6.10
    - Pillow 7.2.0

# 事前準備

```
$ pip3 install Pillow
```

# コード

```python3:rename_images.py
from PIL import Image
from PIL.ExifTags import TAGS
from pathlib import Path
import datetime

# https://www.lifewithpython.com/2014/12/python-extract-exif-data-like-data-from-images.html
def get_exif_of_image(file):
    """Get EXIF of an image if exists.

    指定した画像のEXIFデータを取り出す関数
    @return exif_table Exif データを格納した辞書
    """
    im = Image.open(file)

    # Exif データを取得
    # 存在しなければそのまま終了 空の辞書を返す
    try:
        exif = im._getexif()
    except AttributeError:
        return {}

    # タグIDそのままでは人が読めないのでデコードして
    # テーブルに格納する
    exif_table = {}
    for tag_id, value in exif.items():
        tag = TAGS.get(tag_id, tag_id)
        exif_table[tag] = value

    return exif_table

# 画像が入っているPC側のフォルダのパスを指定
for filename in Path("/path/to/images").glob("DSC_*.JPG"):
    exif = get_exif_of_image(filename)
    if "DateTimeOriginal" in exif:
        # strftime() で新しい名前のフォーマットを指定
        new_name = Path(filename).with_name(datetime.datetime.strptime(exif["DateTimeOriginal"], "%Y:%m:%d %H:%M:%S").strftime("IMG_%Y%m%d_%H%M%S.JPG"))
        print(f"{filename} \n -> {new_name}")
        filename.rename(new_name)
    else:
        print(f"[WARNING] {filename}: no EXIF header")
```

## 解説

画像ファイルのEXIFヘッダから撮影日時を取り出し、撮影日時をもとに指定したフォーマットで新しいファイル名を作ってリネームします。

```for filename in Path("/path/to/images").glob("DSC_*.JPG"):``` 部分で、特定のフォルダに含まれている画像ファイルを列挙します。``DSC_*.JPG`` の部分はスマホのカメラアプリが生成するファイルフォーマットに合わせてください。このパターンはXperiaの場合の例です。

``get_exif_of_image()`` で、EXIFヘッダの情報を取得します。この関数は以下のページから引用しました。m(_ _)m
[Python Tips：画像の Exif データを取得したい - Life with Python](https://www.lifewithpython.com/2014/12/python-extract-exif-data-like-data-from-images.html)

``DateTimeOriginal`` という名前で撮影日時が記録されていますので、日時の文字列を ``datetime`` モジュールで解析します。この記録フォーマットは統一されているはずなので、``strptime()`` の引数をカメラごとにカスタマイズする必要はありません。
この解析結果をもとに新しいファイル名を作成します。お好みで ``strftime()`` の引数を変更してください。ただし、``DSC_%Y%m%d_%H%M%S.JPG`` のようなオリジナルと同じフォーマットの名前はやめておいたほうが良いと思います。

``strptime()`` や ``strftime()`` のフォーマット文字列の意味についてはこちらをどうぞ。
[8.1. datetime --- 基本的な日付型および時間型 — Python 3.6.12 ドキュメント](https://docs.python.org/ja/3.6/library/datetime.html#strftime-strptime-behavior)

## 実行例

以下のようにログが表示されます。ファイルが正しくリネームされているか確認してください。

```
/path/to/images/DSC_0001.JPG
 -> /path/to/images/IMG_20190928_135805.JPG
/path/to/images/DSC_0004.JPG
 -> /path/to/images/IMG_20191104_172704.JPG
/path/to/images/DSC_0005.JPG
 -> /path/to/images/IMG_20191106_174423.JPG
/path/to/images/DSC_0006.JPG
 -> /path/to/images/IMG_20191106_174752.JPG
/path/to/images/DSC_0007.JPG
 -> /path/to/images/IMG_20191106_174808.JPG
/path/to/images/DSC_0009.JPG
 -> /path/to/images/IMG_20191213_180056.JPG
/path/to/images/DSC_0010.JPG
 -> /path/to/images/IMG_20191213_180103.JPG
/path/to/images/DSC_0011.JPG
 -> /path/to/images/IMG_20191213_180112.JPG
```

# 補足

もし可能なら、撮影時点でスマホ側でリネームしておくほうが面倒がなくてよいと思います。
Androidならこういうアプリが使えます。私もこれからはアプリを使っていきます。
[「DSC Auto Rename」を使って写真撮影時にファイル名を自動的にリネームする | 忘れ荘](http://wasure.net/dscautorename/#i)