THETA Sのサンプルの静止画像(.jpg)のメタデータをパースしてみた

来月下旬(Amazonでは10/30販売となっている)、THETAの新機種であるTHETA Sが販売されます。
そこで、サンプルの動画は公開されていませんが、サンプルの静止画像はアプリで取得することができますのでサンプルの静止画像のメタデータをパースしてみました。

#記録されているセグメント構成
##使用されているマーカセグメント
JPEGファイルは、マーカセグメントと呼ばれる単位で構成されています。
マーカセグメントは単にセグメントと呼ばれるほうが多いようです。
文中もまた、以降、マーカセグメントを単にセグメントと呼ぶことにします。
サンプル画像で使用されているセグメントを表にまとめてみます。

| マーカ名<br>(*は必須のマーカセグメント)| マーカ値   |説明|
|:--------------------------------------|:----------------------:|:----------------------|
| *SOI<br>(Start of Image)              | FFD8 | JPEGファイルの先頭に必ず配置されるマーカ。<br>このSOIと最後のEOIはマーカのみの構成となっている。(2バイト) |
| *APP1<br>(Application Segment 1)      | FFE1 | このセグメントは２つ記録される。<br>一つのAPP1セグメントにはExifが、もう一つのAPP1セグメントにはXMPが記録される。 |
|  APP13<br>(Application Segment 13)    | FFED | Adobe PhotoShopの情報が記録されるセグメント。 |
| *SOF0<br>(Start of Frame)              | FFC0 | フレームセグメントの始まりに位置し、そのフレームの各種パラメータが<br>このセグメントに記録される。<br>SOFには、0～3,5～7,9～11,13～15(4,8,12はない)があり、<br>SOF0はハフマン式ベースラインDCTのフレームパラメータが記録される。 |
| *DHT<br>(Define Huffman Table)        | FFC4 | ハフマンテーブル |
| *DQT<br>(Define Quantization Table)   | FFDB | 量子化テーブル |
|  DRI<br>(Define Restart Interval)     | FFDD | リスタートインターバル |
| *SOS(RSTn)<br>(Start of Scan)               | FFDA | このセグメントの直後にスキャンデータ(画像データ)が記録される。また、DRIが記録されているため、スキャンデータ中にはRSTn(nは0～7)のセグメントも記録される。 |
| *EOI<br>(End of Image)                | FFD9 | JPEGファイルの最後に必ず配置されるマーカ。 |


##THETA Sのセグメント構成(記録順)
ここで、サンプル画像に記録されているセグメントを記録順に並べてみます。

| セグメント(記録順) |
|:--------------:|
| SOI        |
| APP1(Exif) |
| APP1(XMP)  |
| APP13      |
| APP13      |
| SOF        |
| DHT        |
| DHT        |
| DHT        |
| DHT        |
| DQT        |
| DQT        |
| DRI        |
| SOS        |
| EOI        |

すると、APP1やAPP13、またDHTやDQTが複数記録されていることがわかります。
ただ、ここで問題があります。
JEITAのExif 2.3 規格書において、
APP1やAPP13などのAPPnのセグメントについては、
>(APPn) このマーカの記載は任意であり、必要に応じて複数個記録することが可能である。(APP1, APP2を含む)
http://www.jeita.or.jp/japanese/standard/book/CP-3451C_J/#page=25

となっており、APPnに関しては複数記録してもよいとされていますが、

DQTやDHTのセグメントについては、
>・量子化テーブルは、すべてを一つのDQTマーカセグメント内に記録しなければならない(DQTマーカを複数記録してはならない)。
http://www.jeita.or.jp/japanese/standard/book/CP-3451C_J/#page=89  
>・ハフマンテーブルは、すべてを一つのDHTマーカセグメント内に記録しなければならない(DHTマーカを複数記録してはならない)。
http://www.jeita.or.jp/japanese/standard/book/CP-3451C_J/#page=90

と書かれておりますので、Exif 2.3の規格には沿っていないと思われます。
よって、JPEGのメタデータを取得するアプリなどで画像を読み込むとすべてのDQTやDHTのセグメントを読み込まないなどの問題が起きるかもしれません。


#メタデータ
続いて、サンプル画像に記録されているメタデータ(Exif, XMP, APP13)を見てみます。
##EXif
ExifはIFD(Image File Directory)という単位で構成されており、IFDには0th IFD, Exif IFD, InterOperability IFD, GPS IFD, 1st IFDの5つのIFDがあります。
Exifの仕様においては0th IFDは必須で、それ以外のIFDは任意となっています。
###0th IFD

