Apple Watchのポートレート文字盤をパースする

2021/12/03に公開

ZOZOのAdvent Calendar 2021のカレンダーその1の3日目です。

はじめに

iOSDC Japan 2021で,https://github.com/banjun/WatchFaceDumper を作ったときの話をしました。 https://speakerdeck.com/banjun/codable-techniques-on-analyzing-undocumented-file-format-apple-watch-watchface-file

今回は,写真系文字盤のなかでも新しくwatchOS 8で追加されたポートレート文字盤をパースして,どんな構造か見ていきます。

「ポートレート」の文字盤

ポートレートの文字盤は,iPhone 7 Plus以降で導入されたポートレートモードで撮影したデプス付きの写真を背景に置ける文字盤です。腕に付けておくことで立体的に見えるかというとそれほどでもなく,分かりやすい効果としてはデジタルクラウンを回したときにポートレートの前景が背景から浮いて拡大されるエフェクトがあります。Appleのサポートページではヒントとして記載されていますが言葉からは全くイメージできないと思うので設定して触ってみましょう。

ヒント: Digital Crownを回すと文字盤が拡大されます。

— Apple Watchの文字盤と機能 https://support.apple.com/ja-jp/guide/watch/apde9218b440/watchos

まずは既存実装に読み込む

WatchFaceDumperに,対応していないはずのポートレート文字盤のwatchfaceファイルを読み込ませてみましょう。

ちゃんと(?)未対応でパースに失敗しますね。Codableのエラーは読みやすくはないですがエラーとして妥当な情報は記載されています。「ポートレート文字盤をパースする」セクションで後述します。

背景・前景・マスク

ポートレートで撮影した写真のHEICファイルを知っている人は,Apple Watchの文字盤にデプスを読み取る機能があると思うかもしれませんが,文字盤を作成するときにHEIC内のデプスは解釈され,ごく単純な三つの画像ファイルに分解されます。ファイル名プレフィックスはback, base, maskです。

前景とマスクはアルファチャンネル付き画像にすることでひとつに出来そうな気もします(この記事では「前景」と言ったときに「背景」対する「前景+マスク」を意味していることがあります)。

ポートレートの文字盤はデプスを持つというよりは,前景と背景の2レイヤーの重ね合わせのみを表現していると捉えても良さそうです。

背景画像のうち,前景の裏側はそもそも写真として記録されていないので,このように重ね合わせるときには写っていない裏側の部分をフェイクすることがあります。ポートレート文字盤も,周囲の画素で補完しているようです。OpenCVでいうとinpaintingのようなよくある画像処理にも見えます(下記イメージ)。

食べ物のポートレートの背景・マスクは生成されないかもしれない

ここでは三つのポートレート写真を選択して文字盤を作ったのですが,ひとつの写真のみ背景・前景・マスクが生成され,ふたつの写真については前景しか生成されませんでした。このように,ポートレートの写真によっては,backとmaskの画像ファイルが生成されず,結果としてデジタルクラウンを回しても前景の拡大ではなく全体の拡大になってしまって面白くないことがあります。どこかに裏付けがあるかは分かりませんが,おそらくはデプスそのものではなく,ピープルセグメンテーション的な処理でマスク画像を生成していると思われます。(関連かは不明ですが... https://developer.apple.com/videos/play/wwdc2019/260?time=293 など)

つまり,試すときは食べ物の写真ではなく人間のポートレート写真を使うのがよいでしょう。

ポートレート文字盤をパースする

Work in ProgressのPRをつくりました。https://github.com/banjun/WatchFaceDumper/pull/3

ポートレートの文字盤はPhotosの文字盤と比べてかなり違いがあることが分かります。

ポートレート文字盤であることの特定 (face type)

ポートレート文字盤かどうかはface typeとbundle idで判別できるようです。

  • face type: ポートレート=bundle (c.f. 写真=photos, 万華鏡=kaleidoscope)
  • bundle id: face typeがbundleのときに存在して,ポートレート=com.apple.NTKUltraCubeFaceBundle

写真のface typeは途中までは素朴なcaseが増えていったようですが,ポートレートからは,まるでなんらかのスケールする開発体制をとったかのようですね。内部的にはultra cubeと呼ばれているのだろうと推測できます。(NTKはNanoTimeKitと推測される)

スタイル

文字盤の時刻のフォントは style 1 style 2 style 3 です。

リソース

写真の文字盤の一種なのでResourcesフォルダに画像ファイルがあることは予想していましたが,その内容は写真文字盤とはかなり違います。次のセクションに記載します。

未知パラメーター

argon.k,argon.n, imageAOTBrightness, parallax... などいくつかの未知パラメーターがあり,その効果は未解析です。

パースした構造を写真文字盤と共存させる

写真の文字盤のうち既に写真(ライブフォト含む)と万華鏡のタイプの文字盤は基本構造が解析済みであり,それらの違いはある程度対処可能なものでしたが,ポートレートの場合はResourcesの中の画像ファイルについてもメタデータとなるplistについてもかなり違うものでした。単にユニファイするとほぼ全てのプロパティがOptionalであるとして構造を定義する必要があります。種類に応じた構造とマッピングを作ればよいかとも思ったのですが余りにプロパティが多くて疎で分かりづらいので,別の構造の同居としてみます。

通常の写真の場合は写真の画像のほかにライブフォトであれば動画があり,カラーエフェクトらしいanalysisが含まれています。万華鏡の場合には動画やカラーエフェクトはありませんが差異は大きくはありません。

ポートレートの写真の場合は,写真の画像であるimageURLプロパティすらなく,前述の背景・前景・マスクのそれぞれの画像があります。動画もカラーエフェクトもないかわりに,通常の写真には無かったz orderやbrightnessやparallaxのプロパティがあります。

これらを分離すれば自明な非OptionalにOptionalを使う箇所を減らせます。ビューアー兼エディターであるWatchFaceDumperをそれぞれのcaseの読み書きに対応させ,かつ1面につき画像が3枚のパターン(既にポートレートより前では,画像1枚,または画像+動画ペア,のケースは対応済み)に対応させると,任意の画像を使いポートレートの文字盤を作成できるようになるはずです。

・・・なるはずですが,このPRがオープンしていたらまだ途中までしかやっていないということです。そのうちやります。

おわりに

いかがでしたか?ポートレート文字盤のパースはできたものの,従来の写真文字盤との共存をしやすくする設計&実装と,Macアプリから合成するGUIの作成は残された課題となりました。特に,iPhoneのポートレートモードで撮影したデプスは甘いことも多いので,精度を良くしたマスク画像で置換えることができれば,より美しい文字盤を身につけることができるようになるはずです。興味ある人は試してみてください。

明日もZOZOのアドベントカレンダーをチェックしてみてくださいね。良いお年を。

Discussion