Я пытаюсь оценить положение своего устройства по QR-коду в космосе. Я использую ARKit и платформу Vision, представленные в iOS11, но ответ на этот вопрос, вероятно, не зависит от них.
С помощью инфраструктуры Vision я могу получить прямоугольник, ограничивающий QR-код в кадре камеры. Я хотел бы сопоставить этот прямоугольник с перемещением и вращением устройства, необходимым для преобразования QR-кода из стандартного положения.
Например, если я наблюдаю за рамкой:
* *
B
C
A
D
* *
в то время как, если бы я находился на расстоянии 1 м от QR-кода, по центру и предполагая, что QR-код имеет сторону 10 см, я бы увидел:
* *
A0 B0
D0 C0
* *
Каково было преобразование моего устройства между этими двумя кадрами? Я понимаю, что точный результат может быть невозможен, потому что, возможно, наблюдаемый QR-код немного не плоский, и мы пытаемся оценить аффинное преобразование для чего-то, что не является идеальным.
Я думаю, что 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-кода в том порядке, в котором они передаются программе.
Предполагаемая исходная точка остается на месте при вращении телефона, но смещается от того места, где должна быть. Удивительно, но если я сдвинул значения наблюдений, я смогу исправить это:
// (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-кода:
Другое вращение не работает.