代码之家  ›  专栏  ›  技术社区  ›  PvDev

AR参考图像平面在iOS Swift中的位置不正确?

  •  3
  • PvDev  · 技术社区  · 6 年前

    我正在使用AR参考图像处理名片配置文件信息。当reference检测到时,它将显示公司ceo的详细信息、地址、照片和团队成员信息等。最初检测到的图像将使用 运行操作 胺化。

    我的问题是探测到参考,平面位置是稳定的,它在这里和那里移动。如何用ar参考图像拟合平面位置。

    这是我的截图结果:[![在此处输入图像描述][1]][1]

    这是我使用的代码:

    var weboverlayview: CALayer?
    var loadWeb: UIWebView?
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Set the view's delegate
        sceneView.delegate = self
    
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        sceneView.autoenablesDefaultLighting = true
        let ARScene = SCNScene()
        sceneView.scene = ARScene
    
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = [.vertical, .horizontal]
        configuration.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
        // Run the view's session
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    
        // Pause the view's session
        sceneView.session.pause()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }
    
    
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    
        let anchorNode = SCNNode()
        anchorNode.name = "anchor"
        sceneView.scene.rootNode.addChildNode(anchorNode)
        return anchorNode
    
    }
    
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    
        var labelNode:SCNNode?
        var companyLabelNode: SCNNode?
        var addressLabelNode: SCNNode?
        var webaddressLabelNode: SCNNode?
        var maillabelNode: SCNNode?
        var mobileLabelNode:SCNNode?
        var teamLabelNode:SCNNode?
    
    
        guard let imageAnchor = anchor as? ARImageAnchor else {return}
        if let imageName  = imageAnchor.referenceImage.name {
    
            print(imageName)
    
    
    
            if imageName == "card"{
    
    
    
                let plane = SCNPlane(width: 20,height: 24)
                plane.firstMaterial?.diffuse.contents = UIColor.black.withAlphaComponent(0.75)
                plane.cornerRadius = 0.25
    
                let planeNodee = SCNNode(geometry: plane)
                planeNodee.eulerAngles.x = -.pi / 2
                planeNodee.runAction(SCNAction.moveBy(x: -5, y: 0, z: 19, duration: 0.75))
    
                labelNode = self.addLabel(text: "Gowdhaman Kandasamy \nFounder and CEO", anchor: imageAnchor)
                labelNode?.runAction(SCNAction.moveBy(x: -1.3, y: 1, z: 16.8, duration: 0.75))
    
    
    
                companyLabelNode = self.addLabel(text: "CZ Smart Mobility", anchor: imageAnchor)
                companyLabelNode?.runAction(SCNAction.moveBy(x: 1.5, y: 1, z: 22, duration: 0.75))
    
                addressLabelNode = self.addAddressLabel(text: "Official Address:\n\n1st floor, TBI Office,\nDr.col JEPPIAR Research Park,\nResearch and development center,\nSathyabama University,\nChennai-600119\nTamil nadu, India.", anchor: imageAnchor)
                addressLabelNode?.runAction(SCNAction.moveBy(x: -4.8, y: 1, z: 16.8, duration: 0.75))
    
    
    
                let userImagePlane = SCNPlane(width: 3.5, height: 3.5)
                userImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "gow")
                userImagePlane.cornerRadius = 0.25
                let userPlaneNode = SCNNode(geometry: userImagePlane)
                userPlaneNode.eulerAngles.x = -.pi/2
                userPlaneNode.runAction(SCNAction.moveBy(x: -1, y: 1, z: 9.5, duration: 0.75))
    
    
                let webImagePlane = SCNPlane(width: 1, height: 1)
                webImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "web")
    
                webImagePlane.cornerRadius = 0.25
                let webPlanenode = SCNNode(geometry: webImagePlane)
                webPlanenode.eulerAngles.x = -.pi/2
                webPlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 12.5, duration: 0.75))
                webaddressLabelNode = addAddressLabel(text: "www.czsm.co.in", anchor: imageAnchor)
                webaddressLabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 18.2, duration: 0.75))
    
                let mailImagePlane = SCNPlane(width: 1, height: 1)
                mailImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "mail")
    
                mailImagePlane.cornerRadius = 0.25
                let mailPlanenode = SCNNode(geometry: mailImagePlane)
                mailPlanenode.eulerAngles.x = -.pi/2
                mailPlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 17.5, duration: 0.75))
                maillabelNode = addAddressLabel(text: "gowdhaman@czsm.co.in", anchor: imageAnchor)
                maillabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 23.2, duration: 0.75))
    
                let mobileImagePlane = SCNPlane(width: 1, height: 1)
                mobileImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "mobile")
    
                mobileImagePlane.cornerRadius = 0.25
                let mobilePlanenode = SCNNode(geometry: mobileImagePlane)
                mobilePlanenode.eulerAngles.x = -.pi/2
                mobilePlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 23.9, duration: 0.75))
                mobileLabelNode = addAddressLabel(text: "+919941123110", anchor: imageAnchor)
                mobileLabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 29.7, duration: 0.75))
    
    
                /************Team members*************/
    
                teamLabelNode = self.addLabel(text: "Team Members", anchor: imageAnchor)
                teamLabelNode?.runAction(SCNAction.moveBy(x: -9.8, y: 1, z: 22, duration: 0.75))
    
    
                let sivaImagePlane = SCNPlane(width: 2.7, height: 2.7)
                sivaImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "pic2")
    
                sivaImagePlane.cornerRadius = 0.25
                let sivaPlanenode = SCNNode(geometry: sivaImagePlane)
                sivaPlanenode.eulerAngles.x = -.pi/2
                sivaPlanenode.runAction(SCNAction.moveBy(x: -11, y: 1, z: 9.5, duration: 0.75))
    
    
                let parameshImagePlane = SCNPlane(width: 2.7, height: 2.7)
                parameshImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "pic4")
    
                parameshImagePlane.cornerRadius = 0.25
                let parameshPlanenode = SCNNode(geometry: parameshImagePlane)
                parameshPlanenode.eulerAngles.x = -.pi/2
                parameshPlanenode.runAction(SCNAction.moveBy(x: -11, y: 1, z: 9.5, duration: 0.75))
    
    
    
    
    
    
                node.addChildNode(planeNodee)
                node.addChildNode(labelNode!)
                node.addChildNode(companyLabelNode!)
                node.addChildNode(userPlaneNode)
                node.addChildNode(addressLabelNode!)
                node.addChildNode(webPlanenode)
                node.addChildNode(webaddressLabelNode!)
                node.addChildNode(mailPlanenode)
                node.addChildNode(maillabelNode!)
                node.addChildNode(mobilePlanenode)
                node.addChildNode(mobileLabelNode!)
                node.addChildNode(teamLabelNode!)
    
                node.addChildNode(sivaPlanenode)
                node.addChildNode(parameshPlanenode)
    
    
           //                node.addChildNode(czwebPlaneNode)
                self.sceneView.scene.rootNode.addChildNode(node)
    
    
    
            }
    
        }
    
    
       }
       func webButton() {
    
    
    
    
    
    
      }
    
    
    func addLabel(text: String, anchor: ARImageAnchor) -> SCNNode {
        let plane = SCNPlane(width: 10,
                             height: 4)
    
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = (-.pi)/2
        planeNode.eulerAngles.y = (-.pi)/2
        //        planeNode.eulerAngles.z = (-.pi)/2
    
    
    
        let skScene = SKScene(size: CGSize(width: 400, height: 100))
    
        skScene.backgroundColor = UIColor.clear
    
        let substrings: [String] = text.components(separatedBy: "\n")
        for aSubstring in substrings {
            let lbl = SKLabelNode(text: aSubstring)
            lbl.fontSize = 18
            lbl.numberOfLines = 1
            lbl.fontColor = UIColor.white
            lbl.fontName = "Avenir-medium"
            let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl.fontSize
            print("yname::::\(y)")
            lbl.position = CGPoint(x: 0, y: y)
            lbl.horizontalAlignmentMode = .left
            lbl.yScale *= -1
            skScene.addChild(lbl)
    
    
    
        }
    
    
    
    
        let material = SCNMaterial()
        material.isDoubleSided = false
        material.diffuse.contents = skScene
        plane.materials = [material]
    
        return planeNode
        }
    
          func addCompanyLabel(text: String, anchor: ARImageAnchor) -> SCNNode {
            let plane1 = SCNPlane(width: 10,
                             height: 4)
    
        let planeNode1 = SCNNode(geometry: plane1)
        planeNode1.eulerAngles.x = (-.pi)/2
        planeNode1.eulerAngles.y = (-.pi)/2
        //        planeNode.eulerAngles.z = (-.pi)/2
    
    
    
        let skScene1 = SKScene(size: CGSize(width: 400, height: 100))
    
        skScene1.backgroundColor = UIColor.clear
    
        let substrings: [String] = text.components(separatedBy: "\n")
        for aSubstring in substrings {
            let lbl1 = SKLabelNode(text: aSubstring)
            lbl1.fontSize = 20
            lbl1.numberOfLines = 1
            lbl1.fontColor = UIColor.white
            lbl1.fontName = "Avenir-medium"
            let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl1.fontSize
            print("ycompanname::::\(y)")
            lbl1.position = CGPoint(x: 0, y: y)
            lbl1.horizontalAlignmentMode = .left
            lbl1.yScale *= -1
            skScene1.addChild(lbl1)
    
    
    
        }
    
        let material = SCNMaterial()
        material.isDoubleSided = false
        material.diffuse.contents = skScene1
        plane1.materials = [material]
    
           return planeNode1
         }
    
        func addAddressLabel(text: String, anchor: ARImageAnchor) -> SCNNode {
        let plane = SCNPlane(width: 10,
                             height: 4)
    
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = (-.pi)/2
        planeNode.eulerAngles.y = (-.pi)/2
            //        planeNode.eulerAngles.z = (-.pi)/2
    
    
    
        let skScene = SKScene(size: CGSize(width: 500, height: 200))
    
        skScene.backgroundColor = UIColor.clear
    
        let substrings: [String] = text.components(separatedBy: "\n")
        for aSubstring in substrings {
            let lbl = SKLabelNode(text: aSubstring)
            lbl.fontSize = 20
         //            lbl.numberOfLines = 1
            lbl.fontColor = UIColor.white
            lbl.fontName = "Avenir-medium"
            let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl.fontSize
            print("yaddress::::\(y)")
            lbl.position = CGPoint(x: 0, y: y)
            lbl.horizontalAlignmentMode = .left
            lbl.yScale *= -1
            skScene.addChild(lbl)
    
    
    
        }
    
        let material = SCNMaterial()
        material.isDoubleSided = false
        material.diffuse.contents = skScene
        plane.materials = [material]
    
        return planeNode
       }
    
    
    
    
         func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
    
    
      //        guard let planeAnchor = anchor as? ARPlaneAnchor else {return}
      //        let planeGeometry = planeAnchor.geometry
       //        guard let device = MTLCreateSystemDefaultDevice() else {return}
      //        let plane  = ARSCNPlaneGeometry(device: device)
       //        plane?.update(from: planeGeometry)
        //        node.geometry = plane
        //        node.geometry?.firstMaterial?.diffuse.contents = UIColor.black
       //        node.geometry?.firstMaterial?.transparency = 1
      //        node.geometry?.firstMaterial?.fillMode = SCNFillMode.lines
    
         }
    
    
    
      }
    

    例外结果,如AR基准面将适合卡 https://www.facebook.com/oscarfalmer/videos/10156651667309345/

    2 回复  |  直到 6 年前
        1
  •  5
  •   BlackMirrorz    6 年前

    首先你需要考虑的是你是否想使用 ARWorldTrackingConfiguration ARImageTrackingConfiguration ( IOS12 )中。

    如果你使用 图像跟踪配置 ,无法使用平面检测,因为这是仅图像跟踪配置:

    它允许您将虚拟内容锚定到已知的图像,仅当 图像在摄像机的视野中。基于图像检测的世界跟踪 允许您使用已知图像将虚拟内容添加到三维世界,以及 继续跟踪该内容在世界空间中的位置 在图像不再显示之后。

    如果您希望您的内容始终固定在图像上(当您看到相机时),这将是您的最佳选择,因为:

    它以6个自由度(6个自由度)跟踪它们的运动: 具体来说,三个旋转轴(滚动、俯仰和偏航)和 三个平移轴(X、Y和Z方向的移动)。

    另一方面如果你想发现 ARPlaneAnchors ,以及 ARImageAnchors ,但不必担心与 ARImageAnchor 不会一直跟踪你应该用 arworld跟踪配置 是的。

    正如@trinca还说的,你需要确保你为你的图像提供的测量值尽可能精确。 ARKit 使用这些返回 physicalSize physicalWidth 使虚拟内容能够更准确地放置的图像(例如,如果您指定的图像大小大于现实生活中图像的实际大小,则无法准确对齐虚拟内容)。

    enter image description here

    创建名片或任何ImageTarget时,我们必须确保在 ARReferenceImage Settings 盒子:

    enter image description here

    我们可以检查一下 imageTarget 检测结果如下:

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    
            //1. Check We Have Detected An ARImageAnchor & Check It's The One We Want
            guard let validImageAnchor = anchor as? ARImageAnchor,
                  let targetName = validImageAnchor.referenceImage.name, targetName == "TargetCard" else { return}
    
    
            //2. Check To See The Detected Size Of Our Business Card (Should By 5cm*3cm)
            let businessCardWidth = validImageAnchor.referenceImage.physicalSize.width
            let businessCardHeight =  validImageAnchor.referenceImage.physicalSize.height
    
            print(
                 """
                  We Have Detected Business Card With Name \(targetName)
                  \(targetName)'s Width Is \(businessCardWidth)
                  \(targetName)'s Height Is \(businessCardHeight)
                 """)
    
        }
    

    在检查了我们检测到的大小是否准确之后,我们就可以放置与此相关的任何内容。

    与其按程序来做每件事,更容易达到你想要的结果的方法是创建一个 SCNScene 是的。

    更新:

    正如您所要求的一个示例项目,我已经为每个人创建了一个完全工作的示例,可以在这里下载: ARKit Business Card

    没有经历每一个 Class 我将详细地向您提供基本的细节。

    我们将使用 scnscene公司 作为一个可重用的模板,它包含 SCNNode 它们用作按钮,按下按钮时可以执行不同的操作。

    基本模板如下所示:

    enter image description here

    businesscard节点初始化为businesscarddata结构,如下所示:

    typealias SocialLinkData = (link: String, type: SocialLink)
    
    /// The Information For The Business Card Node & Contact Details
    struct BusinessCardData{
    
        var firstName: String
        var surname: String
        var position: String
        var company: String
        var address: BusinessAddress
        var website: SocialLinkData
        var phoneNumber: String
        var email: String
        var stackOverflowAccount: SocialLinkData
        var githubAccount: SocialLinkData
    
    }
    
    /// The Associates Business Address
    struct BusinessAddress{
    
        var street: String
        var city: String
        var state: String
        var postalCode: String
        var coordinates: (latittude: Double, longtitude: Double)
    }
    
    /// The Type Of Social Link
    ///
    /// - Website: Business Website
    /// - StackOverFlow: StackOverFlow Account
    /// - GitHub: Github Account
    enum SocialLink: String{
    
        case Website
        case StackOverFlow
        case GitHub
    }
    

    所有提供的数据都映射到 scnnode公司 并帮助执行必要的功能。

    通过使用 struct 我们可以创建多个交互式名片,例如:

    //--------------------------
    //MARK: -  ARSessionDelegate
    //--------------------------
    
    extension  ViewController: ARSCNViewDelegate{
    
        func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    
            //1. Check We Have A Valid Image Anchor
            guard let imageAnchor = anchor as? ARImageAnchor else { return }
    
            //2. Get The Detected Reference Image
            let referenceImage = imageAnchor.referenceImage
    
            //3. Load Our Business Card
            if let matchedBusinessCardName = referenceImage.name, matchedBusinessCardName == "BlackMirrorz"{
    
                //4. Create Our Business Card
                let businessCardData = BusinessCardData(firstName: "Josh",
                                                        surname: "Robbins",
                                                        position: "Software Engineer",
                                                        company: "BlackMirrorz",
                                                        address: BusinessAddress(street: "1 Infinite Loop",
                                                                                 city: "Cupertino",
                                                                                 state: "CA",
                                                                                 postalCode: "95015",
                                                                                 coordinates: (latittude: 37.3349, longtitude: -122.0090201)),
                                                        website: SocialLinkData(link: "https://www.blackmirrorz.tech", type: .Website),
                                                        phoneNumber: "+821076337633",
                                                        email: "josh.robbins@blackmirroz.tech",
                                                        stackOverflowAccount: SocialLinkData(link: "https://stackoverflow.com/users/8816868/josh-robbins", type: .StackOverFlow),
                                                        githubAccount: SocialLinkData(link: "https://github.com/BlackMirrorz", type: .GitHub))
    
                //5. Assign It To The Business Card Node
                let businessCard = BusinessCard(data: businessCardData, cardType: .noProfileImage)
                businessCardPlaced = true
                node.addChildNode(businessCard)
    
            }
        }
    }
    

    因为设计已经布置好了,我们不需要做任何复杂的计算。一切都是为了我们!

    用户使用以下图标进行交互: enter image description here

    • 这个 StackOverFlow Button 呈现滑出 WKWebView 显示用户 StackOverFlow 帐户。
    • 这个 GitHub Button 呈现滑出 wkwebview网站 显示用户 GitHub 帐户。
    • 这个 Internet Button 呈现滑出 wkwebview网站 显示用户网站。
    • 这个 Phone Button 允许用户拨打业务电话号码。
    • 这个 SMS Button 呈现一个 MFMessageComposeViewController 允许用户向企业发送文本消息。
    • 这个 Email Button 呈现一个 MFMailComposeViewController 允许用户通过电子邮件发送业务。
    • 这个 Contact Button 创建 CNMutableContact 并将业务另存为用户设备上的新联系人。
    • 这个 Location Button 呈现滑出 MKMapView 显示用户的业务位置。

    自从呈现一个 wkwebview网站 作为 SCNMaterial ,我不得不考虑其他方法来让内容完全交互。

    因此,我利用了这个神奇的仓库 SideMenu 通过 Jonkykong 这里有: SideMenu

    这允许用户仍然体验 雅克特 同时允许几乎分裂的屏幕效果:

    enter image description here

    一如既往,希望它能帮助你和其他对学习雅克特感兴趣的人…

        2
  •  2
  •   Trinca    6 年前

    我有一个类似的问题,那是因为设置参考图像的大小时出错。

    如果要手动将它们导入到“ar resources”组,则在右面板上键入宽度和高度时,需要确保设置“meters”或“centimeters”。

    如果您从服务器加载这些图像,而不是使它们成为参考图像,请记住arkit使用的默认度量是“米”。在这种情况下,如果您将宽度设置为20.0,arkit将考虑20米而不是20厘米,这将给您在跟踪图像平面时带来非常不准确的行为。

    希望有帮助。