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

How to track image anchors after initial detection in ARKit 1.5?

我正在尝试使用 ARKit 1.5 进行图像识别,正如我们可以在 sample project from Apple 的代码中读到的那样: 图像锚点在初始检测后未被跟踪,因此创建一个 限制平面可视化出现持续时间的动画。

ARImageAnchor 没有像 ARPlaneAnchor 那样的 center: vector_float3,我找不到如何跟踪检测到的图像锚点。

我想实现类似这个 video 的东西,也就是说,有一个修复图像、按钮、标签等等,停留在检测到的图像之上,我不明白如何我能做到。

这里是图片检测结果的代码:

// 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)”")
    }
}

首先,我还没有完全按照您的意愿去做,所以如果我要实施它,这只是我要开始的地方……

根据您列出的代码,您正在使用 Apple 的示例代码在 AR 体验中识别图像。该项目设置为使用没有标签、按钮或图像的 SceneKit。这意味着您需要使用 SpriteKit,它具有可以显示标签和图像的节点。

这意味着您的第一步是创建一个全新的项目和 select 内容技术设置为 SpriteKit 的增强现实模板。

您可以查看resetTracking() Apple 的图像识别示例,了解如何设置图像识别部分。您还需要手动将 AR Resources 文件夹添加到资产目录以保存所有参考图像。

要放置项目,您可以使用 renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) 的 SpriteKit 版本,即 view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor)

使用 Apple 的图像识别示例代码,新的 SceneKit 对象被添加到参考图像的中心,如果图像没有移动,虚拟对象将保持在中心(-ish)。

我们也知道我们可以获得参考图像的高度和宽度(如您发布的代码所示)。

很可能我们也可以使用 SpriteKit 获取参考图像的尺寸,并且新放置的 SKNodes 将以与 SCNNodes 相同的方式最终位于检测到的图像的中心。这意味着我们应该能够创建一个新的 SKNodeSKSpriteNode 用于图像或 SKLabelNode 用于标签)并将它的转换偏移参考图像高度的一半以获得顶部中心图片。放置节点后,它应该会粘在海报上(ARKit 并不完美,因此会发生一些移动)。

你已经完成了大部分工作——你的代码将一个平面放置在检测到的图像之上,所以很明显你在那里发生了一些事情,成功地将平面的中心位置设置为图像锚点的中心位置。也许您的第一步应该是更好地理解您拥有的代码...

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

随着时间的推移,ARKit 会找出更多相同平面的位置,因此平面锚点的 extent 会变大。但是你可能最初检测到,比方说,table 的一端,然后识别出更多的远端——这意味着平坦表面并不以检测到的第一个补丁为中心。 ARKit 并没有更改锚点的 transform,而是告诉您新的 center 相对于 变换)。

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

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

  • 只需将其添加为 SCNNode ARKit 在该委托方法中为您创建的 child 节点。如果你不做一些改变它们,它的位置和方向将与拥有它的节点相匹配。 (这就是您引用的 Apple 示例代码所做的。)

  • 放在worldspace中(即作为场景的rootNode的child),使用anchor的transform得到位置或方向或两者。

    (您可以从转换矩阵中提取平移——即相对位置:获取最后一列的前三个元素;例如 transform.columns.3 是一个 float4向量,其xyz元素是你的位置,w元素是1。)


您链接到的演示视频并未将内容放入 3D space,但它在屏幕上放置了 2D UI 元素,其位置跟踪 3D camera-relative 移动世界上的主播 space.

通过使用 ARSKView (ARKit+SpriteKit) 而不是 ARSCNView (ARKit +场景套件)。这使您可以将 2D 精灵与世界 space 中的 3D 位置相关联,然后 ARSKView 自动移动和缩放它们,使它们看起来与这些 3D 位置保持联系。这是一种称为“广告牌”的常见 3D 图形技巧,其中 2D 精灵始终保持直立并面向相机,但会四处移动并缩放以匹配 3D 透视图。

如果这就是您要寻找的效果,那么也有一个应用程序(示例代码)。 Using Vision in Real Time with 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)
}

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


然而,演示视频不仅仅做 sprite 广告牌:它与绘画关联的标签不仅在 2D 方向上保持固定,而且在 2D 大小上保持固定(也就是说,它们不会缩放以模拟透视一个广告牌精灵确实如此)。更重要的是,它们似乎是 UIKit 控件,具有完整的继承交互行为集,而不仅仅是 2D 图像,类似的是使用 SpriteKit 的方法。

Apple 的 APIs 没有提供“开箱即用”的直接方法,但不难想象可以通过某些方式将 API 部分拼凑起来这种结果。这里有几个探索途径:

  • 如果你不需要 UIKit 控件,你可能可以在 SpriteKit 中完成所有操作,使用约束来匹配“广告牌”节点的位置 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 ]
    }
    
  • 如果您想要 UIKit 元素,请将 ARSKView 设为视图控制器的 child 视图(不是根视图),然后将那些 UI套件元素其他 child 视图。然后,在你的 SpriteKit 场景的 update 方法中,遍历你的 ARAnchor-following 节点,将它们的位置从 SpiteKit 场景坐标为 UIKit 视图坐标,并相应地设置 UIKit 元素的位置。 (该演示似乎使用了弹出窗口,因此您不会将其管理为 child 视图……您可能会为每个弹出窗口更新 sourceRect。)涉及的内容更多,所以细节超出了这个已经很长的答案的范围。


最后一点...希望这个 long-winded 答案对您问题的关键 问题 有所帮助(了解锚点位置并放置 3D 或 2D 内容随着相机移动跟随他们)。

但是要在你的问题的开头就一些关键的澄清并发出警告:

当 ARKit 说它在检测后不跟踪图像时,这意味着它不知道 when/if 图像移动(相对于它周围的世界)。 ARKit 只报告一次图像的位置,因此该位置甚至不会受益于 ARKit 如何继续改进其对您周围世界的估计以及您在其中的位置。例如,如果图像在墙上,报告的图像 position/orientation 可能与墙上的垂直平面检测结果不一致(尤其是随着时间的推移,随着平面估计的改进)。

Update: In iOS 12, you can enable "live" tracking of detected images. But there are limits on how many you can track at once, so the rest of this advice may still apply.

这并不意味着您不能放置看似“跟踪”static-in-world-space 位置的内容,即在屏幕上四处移动以随着相机移动而跟随它。

但这确实意味着如果您尝试做依赖于对图像位置的 high-precision、real-time 估计的事情,您的用户体验可能会受到影响。所以,比如说,不要尝试在你的画周围放置一个虚拟框架,或者用它本身的动画版本替换这幅画。但是有一个带有箭头指向 粗略 图像在 space 中的文本标签很棒。

更新自 iOS 12:

您可以使用 ARImageTrackingConfiguration 跟踪后置摄像头中的已知图像。

另请参阅代码示例 Tracking and Altering Images