如何在ARKit 1.5中初始检测后跟踪图像锚点?

mag*_*oth 3 augmented-reality ios swift arkit

我正在尝试使用图像识别的ARKit 1.5,因为我们可以从Apple示例项目代码中读取: 初始检测后不会跟踪图像锚点,因此创建一个限制平面可视化显示持续时间的动画.

一个ARImageAnchor没有center: vector_float3喜欢的ARPlaneAnchor,我找不到如何跟踪检测到的图像锚点.

我希望在这个视频中实现类似的东西,即拥有修复图像,按钮,标签等等,保持在检测到的图像之上,我不明白我是如何实现这一点的.

这是图像检测结果的代码:

// MARK: - ARSCNViewDelegate (Image detection results)
/// - Tag: ARImageAnchor-Visualizing
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let imageAnchor = anchor as? ARImageAnchor else { return }
    let referenceImage = imageAnchor.referenceImage
    updateQueue.async {

        // Create a plane to visualize the initial position of the detected image.
        let plane = SCNPlane(width: referenceImage.physicalSize.width,
                         height: referenceImage.physicalSize.height)
        plane.materials.first?.diffuse.contents = UIColor.blue.withAlphaComponent(0.20)
        self.planeNode = SCNNode(geometry: plane)

        self.planeNode?.opacity = 1

        /*
         `SCNPlane` is vertically oriented in its local coordinate space, but
         `ARImageAnchor` assumes the image is horizontal in its local space, so
         rotate the plane to match.
         */
        self.planeNode?.eulerAngles.x = -.pi / 2

        /*
         Image anchors are not tracked after initial detection, so create an
         animation that limits the duration for which the plane visualization appears.
         */

        // Add the plane visualization to the scene.
        if let planeNode = self.planeNode {
            node.addChildNode(planeNode)
        }

        if let imageName = referenceImage.name {
            plane.materials = [SCNMaterial()]
            plane.materials[0].diffuse.contents = UIImage(named: imageName)
        }
    }

    DispatchQueue.main.async {
        let imageName = referenceImage.name ?? ""
        self.statusViewController.cancelAllScheduledMessages()
        self.statusViewController.showMessage("Detected image “\(imageName)”")
    }
}
Run Code Online (Sandbox Code Playgroud)

ric*_*ter 14

你已经大部分都在那里 - 你的代码在检测到的图像上放置了一个平面,所以很明显你有一些东西可以成功地将平面的中心位置设置为图像锚点的中心位置.也许你的第一步应该是更好地理解你拥有的代码......

ARPlaneAnchor有一个center(和extent)因为在ARKit最初检测到它们之后,平面可以有效地增长.当你第一次得到一个平面锚时,它会transform告诉你一些平坦的水平(或垂直)表面的小块的位置和方向.仅此就足以让您将一些虚拟内容放置在那小块表面的中间.

随着时间的推移,ARKit会计算出更多相同的平面,因此平面锚extent越来越大.但是你最初可能会检测到一个表的一端,然后识别更多的远端 - 这意味着平面不会在检测到的第一个补丁周围.transformARKit 不是更改锚点,而是告诉你新的center(相对于该转换).

一个ARImageAnchor不会增长 - 要么ARKit立刻检测到整个图像,要么根本不检测图像.因此,当您检测到图像时,锚点会transform告诉您图像中心的位置和方向.(如果你想知道大小/范围,你可以从physicalSize检测到的参考图像得到它,就像示例代码一样.)

因此,要将一些SceneKit内容放在ARImageAnchor(或任何其他ARAnchor子类)的位置,您可以:

  • 只需将其添加为SCNNodeARKit在该委托方法中为您创建的子节点.如果您没有做某些事情来改变它们,它的位置和方向将与拥有它的节点的位置和方向相匹配.(这是您引用的Apple示例代码所做的.)

  • 将它放置在世界空间(即,作为场景的孩子rootNode),使用锚点transform来获取位置或方向或两者.

    (您可以从变换矩阵中提取平移 - 即相对位置:抓取最后一列的前三个元素;例如,transform.columns.3float4xyz元素是您的位置且其w元素为1 的向量.)