| タグ名 | データ型 | データの個数(サイズ) | 値 |
|:----------------------------------------|--------|:--:|------------------------|
| ImageDescription<br>(タイトルまたは名前) | ASCII | 64 | "&nbsp;" (スペース63個) |
| Make<br>(カメラ本体のメーカー名)          | ASCII | 22 | "RICHO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" |
| Model<br>(カメラ本体のモデル名)           | ASCII | 64 | "RICOH THETA S&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" |
| Orientation<br>(イメージの向き) | SHORT | 1 | 1 (向き補正なし) |
| XResolution<br>(画像の幅の解像度) | RATIONAL | 1 | 72/1 (72dpi) |
| YResolution<br>(画像の高さの解像度) | RATIONAL | 1 | 72/1 (72dpi) |
| ResolutionUnit<br>(画像の幅と高さの解像度の単位) | SHORT | 1 | 2 (インチ) |
| Software<br>(ファームウェアのソフトウェア名称) | ASCII | 32 | "RICOH THETA S Ver 0.71&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" |
| DateTime<br>(作成時刻または更新時刻) | ASCII | 20 | "2015:05:19 20:48:00" |
| YCbCrPositioning<br>(YCCの画素構成[YとCの位置]) | SHORT | 1 | 1 (中心) |
| Copyright<br>(コピーライト) | ASCII | 46 | "  "(スペース45個) | 

####Exif IFD

| データ名 | データ型 | データの個数(サイズ) | 値 |
|:----------------------------------------|--------|:--:|------------------------|
| ExposureTime<br>(露光時間) | RATIONAL | 1 | 1/30秒 |
| FNumber<br>(F値) | RATIONAL | 1 | 2/1 (2.0) |
| ExposureProgram<br>(露光プログラム) | SHORT | 1 | 2 (ノーマルプログラム) |
| ISOSpeedRatings<br>(ISO感度) | SHORT | 1 | 400 |
| SensitivityType<br>(感度種別) | SHORT | 1 | 1 (標準出力感度[SOS]) |
| ExifVersion<br>(EXIFサポートバージョン) | UNDEFINED | 4 | "0230" (ver2.30) |
| DateTimeOriginal<br>(現画像データの生成日時) | ASCII | 20 | "2015:05:19 20:48:00" |
| CreateDate<br>(デジタルデータの作成日時) | ASCII | 20 | "2015:05:19 20:48:00" |
| ComponentsConfiguration<br>(各コンポーネントの意味) | UNDEFINED | 4 | 1, 2, 3, 0 (Y, Cb, Cr, _) |
| CompressedBitsPerPixel<br>(画像圧縮モード) | RATIONAL | 1 | 16/5 |
| ApertureValue<br>(絞り値) | RATIONAL | 1 | 2/1 (2) |
| BrightnessValue<br>(輝度値) | SRATIONAL | 1 | -1/10 (-0.1) |
| ExposureBiasValue<br>(露出補正値) | SRATIONAL | 1 | 0/1 (0) |
| MaxApertureValue<br>(レンズ最小F値) | RATIONAL | 1 | 2/1 (2.0) |
| MeteringMode<br>(測光方式) | SHORT | 1 | 5 (分割測光) |
| LightSource<br>(光源) | SHORT | 1 | 0 (不明) |
| Flash<br>(フラッシュ露光) | SHORT | 1 | 0 (ストロボ発行なし/ストロボのリターン検出機能なし/カメラのストロボモード不明/ストロボ機能あり/赤目軽減機能なしまたは不明) |
| FocalLength<br>(焦点距離) | RATIONAL | 1 | 131/100 (1.31mm?) |
| UserCOmment<br>(コメント) | UNDEFINED | 264 | "r=11585[2015.5.15]<nt authority.system>@ZRFG030058 " |
| FlashpixVersion<br>(対応 Fraspix バージョン) | UNDEFINED | 4 | "0100" (ver 1.0) |
| ColorSpace<br>(色空間) | SHORT | 1 | 1 (sRGB) |
| PixelXDimension<br>(実効画像幅) | LONG | 1 | 5376 |
| PixelYDimension<br>(実効画像高さ) | LONG | 1 | 2688 |
| ExposureMode<br>(露出モード) | SHORT | 1 | 0 (露出自動) |
| WhiteBalance<br>(ホワイトバランス) | SHORT | 1 | 0 (ホワイトバランス自動) |
| SceneCaptureType<br>(撮影シーンタイプ) | SHORT | 1 | 0 (標準) |
| Sharpness<br>(シャープネス) | SHORT | 1 | 0 (標準) |

####GPS IFD

| データ名 | データ型 | データの個数(サイズ) | 値 |
|:----------------------------------------|--------|:--:|------------------------|
| GPSVersionID<br>(GPS タグのバージョン) | BYTE | 4 | 2, 3, 0, 0 (ver2.3) |
| GPSImgDirectionRef<br>(撮影した画像の方向の単位) | ASCII | 2 | "T" (真方位) |
| GPSImgDirection<br>(撮影した画像の方向) | RATIONAL | 1 | 45/2 (22.5) |

###XMPデータ
Photo Sphere XMPが記録されています。
XMPはExifと同様APP1セグメントで記録されますので、前述のマーカセグメントの項目で書きましたが、ExifとXMPを両方記録する場合は、APP1セグメントが2つになります。
XMPは、XMLを拡張したものとなっており、XMLパーサーやDOMパーサー等でパースすることが可能と思います。
サンプル画像の実際に記録されているPhoto Sphere XMPをそのまま記述すると以下のようになります(インデントは追加してます)。

