GooglePhotoから一括ダウンロードしたらExif情報（日付）がなくなってた件について

#はじめに
GooglePhotoが容量うんぬんで困ったことになったのでAmazonPhotoに移行を思い立ちました！
えいやっと，一括ダウンロード＆アップロードして終わるかと思いきや，日付不明の画像がちらほら...
確認するとExifの中でも日付がなくなっていました．
GooglePhotoから一括ダウンロードした際に，JSONとして別ファイルに日付情報が分けられてしまったようです．

今回は，分けられてしまったExifの日付情報をPythonを使って復旧させたいと思います．

#環境

- OS:Windows10
- Python 2.7.17

#GooglePhotoからのダウンロード

[Google データ エクスポート](https://takeout.google.com/)から行うことができます．
詳細は，ググってもらったらたくさんサイトが出てくると思います．
（GooglePhotoの容量制限を受けて，移行したい人はいっぱいいるようですね...）

#ダウンロード結果
Zipを解凍すると

hogehoge.jpg
hogehoge.jpg.json

というように，画像とJSONファイルがセットで保存されていました．

JSONの中身は，だいたいこんな感じです．(本来のファイルからいじっています)

```JSON:hogehoge.jpg.json
{
  "title": "hogehoge.jpg",
  "description": "",
  "imageViews": "0",
  "creationTime": {
    "timestamp": "1111111111",
    "formatted": "2019/05/05 3:46:06 UTC"
  },
  "modificationTime": {
    "timestamp": "1111111111",
    "formatted": "2020/12/06 17:27:33 UTC"
  },
  "photoTakenTime": {
    "timestamp": "1111111111",
    "formatted": "2019/04/07 9:38:14 UTC"
  },
  "geoData": {
    "省略"
  },
  "geoDataExif": {
    "省略"
  },
  "googlePhotosOrigin": {
    "省略"
  }
}
```

GooglePhotoで同じ画像を閲覧した際の情報を確認すると，どうやら`photoTakenTime`の`formatted`の部分を撮影時刻としているようなので，ここを読み取り，画像のExifに挿入してあげたらよいというわけです．

#PythonでExifを読み取る
いろいろ調べましたが，piexifがよさそうでした．
[このサイト](https://python5.com/q/ssompesj)の「その他答え #1」の部分からサンプルコードをお借りしました．

表示するだけのサンプルを載せておきます．
一覧で表示の部分は，[こちら](http://blog.livedoor.jp/takizawa544_ienobu/archives/50899842.html)からお借りしました．

```python:piexif_test.py
# -*- coding: utf-8 -*-
import piexif

#画像ファイルのパス
filename = 'hogehoge.jpg'
#EXIF情報を得る
exif_dict = piexif.load(filename)
#一覧で表示
for ifd in ("0th", "Exif", "GPS", "1st"):
    for tag in exif_dict[ifd]:
        print(tag,piexif.TAGS[ifd][tag]["name"], exif_dict[ifd][tag])
```

#PythonでJSONを読み取る
読み取りは，[PythonでJSON 読み込み](https://qiita.com/kikuchiTakuya/items/53990fca06fb9ba1d8a7)を参考にしました．

読み取るだけのサンプルもおいておきます．
読み取る対象は`photoTakenTime`の`formatted`の部分です．

```python:json_read.py
# -*- coding: utf-8 -*-
import json

#読み取り
json_open = open('hogehoge.jpg.json', 'r')
json_load = json.load(json_open)

#対象部分の保存
#この後必要な形に成形
takenTime = json_load['photoTakenTime']['formatted'].split(' ')
date = takenTime[0].split('/') #日付
time = takenTime[1].split(':') #時間
zone = takenTime[2] #タイムゾーン

#出力
print(json_load['photoTakenTime']['formatted'])
print(date)
print(time)
print(zone)
```

#タイムゾーンの変更
JSON見たらわかると思うんですけど，タイムゾーンがUTCになってます．
日本（JST）との時差は9時間なので，datetimeを使って修正します．

JSONの読み取りにタイムゾーンの変更を加えたコードは以下のようになっています．

```python:json_read.py
# -*- coding: utf-8 -*-
import json
import datetime

#読み取り
json_open = open('hogehoge.jpg.json', 'r')
json_load = json.load(json_open)

#対象部分の保存
#datetimeに投げられるように成形
takenTime = json_load['photoTakenTime']['formatted'].split(' ')
date = takenTime[0].split('/') #日付
time = takenTime[1].split(':') #時間
zone = takenTime[2] タイムゾーン

#datetimeに投げる
basetime = datetime.time(int(time[0]), int(time[1]), int(time[2]))
basedate = datetime.date(int(date[0]), int(date[1]), int(date[2]))
            
if zone == 'UTC':
    #時差修正
    jst_date = datetime.datetime.combine(basedate, basetime) + datetime.timedelta(hours=9)
else:
    print("UTC以外のタイムゾーンです")

#Exif用に型変更
time_jp = jst_date.strftime("%H:%M:%S")
date_jp = jst_date.strftime('%Y:%m:%d')
```

#今までのを組み合わせる

以上を組み合わせたら，以下のようになりました．
個人的に，元ファイルをいきなり直接編集は嫌だったので，コピーしつつ編集してます．
コピーにはshutilを使っています．

```python:change_exif_with_copy.py
# -*- coding: utf-8 -*-
import shutil
import datetime
import piexif
import json

#画像読み取り(パスは適当)
filename = '../../temp/hogehoge.jpg'
new_file = 'hogehoge2.jpg'

#画像のコピーとExifを確認
shutil.copy2(filename, new_file)
exif_dict = piexif.load(new_file)

#JSON読み取り
json_open = open(filename + '.json', 'r')
json_load = json.load(json_open)

#日時部分の保存
#この後必要な形に成形
takenTime = json_load['photoTakenTime']['formatted'].split(' ')
date = takenTime[0].split('/') #日付
time = takenTime[1].split(':') #時間
zone = takenTime[2] #タイムゾーン

#datetimeに投げる
basetime = datetime.time(int(time[0]), int(time[1]), int(time[2]))
basedate = datetime.date(int(date[0]), int(date[1]), int(date[2]))
            
if zone == 'UTC':
    #時差修正
    jst_date = datetime.datetime.combine(basedate, basetime) + datetime.timedelta(hours=9)
else:
    print("UTC以外のタイムゾーンです")

#Exif用に型変更
time_jp = jst_date.strftime("%H:%M:%S")
date_jp = jst_date.strftime('%Y:%m:%d')

#Exif情報の変更
exif_dict['0th'][piexif.ImageIFD.DateTime] = date_jp + " " + time_jp
exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = date_jp + " " + time_jp
exif_dict['Exif'][piexif.ExifIFD.DateTimeDigitized] = date_jp + " " + time_jp
exif_bytes = piexif.dump(exif_dict)

#Exifの変更反映
piexif.insert(exif_bytes, filename)
```

たぶんこれでいけると思います．

#さらに改善
ここまで書いて気付いたんですが，画像は何百何千とあるしExifが欠落してないものもあるやん...
というわけで，globを使って全ファイル参照と日付あるなしによる分類をします．

```python:change_exif_with_copy.py
# -*- coding: utf-8 -*-
import glob
import shutil
import datetime
import piexif
import json

#パスは適当
path = '../../temp/'
new_path = 'photo_add_exif/'

#jpgの探索
for f in glob.glob(path + '*.jpg'):
    #画像読み取り(パスは適当)
    exif_dict = piexif.load(f)

    #Exifに日付があるか確認
    #なかったら処理開始
    if exif_dict['0th'].get(piexif.ImageIFD.DateTime) == None \
    and exif_dict['Exif'].get(piexif.ExifIFD.DateTimeOriginal) == None \
    and exif_dict['Exif'].get(piexif.ExifIFD.DateTimeDigitized) == None:

        #globでフルの相対パスが帰ってくるのでファイル名のみ取り出す
        all_path = f.split('/')
        filename = all_path[len(all_path) - 1]

        #画像のコピーとExifを再読取り（再読取りはいらないかもしれない）
        shutil.copy2(path + filename, new_path + filename)
        exif_dict = piexif.load(new_path + filename)

        #以下，1つ前と同じ
        #JSON読み取り
        json_open = open(path + filename + '.json', 'r')
        json_load = json.load(json_open)

        #日時部分の保存
        #この後必要な形に成形
        takenTime = json_load['photoTakenTime']['formatted'].split(' ')
        date = takenTime[0].split('/') #日付
        time = takenTime[1].split(':') #時間
        zone = takenTime[2] #タイムゾーン

        #datetimeに投げる
        basetime = datetime.time(int(time[0]), int(time[1]), int(time[2]))
        basedate = datetime.date(int(date[0]), int(date[1]), int(date[2]))

        if zone == 'UTC':
            #時差修正
            jst_date = datetime.datetime.combine(basedate, basetime) + datetime.timedelta(hours=9)
        else:
            print("UTC以外のタイムゾーンです")

        #Exif用に型変更
        time_jp = jst_date.strftime("%H:%M:%S")
        date_jp = jst_date.strftime('%Y:%m:%d')

        #Exif情報の変更
        exif_dict['0th'][piexif.ImageIFD.DateTime] = date_jp + " " + time_jp
        exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = date_jp + " " + time_jp
        exif_dict['Exif'][piexif.ExifIFD.DateTimeDigitized] = date_jp + " " + time_jp
        exif_bytes = piexif.dump(exif_dict)

        #Exifの変更反映
        piexif.insert(exif_bytes, new_path + filename)

        print("mod exif : " + filename)
```

以上です．
本当は，ここからJSONの名前がhogehoge.jpg + .json ではない場合があったり，ファイル名に日本語混ざってたりと苦労したんですが，そのあたりは環境によりますし割愛で
お疲れ様でした～