您链接到的演示视频并没有把东西放在3D空间中 - 它将2D UI元素放在屏幕上,其位置跟踪3D摄像机在世界空间中相对移动的锚点.

您可以使用(ARKit + SpriteKit)而不是(ARKit + SceneKit)轻松获得这种效果(到第一个近似值).这使您可以将2D精灵与世界空间中的3D位置相关联,然后自动移动和缩放它们,使它们看起来保持与这些3D位置的连接.这是一种常见的3D图形技巧,称为"广告牌",其中2D精灵始终保持直立并面向相机,但移动并缩放以匹配3D透视.ARSKViewARSCNViewARSKView

如果那是您正在寻找的效果,那么也有一个应用程序(示例代码).在与ARKit使用实时视觉例子主要是关于其他议题,但它确实展示了如何使用ARSKView,以显示与相关的标签ARAnchor位置.(正如您在上面所看到的,无论ARAnchor您使用哪个子类,放置匹配锚位置的内容都是相同的.)以下是其代码中的关键位:

func view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor) {
    // ... irrelevant bits omitted... 
    let label = TemplateLabelNode(text: labelText)
    node.addChild(label)
}
Run Code Online (Sandbox Code Playgroud)

也就是说,只需实现ARSKView didAdd委托方法,并添加您想要的任何SpriteKit节点作为ARKit提供的子节点.


然而,演示视频不仅仅是精灵广告牌:它与绘画相关联的标签不仅保持固定在2D方向,它们保持固定在2D大小(也就是说,它们不能缩放以模拟透视,就像广告牌精灵一样) .更重要的是,它们似乎是UIKit控件,具有完整的继承交互行为,而不仅仅是2D图像,而这些图像与SpriteKit相关.

Apple的API没有直接提供"开箱即用"的方法,但想象一下将API组合在一起以获得这种结果的方式并不是一件容易的事.以下是一些探索途径:

  • 如果您不需要UIKit控件,您可以在SpriteKit中完成所有操作,使用约束来匹配"billboarded"节点ARSKView提供的位置,而不是它们的比例.这可能看起来像这样(未经测试,警告的经纪人):

    func view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor) {
        let label = MyLabelNode(text: labelText) // or however you make your label
        view.scene.addChild(label)
    
        // constrain label to zero distance from ARSKView-provided, anchor-following node
        let zeroDistanceToAnchor = SKConstraint.distance(SKRange(constantValue: 0), to: node)
        label.constraints = [ zeroDistanceToAnchor ]
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您想要UIKit元素,请创建ARSKView视图控制器的子视图(而不是根视图),并使这些UIKit元素成为其他子视图.然后,在SpriteKit场景的update方法中,浏览ARAnchor以下节点,将它们的位置从SpriteKit场景坐标转换为UIKit视图坐标,并相应地设置UIKit元素的位置.(该演示似乎是使用弹出框,所以那些你不会作为子视图管理...你可能会更新sourceRect每个popover.)这涉及更多,所以细节超出了这个范围已经很久了.


最后一点......希望这个冗长的答案对你的问题的关键问题有所帮助(理解锚定位置并在摄像机移动时放置跟随它们的3D或2D内容).

但澄清并给有关的一些关键警告的话在你的问题早:

当ARKit说它在检测后没有跟踪图像时,这意味着它不知道图像何时/是否移动(相对于它周围的世界).ARKit仅报告一次图像的位置,因此该位置甚至不会受益于ARKit如何继续改进其对您周围世界的估计以及您在其中的位置.例如,如果图像在墙上,则报告的图像位置/方向可能与墙上的垂直平面检测结果不一致(特别是随着时间的推移,平面估计得到改善).

更新:在iOS 12中,您可以启用对搜索到的图像的"实时"跟踪.但是,您可以同时跟踪多少人有限制,因此其他建议可能仍然适用.

这并不意味着你不能放置看似"跟踪"静态世界空间位置的内容,就像你在相机移动时在屏幕上移动一样.

但它确实意味着如果您尝试做依赖于对图像位置进行高精度,实时估计的事情,您的用户体验可能会受到影响.所以不要试图在你的绘画周围放置虚拟框架,或者用自己的动画版本替换画面.但是,带有箭头的文本标签大致指向图像在空间中的位置是很棒的.