Я намагаюся оцінити положення свого пристрою, пов'язане з 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-код прямо, але потім він змінюється на щось, що, здається, пов’язано з обертанням телефону:
Вирішені запитання у мене:
- Як вирішити обертання?
- звідки беруться значення зсуву позиції?
- Які прості відносини перевіряють обертання, переклад, QRCornerCoordinateInQRRef, спостереження, інтрики? Це 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
==== Редагувати2 ====
Я помітив, що обертання працює нормально, коли телефон залишається горизонтально паралельним QR-коду (тобто матриця обертання - [[a, 0, b], [0, 1, 0], [c, 0, d]] ), незалежно від фактичної орієнтації QR-коду:
Інші ротації не працюють.