```xml
http://ns.adobe.com/xap/1.0/
<?xpacket begin="﻿(UTF-8 BOM 3バイト)" id="W5M0MpCehiHzreSzNTczkc9d"?>
  <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="RICOH THETA for iOS 1.0.0">
    <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description xmlns:GPano="http://ns.google.com/photos/1.0/panorama/" rdf:about="">
        <GPano:ProjectionType>equirectangular</GPano:ProjectionType>
        <GPano:UsePanoramaViewer>True</GPano:UsePanoramaViewer>
        <GPano:CroppedAreaImageWidthPixels>5376</GPano:CroppedAreaImageWidthPixels>
        <GPano:CroppedAreaImageHeightPixels>2688</GPano:CroppedAreaImageHeightPixels>
        <GPano:FullPanoWidthPixels>5376</GPano:FullPanoWidthPixels>
        <GPano:FullPanoHeightPixels>2688</GPano:FullPanoHeightPixels>
        <GPano:CroppedAreaLeftPixels>0</GPano:CroppedAreaLeftPixels>
        <GPano:CroppedAreaTopPixels>0</GPano:CroppedAreaTopPixels>
        <GPano:PoseHeadingDegrees>0.0</GPano:PoseHeadingDegrees>
        <GPano:PosePitchDegrees>0.0</GPano:PosePitchDegrees>
        <GPano:PoseRollDegrees>0.0</GPano:PoseRollDegrees>
      </rdf:Description>
    </rdf:RDF>
  </x:xmpmeta>
<?xpacket end="w"?>
```
一番最初のルートタグ?(xpacket)の外に書かれている"http&#58;//ns.adobe.com/xap/1.0/"(\0終端,計29バイト)はXMPの仕様において固定で記録しなければならないとされており、XMLパーサー等にかけるときはこの29バイトは除外してかけるようにします。
xpacketタグのbegin属性には、文字コードのBOMがバイナリーのまま記録されています。
このBOMによってXMPのエンコードおよびバイトオーダーをUTF-8,UTF-16BE,UTF-UTF-16LE,UTF-32BE,UTF-32LEの中から選択することが可能なのですが、
[XMPの仕様において](http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf#page=19)JPEGにXMPを記録する場合はUTF-8にしなければならないとなっており、実際、サンプル画像のXMPもUTF-8(のBOM)となっていました。
ちなみに、id属性の"W5M0MpCehiHzreSzNTczkc9d"は仕様において固定で、この文字列でないといけないようです。

一応、サンプル画像のXMPデータを表にまとめると以下のようになります。

| データ名             | 値 |
|:--------------------|----:|
| GPano:ProjectionType | "equirectangular" |
| GPano:UsePanoramaViewer | "True" |
| GPano:CroppedAreaImageWidthPixels | 5376 |
| GPano:CroppedAreaImageHeightPixels | 2688 |
| GPano:FullPanoWidthPixels | 5376 |
| GPano:FullPanoHeightPixels | 2688 |
| GPano:CroppedAreaLeftPixels | 0 |
| GPano:CroppedAreaTopPixels | 0 |
| GPano:PoseHeadingDegrees | 0 |
| GPano:PosePitchDegrees | 0 |
| GPano:PoseRollDegrees | 0 |

##1つ目のAPP13
APP13のセグメントは、ググってみると[PhotoShop資源データ](http://hp.vector.co.jp/authors/VA032610/JPEGFormat/marker/APP13IRB.htm)が記録されるとなっています。具体的なデータの意味は分らないため、16進数でデータを記述します。

| データID(hex)        | 説明 | 値 |
|:-------------------:|------|----|
| 0404 | IPTC-NAA record Contains the File Info... | (データなし) |
| 0425 | Caption digest 16 bytes: RSA Data Security, MD5 message-digest algorithm | D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E |

##2つ目のAPP13
１つ目と同様で、１つ目とは違うデータIDのデータが記録されているのかと思いましたが、２つ目も値は違いますが同じデータIDのデータが記録されていました。

| データID(hex)        | 説明 | 値 |
|:-------------------:|------|----|
| 0404 | IPTC-NAA record Contains the File Info... | 1C 01 5A 00 03 1B 25 47 1C 02 00 00 02 00 02 1C 02 74 00 2D 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 1C 02 78 00 3F 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 00 |
| 0425 | Caption digest 16 bytes: RSA Data Security, MD5 message-digest algorithm | 15 F2 53 FB 69 9E 2C 71 D4 FD D4 21 05 ED 4D 36 |

##最後に気になる点
###セグメント構成
やはり、前述したようにセグメント構成がExif 2.3の規格には沿っていないと思われるところです。
この点は修正してほしいところです。

###GPS
THETA S のスペックを見るとGPSは搭載されていません。
APIリファレンスの [gpsInfo](https://developers.theta360.com/ja/docs/v2/api_reference/options/gps_info.html)には、"クライアントより指定可能"と書かれており、スマホのアプリから撮影したときにスマホのGPS情報ををTHETA Sに送り撮影した画像に付加するといった感じの運用になるのかなと予想します。