ARKit - Как поместить 3D-объект в QRCode?

Я на самом деле пытаюсь поместить 3D-объект в QRCode с помощью ARKit. Для этого я использую AVCaptureDevice, чтобы обнаружить QRCode и установить область QRCode, которая дает мне CGRect. Затем я делаю hitTest для каждой точки CGRect, чтобы получить средние трехмерные координаты, например, так:

positionGiven = SCNVector3(0, 0, 0)

for column in Int(qrZone.origin.x)...2*Int(qrZone.origin.x + qrZone.width) {
    for row in Int(qrZone.origin.y)...2*Int(qrZone.origin.y + qrZone.height) {
        for result in sceneView.hitTest(CGPoint(x: CGFloat(column)/2,y:CGFloat(row)/2), types: [.existingPlaneUsingExtent,.featurePoint]) {

            positionGiven.x+=result.worldTransform.columns.3.x
            positionGiven.y+=result.worldTransform.columns.3.y
            positionGiven.z+=result.worldTransform.columns.3.z
            cpts += 1
        }
    }
}

positionGiven.x=positionGiven.x/cpts
positionGiven.y=positionGiven.y/cpts
positionGiven.z=positionGiven.z/cpts

Но hitTest не обнаруживает никаких результатов и не останавливает камеру, в то время как когда я делаю hitTest с сенсорным экраном, он работает. У вас есть идеи, почему это не работает? У вас есть другая идея, которая может помочь мне достичь того, что я хочу сделать?

Я уже думал о 3D-переводе с CoreMotion, который может дать мне наклон устройства, но это кажется действительно утомительным. Я также слышал об ARWorldAlignmentCamera, который может заблокировать координаты сцены в соответствии с ориентацией камеры, но я не знаю, как ее использовать!

Изменить: я пытаюсь переместить мой 3D-объект каждый раз, когда я касаюсь экрана, и хит-тест является положительным, и это довольно точно! Я действительно не понимаю, почему hitTest для области пикселей не работает...

Редактировать 2: Вот код hitTest, который работает с 2-5 касаниями на экране:

@objc func touch(sender : UITapGestureRecognizer) {

    for result in sceneView.hitTest(CGPoint(x: sender.location(in: view).x,y: sender.location(in: view).y), types: [.existingPlaneUsingExtent,.featurePoint]) {
        //Pop up message for testing
        alert("\(sender.location(in: view))", message: "\(result.worldTransform.columns.3)")

        //Moving the 3D Object to the new coordinates
        let objectList = sceneView.scene.rootNode.childNodes

        for object : SCNNode in objectList {
            object.removeFromParentNode()
        }
        addObject(SCNVector3(result.worldTransform.columns.3.x,result.worldTransform.columns.3.y,result.worldTransform.columns.3.z))
    }
}

Редактировать 3: мне удается решить мою проблему частично.

Я беру матрицу преобразования камеры (session.currentFrame.camera.transform), чтобы объект находился перед камерой. Затем я применяю перевод на (x, y) с позиции CGRect. Однако я не могу перевести ось Z, потому что у меня недостаточно информации. И мне, вероятно, понадобится оценка координаты z, как это делает hitTest.

Заранее спасибо ! :)

Ответ 1

Вы можете использовать Apple Vision API для обнаружения QR-кода и установки якоря.

Чтобы начать обнаружение QR-кодов, используйте:

 var qrRequests = [VNRequest]()
 var detectedDataAnchor: ARAnchor?
 var processing = false

 func startQrCodeDetection() {
    // Create a Barcode Detection Request
    let request = VNDetectBarcodesRequest(completionHandler: self.requestHandler)
    // Set it to recognize QR code only
    request.symbologies = [.QR]
    self.qrRequests = [request]
}

В ARSession didUpdate Frame

public func session(_ session: ARSession, didUpdate frame: ARFrame) {
    DispatchQueue.global(qos: .userInitiated).async {
        do {
            if self.processing {
              return
            }
            self.processing = true
            // Create a request handler using the captured image from the ARFrame
            let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: frame.capturedImage,
                                                            options: [:])
            // Process the request
            try imageRequestHandler.perform(self.qrRequests)
        } catch {

        }
    }
}

Обработайте QR-запрос Vision и запустите тест на попадание

func requestHandler(request: VNRequest, error: Error?) {
    // Get the first result out of the results, if there are any
    if let results = request.results, let result = results.first as? VNBarcodeObservation {
        guard let payload = result.payloadStringValue else {return}
        // Get the bounding box for the bar code and find the center
        var rect = result.boundingBox
        // Flip coordinates
        rect = rect.applying(CGAffineTransform(scaleX: 1, y: -1))
        rect = rect.applying(CGAffineTransform(translationX: 0, y: 1))
        // Get center
        let center = CGPoint(x: rect.midX, y: rect.midY)

        DispatchQueue.main.async {
            self.hitTestQrCode(center: center)
            self.processing = false
        }
    } else {
        self.processing = false
    }
}

 func hitTestQrCode(center: CGPoint) {
    if let hitTestResults = self.latestFrame?.hitTest(center, types: [.featurePoint] ),
        let hitTestResult = hitTestResults.first {
        if let detectedDataAnchor = self.detectedDataAnchor,
            let node = self.sceneView.node(for: detectedDataAnchor) {
            let previousQrPosition = node.position
            node.transform = SCNMatrix4(hitTestResult.worldTransform)

        } else {
            // Create an anchor. The node will be created in delegate methods
            self.detectedDataAnchor = ARAnchor(transform: hitTestResult.worldTransform)
            self.sceneView.session.add(anchor: self.detectedDataAnchor!)
        }
    }
}

Затем обработайте добавление узла при добавлении привязки.

func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {

    // If this is our anchor, create a node
    if self.detectedDataAnchor?.identifier == anchor.identifier {
        let sphere = SCNSphere(radius: 1.0)
        sphere.firstMaterial?.diffuse.contents = UIColor.redColor()
        let sphereNode = SCNNode(geometry: sphere)
        sphereNode.transform = SCNMatrix4(anchor.transform)
        return sphereNode
    }
    return nil
}

Источник