iOS cofa projekcję kamery

87

Próbuję oszacować położenie mojego urządzenia związane z kodem QR w przestrzeni. Używam ARKit i frameworka Vision, oba wprowadzone w iOS11, ale odpowiedź na to pytanie prawdopodobnie nie zależy od nich.

Dzięki frameworkowi Vision jestem w stanie uzyskać prostokąt, który ogranicza kod QR w ramce aparatu. Chciałbym dopasować ten prostokąt do translacji i obrotu urządzenia niezbędnego do przekształcenia kodu QR ze standardowej pozycji.

Na przykład, jeśli obserwuję ramkę:

*            *

    B
          C
  A
       D


*            *

podczas gdy gdybym był 1 m od kodu QR, wyśrodkowany na nim i zakładając, że kod QR ma bok 10 cm, zobaczyłbym:

*            *


    A0  B0

    D0  C0


*            *

jaka była transformacja mojego urządzenia między tymi dwiema ramkami? Rozumiem, że dokładny wynik może nie być możliwy, ponieważ może obserwowany kod QR jest nieco nieplanarny i próbujemy oszacować transformację afiniczną na czymś, co nie jest idealnie.

Myślę, że sceneView.pointOfView?.camera?.projectionTransformjest bardziej pomocny niż ten, sceneView.pointOfView?.camera?.projectionTransform?.camera.projectionMatrixponieważ później uwzględnia już transformację wywnioskowaną z ARKita, która mnie nie interesuje w przypadku tego problemu.

Jak bym wypełnił

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 
}

==== Edytuj ====

Po wypróbowaniu wielu rzeczy, w końcu zdecydowałem się na oszacowanie pozycji kamery za pomocą projekcji openCV i solvera perspektywicznego. solvePnPTo daje mi obrót i tłumaczenie, które powinny odzwierciedlać pozę kamery w referencji kodu QR. Jednak podczas korzystania z tych wartości i umieszczania obiektów odpowiadających transformacji odwrotnej, w których kod QR powinien znajdować się w przestrzeni kamery, otrzymuję niedokładne przesunięte wartości i nie jestem w stanie uzyskać obrotu do pracy:

// 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
}

Oto wynik:

wprowadź opis obrazu tutaj

gdzie A, B, C, D to rogi kodu QR w kolejności, w jakiej są przekazywane do programu.

Przewidywane źródło pozostaje na miejscu, gdy telefon się obraca, ale jest przesunięte z miejsca, w którym powinno. O dziwo, jeśli zmienię wartości obserwacji, jestem w stanie to poprawić:

  // (imageSize.height * (1 - $0.y), imageSize.width * $0.x)
  // replaced by:
  (imageSize.height * (1.35 - $0.y), imageSize.width * ($0.x - 0.2))

wprowadź opis obrazu tutaj

a teraz przewidywane pochodzenie pozostaje na swoim miejscu. Jednak nie rozumiem, skąd pochodzą wartości przesunięcia.

Wreszcie próbowałem ustalić orientację względem referencji kodu 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)

Orientacja jest w porządku, gdy patrzę prosto na kod QR, ale potem przesuwa się o coś, co wydaje się być związane z obrotem telefonu:wprowadź opis obrazu tutaj

Wybitne pytania, które mam, to:

  • Jak rozwiązać rotację?
  • skąd się biorą wartości przesunięcia pozycji?
  • Jaką prostą zależność weryfikuje rotacja, tłumaczenie, QRCornerCoordinatesInQRRef, obserwacje, intrisics? Czy to O ~ K ^ -1 * (R_3x2 | T) Q? Bo jeśli tak, to jest to o kilka rzędów wielkości.

Jeśli to pomoże, oto kilka wartości liczbowych:

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 ====

Zauważyłem, że obrót działa dobrze, gdy telefon pozostaje poziomo równolegle do kodu QR (tj. Macierz obrotu to [[a, 0, b], [0, 1, 0], [c, 0, d]] ), bez względu na rzeczywistą orientację kodu QR:

