背景
前回の投稿でARKitとVisionを使ってARKitの入力から四角形検知を行う方法を紹介したが、次の問題点があった。
- iPadの横画面ではうまく四角形を検知(重ねて表示)できるが、タブレットの縦画面では四角形がズレてしまう
- iPhoneでは縦横両方とも四角形がズレてしまう
長らく原因調査を放置していたが、原因が分かったので注意点とサンプルコードを公開する。
理由
- ARFrameのcapturedImageをVNImageRequestHandlerで処理する際に画像のorientationを指定していなかった
- ARFrameのcapturedImageを任意のViewに表示するためのAffine変換を取得するdisplayTransform()を使わず、自前でAspectを計算していた
capturedImageはデバイスの向きに関わらず、Landscape
ARFrameより取得できるcapturedImageはデバイスの向きに関わらず、Landscapeになる。VNImageRequestHandler(cvPixelBuffer: CVPixelBuffer, orientation: CGImagePropertyOrientation, options: [VNImageOption : Any] = [:])
で入力画像のorientationを指定できるが、これを正しく指定を行なわないと後の処理で四角形がズレてしまう。capturedImageはY座標が反転しているようで、.donwMirroredを指定することでズレが解消された。
Affine変換は自前で計算せずに、displayTransform()を使う
動作させるデバイスによって大きさは変わるが、大抵画面上のARSCNViewの縦横の大きさはcapturedImageの大きさと一致しない。さらに都合が悪いことに大抵縦横のアスペクト比も異なる。そのためcapturedImage上で検知した四角形をARSCNView上に描画するにはAffine変換を行う必要がある。iPadの縦画面だけであれば、なんとか計算して合わせることができたが、横画面やiPhoneのように縦横の比率が変わると条件が複雑になり、自前で計算することはほぼ不可能である。(あくまでも私の個人的な意見だが)そのようなときに備えてなのかdisplayTransform()というメソッドが用意されている。これを使えば任意のサイズのCGRectに合わせたAffine変換を自動で生成してくれる。そのタイミングの画面のorientationを指定してやる必要があるが、適切な引数を指定すれば、iPhoneでもiPadでもどんな向きでも四角形を重ね合わせることができた。
サンプルコード
github上に改善版のソースコードを公開しましたので興味がある方はお試しください。