2017-07-06 9 views
8

私はARKit(SceneKit付き)を使って仮想オブジェクト(ボールなど)を追加しています。ビジョンフレームワークを使用し、ビジョン要求完了ハンドラメソッドでその更新された位置を受け取ることによって、実世界のオブジェクト(例:足)を追跡しています。ビジョンフレームワークの座標系をARKitに変換する方法は?

let request = VNTrackObjectRequest(detectedObjectObservation: lastObservation, completionHandler: self.handleVisionRequestUpdate) 

私は仮想で追跡現実世界のオブジェクトを置換したい(例えば立方体で足を置き換え)が、私はシーンキットノードに(私たちはビジョン要求の完了に受信)このBoundingBox RECTを交換する方法がわからないです座標系として異なる。以下

ビジョン要求の完了ハンドラのコードです:

private func handleVisionRequestUpdate(_ request: VNRequest, error: Error?) { 
    // Dispatch to the main queue because we are touching non-atomic, non-thread safe properties of the view controller 
    DispatchQueue.main.async { 
     // make sure we have an actual result 
     guard let newObservation = request.results?.first as? VNDetectedObjectObservation else { return } 

     // prepare for next loop 
     self.lastObservation = newObservation 

     // check the confidence level before updating the UI 
     guard newObservation.confidence >= 0.3 else { 
     return 
     } 

     // calculate view rect 
     var transformedRect = newObservation.boundingBox 

    //How to convert transformedRect into AR Coordinate 
    self.node.position = SCNVector3Make(?.worldTransform.columns.3.x, 
    ?.worldTransform.columns.3.y, 

    } 
    } 

座標系を転送するために私を導いてください。

答えて

3

主な考慮点は、境界矩形が2D画像にあり、ARKitのシーンが3Dであることです。これは、深さを選択するまで、境界矩形が3Dのどこにあるかは定義されていません。

何をすべきことは、2Dから取得するシーンに対するヒットテストを実行しているが、3Dに座標:

let box = newObservation.boundingBox 
let rectCenter = CGPoint(x: box.midX, y: box.midY) 
let hitTestResults = sceneView.hitTest(rectCenter, types: [.existingPlaneUsingExtent, .featurePoint]) 
// Pick the hitTestResult you need (nearest?), get position via worldTransform 
+1

これは実際に動作しますか?ビジョン観測によって返されるboundingBoxの起点とサイズは、uikit座標ではなく[0,1]の範囲にあり、hitTestはuikit座標が必要です –

5

長方形を想定すると、水平面上にある、あなたは上のシーンに対するヒットテストを実行することができますすべての4つのコーナーを使用し、それらのコーナーの3つを使用して、長方形の幅、高さ、中心、および向きを計算します。

Iは正確にないGitHubの上で利用可能なデモアプリケーションを有する: https://github.com/mludowise/ARKitRectangleDetection

VNRectangleObservationから矩形の角の座標が、携帯電話の回転に応じて画像のサイズ及び異なる座標における相対的であろうが。あなたは、ビューのサイズによってそれらを乗算し、携帯電話の回転に基づいてそれらを反転必要があります

func convertFromCamera(_ point: CGPoint, view sceneView: ARSCNView) -> CGPoint { 
    let orientation = UIApplication.shared.statusBarOrientation 

    switch orientation { 
    case .portrait, .unknown: 
     return CGPoint(x: point.y * sceneView.frame.width, y: point.x * sceneView.frame.height) 
    case .landscapeLeft: 
     return CGPoint(x: (1 - point.x) * sceneView.frame.width, y: point.y * sceneView.frame.height) 
    case .landscapeRight: 
     return CGPoint(x: point.x * sceneView.frame.width, y: (1 - point.y) * sceneView.frame.height) 
    case .portraitUpsideDown: 
     return CGPoint(x: (1 - point.y) * sceneView.frame.width, y: (1 - point.x) * sceneView.frame.height) 
    } 
} 

その後、あなたはすべての4頭の隅にヒットテストを実行することができます。ヒットテストを実行するときは、タイプ.existingPlaneUsingExtentを使用して、ARKitが水平面に対してヒットを返すようにすることが重要です。

let tl = sceneView.hitTest(convertFromCamera(rectangle.topLeft, view: sceneView), types: .existingPlaneUsingExtent) 
let tr = sceneView.hitTest(convertFromCamera(rectangle.topRight, view: sceneView), types: .existingPlaneUsingExtent) 
let bl = sceneView.hitTest(convertFromCamera(rectangle.bottomLeft, view: sceneView), types: .existingPlaneUsingExtent) 
let br = sceneView.hitTest(convertFromCamera(rectangle.bottomRight, view: sceneView), types: .existingPlaneUsingExtent) 

そして、それは、各ヒットテストは、0からnまでの結果を返す可能性があるため、あなたは別の平面上に含まれている任意のヒットテストをフィルタリングする必要があります

...少し複雑になります。あなたは、各ARHitTestResultのためのアンカーを比較することによって、これを行うことができます。

hit1.anchor == hit2.anchor 

また、一つのコーナーはいずれも返さない場合、それは大丈夫ですので、あなただけの長方形の大きさ、位置、および方向を識別するために、4つの隅のうちの3を必要としますテスト結果をヒットします。私がどのようにしたかについては、hereを見てください。

左隅と右隅の間の距離(上端または下端のいずれか)から長方形の幅を計算できます。同様に、上部の&の下部コーナー(左または右のいずれか)の間の距離から矩形の高さを計算することもできます。

func distance(_ a: SCNVector3, from b: SCNVector3) -> CGFloat { 
    let deltaX = a.x - b.x 
    let deltaY = a.y - b.y 
    let deltaZ = a.z - b.z 

    return CGFloat(sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ)) 
} 

let width = distance(right, from: left) 
let height = distance(top, from: bottom) 

あなたは長方形の対角(TOPLEFT & bottomRight又はtopRight & bottomLeftいずれか)から中間点を取得することにより、その位置を計算することができる:

let midX = (c1.x + c2.x)/2 
let midY = (c1.y + c2.y)/2 
let midZ = (c1.z + c2.z)/2 
let center = SCNVector3Make(midX, midY, midZ) 

あなたはまた、矩形の向きを算出することができます(y軸に沿った回転)を左右のコーナー(上または下のいずれかの方向)から選択することができる。

let distX = right.x - left.x 
let distZ = right.z - left.z 
let orientation = -atan(distZ/distX) 

それをまとめて、AR内に何かを矩形の上に重ねて表示します。 SCNNodeをサブクラス化して仮想矩形を表示する例を示します。

class RectangleNode: SCNNode { 

    init(center: SCNVector3, width: CGFloat, height: CGFloat, orientation: Float) { 
     super.init() 

     // Create the 3D plane geometry with the dimensions calculated from corners 
     let planeGeometry = SCNPlane(width: width, height: height) 
     let rectNode = SCNNode(geometry: planeGeometry) 

     // Planes in SceneKit are vertical by default so we need to rotate 
     // 90 degrees to match planes in ARKit 
     var transform = SCNMatrix4MakeRotation(-Float.pi/2.0, 1.0, 0.0, 0.0) 

     // Set rotation to the corner of the rectangle 
     transform = SCNMatrix4Rotate(transform, orientation, 0, 1, 0) 

     rectNode.transform = transform 

     // We add the new node to ourself since we inherited from SCNNode 
     self.addChildNode(rectNode) 

     // Set position to the center of rectangle 
     self.position = center 
    } 
} 
関連する問題