wprowadź opis obrazu tutaj

Inna rotacja nie działa.

Guig
źródło
Hej, czy próbujesz uzyskać odległość między urządzeniami za pomocą kodu QR? Jeśli tak, zobacz moją odpowiedź poniżej.
Ephellon Dantzler
EDYCJA: w przypadku nierozstrzygniętych pytań: 1. Wygląda na to, że wstawiono po prostu niepotrzebną wartość. Prawdopodobnie w wywołanej metodzie mapowania lub w czymkolwiek innym zajmującym się rysowanymi okręgami (np. drawCircle(... rotation)) 2. Nie miałem czasu na przeczytanie specyfikacji 3. Tak samo jak 2
Ephellon Dantzler
Czy będziesz mógł udostępnić jakiś kod?
Michał Zaborowski

Odpowiedzi:

1

Matematyka (tryg.):

Równanie

Uwagi: dół to l(długość kodu QR), lewy kąt to k, a górny kąt to i(kamera)

Obrazek

Ephellon Dantzler
źródło
il
jasne
w porządku, czy istnieje sposób, aby znaleźć przeciwieństwo i? Jeśli nie jest pod kątem prostym, ltrzeba znaleźć więcej matematyki albo kalbo theta; i + k + theta = 180.
Ephellon Dantzler
1
Aby trygonometria działała, potrzebuję albo dwóch odległości i jednego kąta, albo dwóch kątów i jednej odległości. Nie ma sposobu, aby uzyskać wszystko z jednego kąta i jednej odległości
Guig
Czy pomaga to, że kod QR jest kwadratowy, dzięki czemu można obserwować dwa kąty, zarówno pionowy, jak i poziomy?
Bob Wakefield
1

Przypuszczam, że problem nie dotyczy matrycy. Jest w położeniu wierzchołków. Aby śledzić obrazy 2D, musisz umieścić wierzchołki ABCD w kierunku przeciwnym do ruchu wskazówek zegara (punktem początkowym jest wierzchołek znajdujący się w urojonym początku x:0, y:0 ). Myślę, że dokumentacja Apple dotycząca klasy VNRectangleObservation (informacje o rzutowanych prostokątnych obszarach wykrytych przez żądanie analizy obrazu) jest niejasna. Umieściłeś wierzchołki w tej samej kolejności, co w oficjalnej dokumentacji:

var bottomLeft: CGPoint
var bottomRight: CGPoint
var topLeft: CGPoint
var topRight: CGPoint

Ale należy je umieścić w taki sam sposób, jak dodatni kierunek obrotu (wokół Zosi) występuje w układzie współrzędnych kartezjańskich:

wprowadź opis obrazu tutaj

Przestrzeń współrzędnych świata w ARKit (jak również w SceneKit i Vision) zawsze podąża za a right-handed convention(dodatnia Yoś jest skierowana w górę, dodatnia Zoś jest skierowana w stronę widza, a dodatnia Xoś wskazuje w prawo widza), ale jest zorientowana w oparciu o konfigurację sesji . Kamera działa w lokalnej przestrzeni współrzędnych.

Kierunek obrotu wokół dowolnej osi jest dodatni (przeciwnie do ruchu wskazówek zegara) i ujemny (zgodnie z ruchem wskazówek zegara). Do śledzenia w ARKit i Vision jest to niezwykle ważne.

wprowadź opis obrazu tutaj

Kolejność rotacji również ma sens. ARKit, podobnie jak SceneKit, stosuje obrót względem właściwości pivot węzła w odwrotnej kolejności komponentów: najpierw roll(wokół Zosi), następnie yaw(wokół Yosi), a następnie pitch(wokół Xosi). Więc kolejność rotacji jest ZYX.

Jest też przydatny post na temat operacji Matrix na Nukepedii.

Andy Fedoroff
źródło