piexifで画像にジオタグを付与する　～恐縮だが、指定する緯度経度で写真を撮ってくれないか～

# はじめに
恐縮ですが前の記事からお読みください。
　　[PillowでExif情報を遊ぶ　～辞書の復習を兼ねて～](https://qiita.com/mo256man/items/b4567b0e0945b99f9cff)

# piexifを使う

`piexif`により、Exifの規格にのっとった非常に多くの情報を得ることができる。
　　[公式](https://pypi.org/project/piexif/)
辞書の構造がPILと違っているので注意。

```Python:ソース6
from PIL import Image, ExifTags
import piexif
from pprint import pprint

filename = "hoge.jpg"
img = Image.open(filename)
exif_dict = piexif.load(img.info["exif"])

pprint(exif_dict)
```

```Python:結果6
{'0th': {256: 3264,
         257: 2448,
         以下略},
 '1st': {256: 512,
         257: 384,
         以下略},
 'Exif': {33434: (1122000, 1000000000),
          33437: (220, 100),
         以下略},
 'GPS': {0: (2, 2, 0, 0),
         1: b'N',
         2: ((35, 1), (25, 1), (21098327, 1000000)),
         以下略},
 以下略}
```
もちろんタグ番号でなくタグ名称で引っ張り出すこともできる。
以下は公式のサンプルコードを少しだけ改造したもの。

```Python:ソース7
for ifd in ("0th", "Exif", "GPS", "1st"):
    for tag in exif_dict[ifd]:
        print(ifd, tag, piexif.TAGS[ifd][tag]["name"], exif_dict[ifd][tag])
```
```Python:結果7
0th 256 ImageWidth 3264
0th 257 ImageLength 2448
中略
Exif 33434 ExposureTime (1122000, 1000000000)
Exif 33437 FNumber (220, 100)
中略
GPS 0 GPSVersionID (2, 2, 0, 0)
GPS 1 GPSLatitudeRef b'N'
GPS 2 GPSLatitude ((35, 1), (25, 1), (21098327, 1000000))
後略
```

### 辞書を逆引きする
これまで人間にとって理解しやすいようタグ名称の辞書を使ったが、内容を変更する際はやはり元のタグ番号で指定する必要がある。
名称から番号に戻すにはキーと値を逆転させた辞書を作るかループを回して調べるかするしかない。
キーと値を逆転させた辞書は、前回挙げた内包表記で1行で書くことができる。
もちろん辞書の値が辞書だったりタプルだったりするとひっくり返してキーにすることはできないので気を付ける必要がある。

```Python:ソース8
# 基本形
# dic_swap = {value: key for key, value in dic.items()}

dic_swap_0th = {piexif.TAGS["0th"][tag]["name"]: tag for tag in exif_dict["0th"].keys()}
dic_swap_gps = {piexif.TAGS["GPS"][tag]["name"]: tag for tag in exif_dict["GPS"].keys()}

print (dic_swap_0th["ImageWidth"])
print (dic_swap_gps["GPSLatitude"])
```
```Python:結果8
256  # dic_swap_0th["ImageWidth"]
2    # dic_swap_gps["GPSLatitude"]
```

とはいえ、`piexif`はタグ名称が（辞書ではなく）クラス化されているので、実際のところ逆引きの辞書を作る必要はない。辞書の勉強として作っただけだ。

```Python:ソース9
print (piexif.ImageIFD.ImageWidth)
print (piexif.GPSIFD.GPSLatitude)
```
```Python:結果9
256  # piexif.ImageIFD.ImageWidth
2    # piexif.GPSIFD.GPSLatitude
```

では実際に書き換えてみる。

```Python:change_exif.py
from PIL import Image, ExifTags
import piexif

filename = "hoge.jpg"
img = Image.open(filename)
exif_dict = piexif.load(img.info["exif"])
exif_dict["0th"][piexif.ImageIFD.Make] = b"Appoooh"
exif_dict["0th"][piexif.ImageIFD.Model] = b"xPhone champion professional special edition"

exif_bytes = piexif.dump(exif_dict)
img.save("_" + filename, exif=exif_bytes)
```

このプログラムを実行後、あらためて前回のプログラム（完成形がなくて恐縮ですけど）でExif情報を確認すると、ちゃんとメーカーと機種名が更新されていた。HuaweiのANE-LX2J（P20 lite）からAppoooh社のxPhone champion professional special editionに。

```Python:変更前
{前略
 'Make': 'HUAWEI',
 'Model': 'ANE-LX2J',
 後略}
```

```Python:変更後
{前略
 'Make': 'Appoooh',
 'Model': 'xPhone champion professional special edition',
 後略}
```

# 応用

Exif情報を持たないCGにExif情報を書き込んでみよう。
辞書の指定の仕方が上のコードと違っているが、どちらでも可だ。
緯度経度は**3つのRATIONAL**で指定しよう。Pillowではないので小数は不可だ。

```Python:make_exif.py
import piexif

filename = "rapture.jpg"
exif_dict = {}

oth = {piexif.ImageIFD.Make: b"Fontaine Futuristics",
       piexif.ImageIFD.Model: b"Research Camera"}

gps = {piexif.GPSIFD.GPSLatitudeRef: b"N",
       piexif.GPSIFD.GPSLatitude: ((63, 1), (2, 1), (0, 1)),
       piexif.GPSIFD.GPSLongitudeRef: b"W",
       piexif.GPSIFD.GPSLongitude:((29, 1), (55, 1), (0, 1))}

exif_dict["0th"] = oth
exif_dict["GPS"] = gps

exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, filename)  # insertでExif情報を挿入するにあたりPILは不要
```

これがExif情報が追加された写真（？）だ。
![rapture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/b7251c02-29f2-d5a0-f425-b51fd2c5c1d9.jpeg)
追加されたExif情報をテキストで見ても面白くないので、[EXIF確認君](http://exif-check.org/)の結果をお見せしよう。
![rapture_property.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520216/bc48566c-cac3-21f3-a48b-b4f84052c1af.png)
すごいぞ！ ラプチャーは本当に63°2'N, 29°55'Wにあったんだ！
この写真のExif情報を見てみろよ、ちゃんと緯度経度が記録されているだろ。え？ カメラ？ フォンテイン未来技術社のリサーチカメラですが何か？
…ということができてしまうので、一般論としてデジタル記録は証拠能力が低いとされているわけですね。

# ギャラリー

 - キングコングがエンパイアステートビルに登っている場面
 - アサシンが世界的名所に立っている場面
 - 実在する土地や施設が登場するアニメ画像（定義的に「聖地」の逆）

などにジオタグを付与することを思いついたが、[以前の記事](https://qiita.com/mo256man/items/27d0a44071aee5f06933)とは違い画像としては変化がなく面白みに欠けるのでやめた。

# 終わりに
緯度経度入りの写真をアップして家バレ、ということのないように、イマドキのSNSは写真からExif情報を削除しているらしい。これも時代か。