豪鬼メモ

一瞬千撃

Google Photosに圧縮TIFFを保存する際の投機的なサイズ設定

Google Photosに16ビットTIFFを保存するワークフローを確立したのだが、その際の画像サイズの設定について考えてみた。ZIP圧縮を考慮すると、ちょっと大きめの画像を保存できる。

f:id:fridaynight:20170125002916j:plain


以前の記事のおさらいになるが、Google Photosでは各々のファイルサイズが50MBかつ総ピクセル数が1600万までの画像データを保存できる。おそらくこの二つの値は、各画素が8ビット=1バイトであるTIFFを想定して、16,000,000 × 3 = 48,000,000 であるところに呼応がある。

その制限内で、各アスペクト比で最大の画像サイズを知りたい。しかも、長辺も短辺も切りのよい長さがよい。切りが良いと言うには、最低限、各辺の長さが12の倍数であることが求められる。2分割しても3分割しても4分割しても6分割しても割り切れるからだ。縮小画像を作る際に割り切れるサイズを指定した方が画質が劣化しにくい。さらに、覚えやすさという意味では、100か50でも割りきれて欲しい。以前の記事でも同じような計算はしたが、今回は100での切りの良さよりも面積の最大化を優先した。また、16:9は切りのいい数字になりにくいので、2で割り切れればいいことにした。

8ビットTIFFを想定して1600万ピクセル以下に抑えるという条件であれば、アスペクトごとのサイズは以下のようになる。

長辺 短辺 ピクセル
1:1 4000 4000 16000000
4:3 4600 3450 15870000
3:2 4800 3200 15360000
16:9 5184 2916 15116544
2:1 5600 2800 15680000
3:1 6900 2300 15870000
4:1 8000 2000 16000000
5:1 8900 1780 15842000
6:1 9600 1600 15360000

一方で、私は、今後のディスプレイの進化を見越して、ProPhoto RGB色域の16ビットTIFFを保存したい。そうなると、逆算して、50MB ÷ 2バイト ÷ 3チャンネル - 約4KBヘッダ = 最大8,734,037画素のファイルをアップロードするのが確実である。その制限内で最大を攻めるとこんな感じ。

長辺 短辺 ピクセル
1:1 2950 2950 8702500
4:3 3400 2550 8670000
3:2 3600 2400 8640000
16:9 3840 2160 8294400
2:1 4100 2050 8405000
3:1 5100 1700 8670000
4:1 5800 1450 8410000
5:1 6600 1320 8712000
6:1 7200 1200 8640000

さらに、TIFF画像は内部データをZIP圧縮できるので、その圧縮率の95パーセンタイルを89%ぐらいとしよう。これらの数値は単なる経験則でかなりいい加減な設定だが、まあこのまま進める。そうすると、8,734,037画素 ÷ 0.89 で、9,813,524画素だ。その制限内で最大を攻めるとこんな感じ。

長辺 短辺 ピクセル
1:1 3100 3100 9610000
4:3 3600 2700 9720000
3:2 3750 2500 9375000
16:9 4032 2268 9144576
2:1 4400 2200 9680000
3:1 5400 1800 9720000
4:1 6200 1550 9610000
5:1 7000 1400 9800000
6:1 7500 1250 9375000

これらの表はこんな富豪的スクリプトで出力した。総当たりせずにもうちょいマシなアルゴリズムがあるだろうけども。

#! /usr/bin/ruby

C_confs = [
[1, 1, true],
[4, 3, true],
[3, 2, true],
[16, 9, false],
[2, 1, true],
[3, 1, true],
[4, 1, true],
[5, 1, true],
[6, 1, true],
]
C_areas = [
16000000,
8734037,
9813524,
]
C_step = 50

C_areas.each { |area|
  printf("Max Area = %d\n", area)
  C_confs.each { |conf|
    long_factor, short_factor, no_remain = conf
    long = area / C_step * C_step
    while true
      if no_remain
        long -= C_step
      else
        long -= 1
    end
      short = long.to_f / long_factor * short_factor
      if short != short.to_i
        next
      end
      if no_remain
        if long % 2 > 0 or short % 2 > 0
          next
        end
      else
        if long % 12 > 0 or short % 12 > 0
          next
        end
      end
      if long * short > area
        next
      end
      printf("|%d:%d|%d|%d|%d\n",
             long_factor, short_factor, long, short, long * short)
      break
    end
  }
}

結論としては、基本4:3アスペクトであるところのM43センサーを使っている私としては、Lightroomからデータをエクスポートする際に、長辺3600に設定するといいということになる。撮影時に3:2を設定した例では長辺3750に、16:9なら長辺4032に、1:1なら3100にだ。ただし、16:9に関しては、近い値の規格として3840x2160=8294400の4K UHD(=2160p=QFHD)という規格があるので、それに合わせておいた方がよさげ。

実際に4:3の長辺3600でZIP圧縮16ビットTIFFを書き出してみたところ、64ファイル中で50MB以上になったのは2ファイルだった。それらはそのままだとGoogle Photosに保存できないので、4:3なら長辺3400以下にしてエクスポートし直す必要がある。まあそんなのを手作業でやるのはさすがにだるいので、適当なスクリプトを書いて自動化することになるだろう。

私の場合、エクスポートした画像は12ビットのJPEG2000に変換して保存するという変態的なことをやっているので、それをGoogle Photosに保存するための一括変換スクリプトにて、上記のサイズを指定することにした。JPEG2000は95パーセンタイルのサイズで保存しつつ、ZIP圧縮16ビットTIFFに変換した際に50MBを超えたならImageMagickで再圧縮をかけている。実際のところ、12ビットJPEG2000エントロピーは直接出力した16ビットTIFFのそれよりもかなり低いはずで、このスクリプトで再圧縮がかかるケースは見たことがない。実際、中央値は32MBくらいで、最大でも42MBくらいだ。よって、再圧縮による品質劣化は無視できると言える。再圧縮処理は単なる保険で実装しただけだ。だったら3800とかもいけるんじゃないかとは思うわけだが、バリエーションがありすぎるのはあんまり美しくない。

これが、私が私の写真データを最高品質でGoogle Photosに保存する最適解だ。長辺3400を長辺3600に伸ばすためにここまでの努力をする実用的な意味はあるのか。いや、ない。しかし、私は3600って数字が好きだ。それを長さ半分に縮小した長辺1800の画像は、Macbook Proで閲覧するのにちょうどいい大きさだ。
f:id:fridaynight:20170124225224j:plain


余談。先日、量販店にてE-M1マーク2をいじり倒してきた。画素数が2000万画素(5184×3888)に増えたのがまず喜ばしい。この記事で述べたように3600×2700に縮小して保存するにしても、オーバーサンプリングするデータは多いほど結果の質はよくなる。それでいて、DxOMarkのスコアもすこぶる良く、ダイナミックレンジも良くなっている。そして、実際に触ってみたら、AFが爆速で驚いた。AF-Cにすると像面位相差だけでAFが完結するらしいのだが、本当に鬼のように早い。動作した刹那には既にピントが合っている。こりゃ欲しくなるわ。M.Zuiko 12-100mm F4も評判いいみたいだし、それと合わせるとシンクロ手ブレ補正で6.5段とかもうすごすぎる。そういや、私のE-M10ちゃんは図らずも今壊れちゃってるなぁ。ジュル。