全天球イラストを“360度画像”と識別させるJPEGセグメント(APP1-XMP)の埋め込み方

# はじめに
FacebookやGoogleフォトなど、一部のSNSやWebサービスは、360度画像(全天球画像)対応しており、[THETA](https://theta360.com/ja/)などで撮影した全天球写真をアップロードすると自動的に360度VRビューで表示されます。

しかし、全天球イラストを描いてアップロードしただけでは“360度画像”と認識せずに、フツーの画像として表示されてしまいます。(下図はFacebook投稿して、フツーの画像として表示されてしまった例)

![360度画像と認識しない例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448922/e07dc039-daf4-ecba-7d9f-7b43c25fe53b.png)

こうなってしまう原因として、JPEG画像内に**“360度画像”と認識させるための情報(JPEGセグメント)が埋め込まれていない**ことがあげられます。試しに、先ほどの全天球イラストに “360度画像と認識させるための情報” を埋め込んで再度アップロードすると、下記のようにちゃんとVRビューで表示できます。

![360度画像と認識する例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448922/a2fae6a7-b5a1-04cd-1b7d-0b4bdd45453e.png)

上で例示したFacebookでの投稿実験は、コチラから実際にご覧頂けます。(https://www.facebook.com/mana544/posts/1941667462592741)

ここでは、**“360度画像”と認識させるためのJPEGセグメントの埋め込み方**について解説をします。

# JPEGセグメントとは
一般的なJPEGの構造とセグメントの説明は、コチラのサイトが詳しいです。(めっちゃくちゃお世話になりました)
https://hp.vector.co.jp/authors/VA032610/JPEGFormat/StructureOfJPEG.htm

すごく簡単に説明すると、JPEGセグメントとは、画像の補助的な情報を記録・保持するための領域のことで、JPEGファイルの先頭数キロバイトは、このセグメントがいくつも記録されています。
JPEGセグメントにはいくつか種類(マーカー)があり、たとえば “DQT, DHT, SOF, SOS…” などなど、色んな種類があり、このうち**“APP1セグメント”にPhoto Sphere XMP メタデータを埋め込むことで、360度画像と認識**します。この記事では便宜上、“Photo Sphere XMP メタデータを記録したAPP1セグメント”を**「APP1-XMP」**と呼ぶことにします。

# APP1セグメントについて
実は、たいていのJPEGファイルのAPP1セグメントには“Photo Sphere XMP”ではなく“Exif”と呼ばれるフォーマットが記録されています。Exifとは、撮影したデジタルカメラの各種情報を記録するためのフォーマットで、カメラの機種からはじまり、どんな設定値で撮ったか(絞り・ISO・シャッタースピードなど)、果てはGPS情報まで、いろいろと記録できます。この記事では便宜上、“Exifが記録されたAPP1セグメント”を**「APP1-Exif」**と呼ぶことにします。
で、ハナシを元に戻すと、360度画像と認識させるためには、**APP1-ExifだけではなくAPP1-XMPを埋め込む必要があります。**
最初「APP1セグメント複数埋め込んでも大丈夫なの？」と、ワタシ自身疑問に思っていましたが、最近の実装ではAPPマーカーの後に続く「Exif識別コード」```Exif \0\0``` を見てAPP1-Exifを判別しているようだし[^3]、事実THETAで撮影したJPEGファイルをバイナリ解析するとAPP1-ExifとAPP1-XMPが埋め込まれていますので[^2]、APP1セグメントが複数あっても問題ないようです。

# Photo Sphere XMPについて
[Photo Sphere XMP](https://developers.google.com/streetview/spherical-metadata?hl=ja&fbclid=IwAR37LZ9-3NHf0gHG1B78e0tBJECoz7qUS2_fdZh1ZHt_wRJ7NT7vX8kXwUg)は、Googleがストリートビュー用に提供しているXMPフォーマットです。
VRプラットフォーム(360度画像表示対応)を組み込んでいる多くのWebサービスは、このメタデータを使って“360度画像”と認識しているようです。それぞれのWebサービスがどこまでメタデータプロパティに対応しているかの仕様開示はされていないので確認のしようがありませんが、ワタシがFacebookとGoogleフォトで投稿実験した限りでは、下記の最低限のプロパティをとりあえず埋め込めば良いのではないかと。

| # | 名前 | 値(全天球イラストの場合) |
|:---:|:---|:----|
| 1 | GPano:ProjectionType | `equirectangular` 一択 |
| 2 | GPano:UsePanoramaViewer |  `True` 一択 |
| 3 | GPano:FullPanoWidthPixels |  画像の横幅[px] |
| 4 | GPano:FullPanoHeightPixels |  画像の縦幅[px] |
| 5 | GPano:CroppedAreaImageWidthPixels |  画像の横幅[px] |
| 6 | GPano:CroppedAreaImageHeightPixels |  画像の縦幅[px] |
| 7 | GPano:CroppedAreaLeftPixels |  `0` [px] |
| 8 | GPano:CroppedAreaTopPixels |  `0` [px] |
| 9 | GPano:PosePitchDegrees |  `0` [degree] |
| 10 | GPano:PoseRollDegrees |  `0` [degree] |

上記の数値は「全天球イラスト」の場合の設定値です。たとえば、[こんなイラストのように](https://www.facebook.com/mana544/photos/a.1941667459259408/2159348644157954/?type=3&theater)全天球ではなく一部分のパノラマイラストを描いた場合は、上記表の3～8のプロパティ値をイラストに合わせて適切な数値設定にすることで、部分パノラマVR表示が可能になります。[^5]
9と10のプロパティはその名が示す通り、360度画像の中心の「ピッチ角」と「ロール角」を指定します。が、全天球イラストを描くときにわざわざ水平レベルから角度を付けて描く高等テクなんてしないでしょうから、ここは基本0です。

# APP1-XMP埋め込みスクリプト
以上のネタと、バイナリをいじれる能力があれば、あとは正距円筒図法で描いた全天球イラストをJPEG書き出しした後に、バイナリエディタでJPEGセグメントを切り貼りして晴れて360度画像対応の全天球イラストが完成するのですが、バイナリを直にいじくるのがメンドいのでPythonでスクリプトを書きました。[Github上に公開](https://github.com/mana544/convert_jpg_eqrect?fbclid=IwAR0Jq3hJ_iz5vVNkEXxD4unAanallczYP7LZ5den3kc2cnd24CcT01hvpI8)していますのでよろしければご覧下さい。

# おわりに
今回解説したPhotoSphere XMP フォーマットは、いちおう360度画像情報記録のデファクトスタンダードのようですが、結局のところ、この情報を使ってどのように解釈するかといった実装のレベルは、SNS・Webサービス各社に依存します。たとえば、RICOH THERAの全天球写真アップロードサービス[^4]は、このセグメントは見ていないらしく、Equirectangularで描かれた画像を単純にVRビューに変換しているようですし(2019年12月現在)、VR系のアプリやプラグイン・フレームワークはむしろこのプロパティを解釈しているほうが少ないです。[^6]
したがって、もしかしたら将来は仕様変更が行われる可能性があることも頭の片隅に入れておく必要があります。てゆーかTwitterはやく360度画像対応してくれないかなー:expressionless:





[^2]: 自分でも手持ちのTHETAの画像をバイナリ解析して確認しましたが、以下の方の記事に解析結果がまとめられていますので、参照してください。(https://qiita.com/gtk2k/items/79c4f6a39a77860d2421?fbclid=IwAR37ZWL_Zfh4hGLxnpY1vyJ2UrGFCItX26GRLvvikcjWrwRXvJ019ms_hSs)

[^3]: https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_JPEG_files?fbclid=IwAR2nsdQbZSW-2eCjop_9oX38kIMjZmHzQwgZG-m6pmh-5lx6J8r951h0nVk

[^4]: 一例として、ワタシのアカウントを掲載します(https://theta360.com/users/242798)

[^5]: 各プロパティの意味については、PhotoShere XMPのページに詳しく説明されています。(https://developers.google.com/streetview/spherical-metadata?hl=ja&fbclid=IwAR37LZ9-3NHf0gHG1B78e0tBJECoz7qUS2_fdZh1ZHt_wRJ7NT7vX8kXwUg)

[^6]: VRアプリやプラグインなどは、正距円筒図法(Equirectanguler)形式の画像を読み込むことを大前提としているため、いちいち “フツーの画像 or 全天球画像” を解析して分類する必要がないためと推察します。