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



我正在尝试使用图像识别功能的ARKit 1.5,正如我们在Apple示例项目的代码中读到的那样:初始检测后不会跟踪图像定位点,因此请创建一个 限制平面可视化显示持续时间的动画。

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

我想实现像这个视频中这样的东西,也就是说,有一个固定图像、按钮、标签等,保持在检测到的图像之上,我不明白我如何实现这一目标。

以下是图像检测结果的代码:

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

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

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

随着时间的推移,ARKit会找出更多相同的平面在哪里,因此飞机锚的extent变得更大。但是,您最初可能会检测到桌子的一端,然后识别出更多的远端 - 这意味着平面不会以检测到的第一个补丁为中心。ARKit 不会更改定位点的transform,而是告诉您新的center(相对于该转换)。

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

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

  • 只需将其添加为 ARKit 在该委托方法中为您创建的SCNNode的子节点即可。如果您不执行某些操作来更改它们,则其位置和方向将与拥有它的节点的位置和方向相匹配。(这就是您引用的 Apple 示例代码的作用。

  • 将其放置在世界空间中(即,作为场景rootNode的子项),使用定位点的transform获取位置和/或方向。

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


不过,您链接到的演示视频并没有将内容放在3D空间中 - 而是将2D UI元素放在屏幕上,其位置跟踪世界空间中锚点的3D摄像机相对运动。

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

如果这就是您正在寻找的效果,那么还有一个应用程序(le 示例代码)。通过 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)
}

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


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

苹果的API并没有提供一种"开箱即用"的直接方法,但想象一些方法可以将API片段放在一起以获得这种结果并不是一件容易的事。以下是几种探索途径:

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

    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设置为视图控制器的子视图(而不是根视图),并使这些 UIKit 元素成为其他子视图。然后,在 SpriteKit 场景的update方法中,遍历以下ARAnchor节点,将它们的位置从 SpriteKit 场景坐标转换为 UIKit 视图坐标,并相应地设置 UIKit 元素的位置。(该演示似乎使用了弹出框,因此您不会将其作为子视图管理的那些...您可能会更新每个弹出窗口的sourceRect。这涉及更多内容,因此细节超出了这个已经很长的答案的范围。


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

但是要澄清并警告您问题早期的一些关键词:

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

更新:在iOS 12中,您可以启用对检测到的图像的"实时"跟踪。但是,您一次可以跟踪的数量是有限制的,因此此建议的其余部分可能仍然适用。

这并不意味着您不能放置似乎"跟踪"该静态世界空间位置的内容,即在屏幕上移动以随着相机移动而跟随它。

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

首先,我还没有完全按照您的要求进行操作,因此,如果我要实现它,这只是我要开始的地方......

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

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

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

对于物品的放置,您将使用renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)的SpriteKit版本,即view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor)

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

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

我们也可以使用SpriteKit获得参考图像的尺寸,并且新放置的SKNode最终将像SCNNodes一样位于检测到的图像的中心。这意味着我们应该能够创建一个新的SKNode(SKSpriteNode用于图像或SKLabelNode用于标签),并将其变换偏移为参考图像高度的一半,以获得图像的顶部中心。一旦节点被放置,它应该看起来会粘在海报上(ARKit 并不完美,所以会发生一些移动)。

自 iOS 12 起更新:

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

另请参阅代码示例跟踪和更改图像。

最新更新