2022年05月08日

AR仮想分度器における操作性の工夫について

AR仮想分度器は、iOSのAR(拡張現実)技術を使って物体の表面に仮想分度器を置くことができるアプリです。
AR vProtractor
(実際の画面表示は日本語です)
水平方向の分度器は画面を横方向になぞると回転させることができ、垂直方向の分度器は縦方向になぞると傾けることができます。
2本指でなぞると空間内で前後左右に移動させることができ、ピンチイン・アウトで拡大縮小できます。
3本指で縦方向に画面をなぞると垂直方向に移動させることができます。
枠(グリッド)を表示させると机の端や部屋の端に方向を合わせるのに便利です。
分度器の位置はARアンカーという機能を使って空間上の位置を安定させていますが、他の部屋などに移動した時にARアンカーを置き直すためには「置き直す」ボタンを使います。

このアプリを作るにあたって、ARの操作性に注意を払いました。
例えば、ボタンを押す回数を最小限にする、なるべく直感的に使えるようにする、などです。
そのための技術的な工夫を以下に説明します。続きを読む
posted by MacLab. at 22:37| Comment(0) | TrackBack(0) | 技術情報

2022年02月26日

アップルのMLサンプルの updateLayerGeometry() 修正する

前回に引き続き、さらにアップルのサンプルを修正します。それは updateLayerGeometry() です。
これはiPhoneの画面の大きさに合わせてキャプチャのプレビューを配置する部分ですが、提供されているサンプルはデバイスの縦持ち(portrait)にしか対応していませんでしたので、横持ち(landscape)にも対応するようにこれを修正します。
func updateLayerGeometry() {
let bounds = rootLayer.bounds // iOS用
//横持ち用
var scale: CGFloat
let xScale: CGFloat = bounds.size.width / bufferSize.width //.height
let yScale: CGFloat = bounds.size.height / bufferSize.height //.width
scale = fmax(xScale, yScale)
if scale.isInfinite {
scale = 1.0
}
//縦持ち用
var scalePortrait: CGFloat
let xScalePortrait: CGFloat = bounds.size.width / bufferSize.height
let yScalePortrait: CGFloat = bounds.size.height / bufferSize.width
scalePortrait = fmax(xScalePortrait, yScalePortrait)
if scalePortrait.isInfinite {
scalePortrait = 1.0
}
//
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
switch UIApplication.shared.statusBarOrientation {
case .portrait:
print("updateLayerGeometry: statusBarOrientation.portrait")
detectionOverlay.bounds = CGRect(x: 0.0, y: 0.0, width: bufferSize.height, height: bufferSize.width)
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: 0.0).scaledBy(x: scalePortrait, y: -scalePortrait))
case .landscapeLeft:
print("updateLayerGeometry: statusBarOrientation.landscapeLeft")
detectionOverlay.bounds = CGRect(x: 0.0, y: 0.0, width: bufferSize.width, height: bufferSize.height)
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: 0.0).scaledBy(x: scale, y: -scale))
case .landscapeRight:
print("updateLayerGeometry: statusBarOrientation.landscapeRight")
detectionOverlay.bounds = CGRect(x: 0.0, y: 0.0, width: bufferSize.width, height: bufferSize.height)
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: 0.0).scaledBy(x: scale, y: -scale))
case .portraitUpsideDown:
print("updateLayerGeometry: statusBarOrientation.portraitUpsideDown")
detectionOverlay.bounds = CGRect(x: 0.0, y: 0.0, width: bufferSize.height, height: bufferSize.width)
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: 0.0).scaledBy(x: scalePortrait, y: -scalePortrait))
default: //break
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: scale, y: -scale))
}
// center the layer
detectionOverlay.position = CGPoint(x: bounds.midX, y: bounds.midY)
CATransaction.commit()
}

タグ:vision
posted by MacLab. at 22:36| Comment(0) | TrackBack(0) | 技術情報

2022年02月17日

アップルのMLアプリのサンプルの exifOrientationFromDeviceOrientation() は間違えているという話

機械学習のためのVisionフレームワークを使ったmacOS/iOSアプリの開発をやっています。
その中で、アップルが公開して世間で広く使われているサンプルのSwiftコードに誤りがあるのではないかということに気がつきました。
それはビデオキャプチャ、特にiPhoneのカメラを使ったアプリの場合に使われる exifOrientationFromDeviceOrientation() のコードです。
これはiPhoneの向きに合わせて推論の向きを変更するコードで、今では世界中の多くのブログや教科書に転載されていますが、これでアプリを作成すると、portrait(iPhoneを縦に持つ)に Visionの推論の精度が低くなることに気がつきました。
また、横持ちの場合でも出力される結果が反転する現象がありました。
アップルが提供するコードに誤りがあるとはついぞ思い至らなかったため、自作の部分をこねくりまわして悩むことになってしまいました。続きを読む
タグ:vision
posted by MacLab. at 22:41| Comment(0) | TrackBack(0) | 技術情報