宇宙のQRコードに関連するデバイスの位置を推定しようとしています。私はiOS11で導入されたARKitとVisionフレームワークを使用していますが、この質問への答えはおそらくそれらに依存していません。
Visionフレームワークを使用すると、カメラフレームのQRコードの境界となる長方形を取得できます。この長方形を、QRコードを標準位置から変換するために必要なデバイスの平行移動と回転に一致させたいと思います。
たとえば、フレームを観察すると、次のようになります。
* *
B
C
A
D
* *
一方、QRコードから1m離れていて、その中心にあり、QRコードの辺が10cmであると仮定すると、次のようになります。
* *
A0 B0
D0 C0
* *
これらの2つのフレーム間のデバイス変換はどのようになっていますか?観測されたQRコードがわずかに非平面であり、完全に1つではないものでアフィン変換を推定しようとしているため、正確な結果が得られない可能性があることを理解しています。
後者は、この問題について私が興味を持っていないARKitから推測された変換をすでに考慮してsceneView.pointOfView?.camera?.projectionTransform
いるsceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrix
ので、よりも役立つと思います。
どのように記入しますか
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コードコーナーです。
予測された原点は、電話が回転しても所定の位置に留まりますが、本来あるべき位置からずれています。驚いたことに、観測値をシフトすると、これを修正できます。
// (imageSize.height * (1 - $0.y), imageSize.width * $0.x)
// replaced by:
(imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))
そして今、予測された起源はしっかりと定位置にとどまります。しかし、シフト値がどこから来ているのかわかりません。
最後に、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コードをまっすぐ見ると向きは問題ありませんが、電話の回転に関連しているように見えるものによってずれます。
私が持っている未解決の質問は次のとおりです。
- 回転を解決するにはどうすればよいですか?
- 位置シフト値はどこから来ますか?
- 回転、平行移動、QRCornerCoordinatesInQRRef、観測、内因性はどのような単純な関係を検証しますか?O〜K ^ -1 *(R_3x2 | T)Qですか?もしそうなら、それは数桁ずれているからです。
それが役立つ場合は、ここにいくつかの数値があります。
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コードの向きに関係なく:
他の回転は機能しません。