デジカメの日付が狂っていて写真のexif辛い問題をRubyで処理


## 背景

デジカメの時計が狂っていて旅行の写真のタイムスタンプが狂っていてつらいです

2014-10-05 ごろの写真が 2014-09-14 ごろってタイムスタンプがついています><；

## 誤差の推定

時間を補正しようにもどれだけずれているかわからないと補正できないです。
ここはRuby関係なく、写真のタイムスタンプとライフログを徹底的に突き合わせて誤差を推定します。

今回の決め手になったのは宮島にむかうときのフェリーで厳島神社の大鳥居を撮った時刻と、その写真をとったときにいただろう場所のGPSログのタイムスタンプから推定しました。
フェリーというそれなりの速度で進む乗り物でかつ写真のために特徴的な軌道を描いていたのでかなり特定しやすかったです。
たぶん10秒もずれてないと思います。

foursquareのチェックイン時刻よりは参考になるはず。

```ruby
pry> camera = Time.parse("2014-09-14 19:19:54")
=> 2014-09-14 19:19:54 +0900
pry> gps = Time.parse("2014-10-05 12:38:12")
=> 2014-10-05 12:38:12 +0900
pry> gps - camera
=> 1790298.0
```

1790298秒くらいずれていたようです。

## 方針

さてこれから情報を修正するのですが、以下の2段階でやっていきます。
ファイル名だけ先に修正するのは上のずれがそれっぽいかを他のカメラの写真との前後関係を確認するためです。

1. ファイル名の修正
2. exif情報の修正

## ファイル名の修正

これは非常に簡単

```ruby
#!/usr/bin/env ruby

require "fileutils"
require "pathname"
require "time"

Pathname.glob("2014-09-*.jpg") do |file|
  date = Time.parse(file.basename(".jpg").to_s.gsub(".", ":"))
  date += 1790298
  to = date.strftime("%Y-%m-%d %H.%M.%S.jpg")

  puts "mv #{file} to #{to}"
  FileUtils.mv(file, to)
end
```

## exif情報の修正

exif情報の表示みたいなエントリーはqiitaにも多いのですが、編集までやっているのはあまりみないですね。

### 利用するgem

rubytoolboxやqiitaとかをみると、

* exifr
* mini_exiftool

あたりがポピュラーなんでしょうか。
exifrは書き込みができないっぽいので、[mini_exiftool](https://github.com/janfri/mini_exiftool)を使います。
mini_exiftoolは[exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool/)というツールのラッパーとなっています。

exiftoolはhomebrewからインストールするか、[mini_exiftool_vendored](https://github.com/wilg/mini_exiftool_vendored)を使えばツールもバンドルされています。
まぁこういうバンドル系のgemは一回限りの利用であれば使ってもいいかもしれないですね。

今回はmini_exiftoolを使いました。

```
$ brew install exiftool
$ gem install mini_exiftool
```

### 修正すべきexif情報

そもそも修正すべきexif情報はなんだろうか、exiftoolを使ってちょっと確認してみます。

```bash
$ exiftool 2014-10-05\ 12.23.51.jpg | grep 2014
File Name                       : 2014-10-05 12.23.51.jpg
File Modification Date/Time     : 2014:09:14 19:05:33+09:00
Modify Date                     : 2014:09:14 19:05:33
Date/Time Original              : 2014:09:14 19:05:33
Create Date                     : 2014:09:14 19:05:33
Ricoh Date                      : 2014:09:14 19:05:33
```

うーんよくわからないけど、`Date/Time Original`をいじればいいのかな？（ひどい）

### mini_exiftoolを使った撮影日時の取得

```ruby
require "mini_exiftool"

photo = MiniExiftool.new("2014-10-05 12.23.51.jpg")
p photo.date_time_original # => 2014-09-14 19:05:33 +0900
```

timeクラスのインスタンスの入出力でよさそう。

### mini_exiftoolを使った撮影日時の更新

timeクラスなんで、さっきと同じdiffを単純に足すだけでもよいんだけど、ファイル名を正としてexif情報を更新するような感じにしたてました。冪等になるので。

```ruby
#!/usr/bin/env ruby

require "mini_exiftool"

require "pathname"
require "time"

Pathname.glob("*.jpg") do |file|
  date = Time.parse(file.basename(".jpg").to_s.gsub(".", ":"))

  photo = MiniExiftool.new(file.to_s, ignore_minor_erros: true)

  next if photo.date_time_original == date

  photo.date_time_original = date

  begin
    photo.save!
    puts "update succeeded for #{file}"
  rescue => e
    puts e
  end
end
```

で、これを実行してみると、なんかエラーで保存できなくなるのがたまに出てきます。そういうファイルは何度リトライしてもだめ。。こんな感じ。。

```
MiniExiftool couldn't save. The following errors occurred: (datetimeoriginal) Warning: [minor] Suspicious MakerNotes offset for ManufactureDate1 - /var/folders/13/phlbpvq513jftjmklvqb09rm0000gn/T/mini_exiftool20150805-41691-idw6nz
Error: [minor] Bad MakerNotes offset for Ricoh_Subdir_0x0021 - /var/folders/13/phlbpvq513jftjmklvqb09rm0000gn/T/mini_exiftool20150805-41691-idw6nz
```

うーんexiftoolが対応しきれてない部分なんかな？それともpicasaがなんかやらかした感じなのかな。。 http://www.sno.phy.queensu.ca/~phil/exiftool/faq.html#Q15

```bash
$ exiftool -a 2014-10-05\ 12.38.23.jpg | grep Bad
Warning                         : Bad RicohSubdir offset for tag 0x0021
Warning                         : Bad RicohSubdir offset for tag 0x0023
Warning                         : Bad RicohSubdir offset for tag 0x0026
```

個人的にはカメラ情報なんてどうでもいいので、削除しますか。。

## MakerNotesの削除

コマンドライン的には、

```bash
$ exiftool -MakerNotes= 2014-10-05\ 12.38.23.jpg
```

なんだが、たくさんあるので、例外発生したらMakerNotesも削除して保存するようなコードにします。さっきのrescue以下をこう書き換えます。


```ruby
  rescue
    begin
      photo.revert
      photo.maker_notes = nil
      photo.date_time_original = date
      photo.save!
      puts "update succeeded for #{file}"
    rescue => e
      puts e
    end
  end
```

一見、 `photo.maker_notes = nil`だけで良さそうなのですが、mini_exiftoolは、変更する値ごとにexiftoolを呼び出して、すべての実行が成功した場合のみOKとするような実装になっています。したがって、一旦revertして、変更する値を空にしておいてから初手`photo.maker_notes = nil`じゃないとだめというわけです。

いやーここはまった。。