空間内のQRコードに関連するデバイスの位置を推定しようとしています。私はiOS11で導入されたARKitとVisionフレームワークを使用していますが、この質問への答えはおそらくそれらに依存していません。
Visionフレームワークを使用すると、カメラフレームでQRコードを囲む四角形を取得できます。この長方形を、QRコードを標準位置から変換するために必要なデバイスの変換と回転に一致させたいと思います。
たとえば、フレームを観察した場合:
* *
B
C
A
D
* *
一方、QRコードから1m離れていて、その中心にある場合、QRコードの横が10cmであると仮定すると、次のように表示されます。
* *
A0 B0
D0 C0
* *
これらの2つのフレーム間のデバイス変換は何ですか?観測されたQRコードはわずかに非平面であり、完全に1つではないもののアフィン変換を推定しようとしているため、正確な結果が得られない可能性があることを理解しています。
sceneView.pointOfView?.camera?.projectionTransform
はsceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrix
後者は、ARKitから推測された変換をすでに考慮しているため、この問題については私は興味がありません。
どのように記入しますか
func get transform(
qrCodeRectangle: VNBarcodeObservation,
cameraTransform: SCNMatrix4) {
// qrCodeRectangle.topLeft etc is the position in [0, 1] * [0, 1] of A0
// expected real world position of the QR code in a referential coordinate system
let a0 = SCNVector3(x: -0.05, y: 0.05, z: 1)
let b0 = SCNVector3(x: 0.05, y: 0.05, z: 1)
let c0 = SCNVector3(x: 0.05, y: -0.05, z: 1)
let d0 = SCNVector3(x: -0.05, y: -0.05, z: 1)
let A0, B0, C0, D0 = ?? // CGPoints representing position in
// camera frame for camera in 0, 0, 0 facing Z+
// then get transform from 0, 0, 0 to current position/rotation that sees
// a0, b0, c0, d0 through the camera as qrCodeRectangle
}
====編集====
多くのことを試した後、openCVプロジェクションとパースペクティブソルバーsolvePnP
を使用してカメラポーズの推定に行きました。これにより、QRコード参照でカメラポーズを表す回転と平行移動が得られます。ただし、これらの値を使用して、QRコードがカメラ空間にあるはずの逆変換に対応するオブジェクトを配置すると、不正確なシフト値が得られ、回転を機能させることができません:
// some flavor of pseudo code below
func renderer(_ sender: SCNSceneRenderer, updateAtTime time: TimeInterval) {
guard let currentFrame = sceneView.session.currentFrame, let pov = sceneView.pointOfView else { return }
let intrisics = currentFrame.camera.intrinsics
let QRCornerCoordinatesInQRRef = [(-0.05, -0.05, 0), (0.05, -0.05, 0), (-0.05, 0.05, 0), (0.05, 0.05, 0)]
// uses VNDetectBarcodesRequest to find a QR code and returns a bounding rectangle
guard let qr = findQRCode(in: currentFrame) else { return }
let imageSize = CGSize(
width: CVPixelBufferGetWidth(currentFrame.capturedImage),
height: CVPixelBufferGetHeight(currentFrame.capturedImage)
)
let observations = [
qr.bottomLeft,
qr.bottomRight,
qr.topLeft,
qr.topRight,
].map({ (imageSize.height * (1 - $0.y), imageSize.width * $0.x) })
// image and SceneKit coordinated are not the same
// replacing this by:
// (imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))
// weirdly fixes an issue, see below
let rotation, translation = openCV.solvePnP(QRCornerCoordinatesInQRRef, observations, intrisics)
// calls openCV solvePnP and get the results
let positionInCameraRef = -rotation.inverted * translation
let node = SCNNode(geometry: someGeometry)
pov.addChildNode(node)
node.position = translation
node.orientation = rotation.asQuaternion
}
出力は次のとおりです。
ここで、A、B、C、Dは、プログラムに渡される順序のQRコードコーナーです。
予測されたOriginは、電話機が回転しても所定の位置に残りますが、本来あるべき位置からずれています。驚いたことに、観測値をシフトすると、これを修正できます。
// (imageSize.height * (1 - $0.y), imageSize.width * $0.x)
// replaced by:
(imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))
そして、予測されたOriginはしっかりとそのままになります。しかし、私はシフト値がどこから来るのか理解していません。
最後に、QRコードの参照に対して相対的に固定された方向を取得しようとしました。
var n = SCNNode(geometry: redGeometry)
node.addChildNode(n)
n.position = SCNVector3(0.1, 0, 0)
n = SCNNode(geometry: blueGeometry)
node.addChildNode(n)
n.position = SCNVector3(0, 0.1, 0)
n = SCNNode(geometry: greenGeometry)
node.addChildNode(n)
n.position = SCNVector3(0, 0, 0.1)
QRコードをまっすぐ見たときの向きは問題ありませんが、電話の回転に関連すると思われる何かによってシフトします:
私が持っている未解決の質問は次のとおりです。
これが役立つ場合、いくつかの数値を次に示します。
Intrisics matrix
Mat 3x3
1090.318, 0.000, 618.661
0.000, 1090.318, 359.616
0.000, 0.000, 1.000
imageSize
1280.0, 720.0
screenSize
414.0, 736.0
==== Edit2 ====
携帯電話がQRコードと水平に平行になっている場合、回転が正常に機能することに気付きました(つまり、回転行列は[[a、0、b]、[0、1、0]、[c、0、d]] )、実際のQRコードの向きに関係なく:
他の回転は機能しません。
問題はマトリックスにないと思います。頂点の配置です。 2D画像を追跡するには、ABCD頂点を反時計回りに配置する必要があります(開始点は想像上のOriginx:0, y:0
)。 Apple VNRectangleObservation class(画像解析リクエストで検出された投影された長方形の領域に関する情報)のドキュメント)は曖昧だと思います。頂点は同じ順序で配置しました公式文書:
var bottomLeft: CGPoint
var bottomRight: CGPoint
var topLeft: CGPoint
var topRight: CGPoint
ただし、デカルト座標系で正の回転方向(Z
軸について)が発生するのと同じように配置する必要があります。
ARKit(およびSceneKitとVision)のワールド座標空間は、常にright-handed convention
(正のY
軸は上を指し、正のZ
軸は視聴者を指し、正のX
軸は視聴者の右を指します)が、セッションの構成。カメラはローカル座標空間で動作します。
任意の軸を中心とした回転方向は、正(反時計回り)および負(時計回り)です。 ARKitとVisionで追跡するには、非常に重要です。
回転の順序も理にかなっています。 SceneKitと同様に、ARKitは、コンポーネントの逆順でノードのピボットプロパティに相対的な回転を適用します。最初のroll
(約Z
軸)、次にyaw
(約Y
軸)、次にpitch
(約X
軸)。したがって、回転順序はZYX
です。
また、Nukepediaには Matrix Operations に関する役立つ投稿があります。
注:下部はl
(QRコードの長さ)、左の角度はk
、上部の角度はi
(カメラ)です。