首页 文章

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

提问于
浏览
3

我正在尝试使用具有图像识别功能的ARKit 1.5,并且正如我们可以在苹果公司的示例项目的代码中看到的那样:初始检测后未跟踪图像锚点,因此创建一个动画来限制显示平面可视化的持续时间。

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

2 回答

  • 14

    您已经完成了大部分工作-您的代码将一个平面放置在检测到的图像上方,因此很显然,您正在执行的操作成功地将平面的中心位置设置为图像锚点的中心位置。也许您的第一步应该是更好地了解您拥有的代码...

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

    随着时间的流逝,ARKit 会找出更多相同平面的位置,因此平面锚的extent会变大。但是,您可能最初会检测到桌子的一端,然后识别出远端的更多部分-这意味着平坦的表面不会围绕检测到的第一个面片居中。 ARKit 不会更改锚的transform,而是告诉您新的center(与该转换有关)。

    ARImageAnchor不会增长-ARKit 会立即检测到整个图像,或者根本无法检测到图像。因此,当您检测到图像时,锚点transform会告诉您图像中心的位置和方向。 (如果您想知道 size/extent,则可以从检测到的参考图像的physicalSize中获取,例如示例代码 does.)

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

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

    • 使用锚点transform获取位置或方向或同时将两者放置在世界空间中(即,作为场景rootNode的子级)。

    transform.columns.3 float4


    但是,链接到的演示视频并没有将内容放到 3D 空间中,而是在屏幕上放了 2D UI 元素,它们的位置跟踪了世界空间中锚点的 3D camera-relative 移动。

    通过使用ARSKView(ARKit SpriteKit)而不是ARSCNView(ARKit SceneKit),您可以轻松获得这种效果(大致近似)。这样一来,您就可以将 2D 精灵与世界空间中的 3D 位置相关联,然后ARSKView自动移动并缩放它们,使它们看起来保持与那些 3D 位置的关系。这是一种常见的 3D 图形技巧,称为“广告牌”,其中 2D 子画面始终保持直立并面向相机,但会随处移动并缩放以匹配 3D 透视图。

    如果您正在寻找这种效果,那么也有一个 App(le 示例代码)。 通过 ARKit 实时使用视觉示例主要是关于其他主题的,但确实显示了如何使用ARSKView显示与ARAnchor职位相关的标签。 (正如您在上面看到的,不管您是 using.)的哪个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 提供的子代即可。


    但是,演示视频不仅仅只是精灵广告牌:它与绘画相关联的标签不仅以 2D 方向固定,而且还以 2D 尺寸固定(也就是说,它们不像广告牌精灵那样缩放以模拟视角) 。而且,它们似乎是 UIKit 控件,具有整套继承的交互行为,不仅需要像 SpriteKit 一样的 2D 图像,还需要 2D 图像。

    Apple 的 API 并没有提供“开箱即用”的直接方法,但是想像一下可以将 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设置为视图控制器的子视图(而不是根视图),并将那些 UIKit 元素设置为其他子视图。然后,在 SpriteKit 场景的update方法中,遍历ARAnchor -following 节点,将其位置从 SpriteKit 场景坐标转换为 UIKit 视图坐标,并相应地设置 UIKit 元素的位置。 (该演示似乎使用的是弹出窗口,因此您不会将其作为子视图进行管理……您可能会为每个 popover.)更新sourceRect,这涉及更多的工作,因此详细信息已超出了此范围长答案。

    最后的注释...希望这个 long-winded 答案对您所遇到的关键问题有所帮助(了解锚点的位置,并在摄像机移动时放置跟随它们的 3D 或 2D 内容)。

    但是,为了澄清和警告您问题初期的一些关键词:

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

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

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

    但这确实意味着,如果您尝试执行依赖于图像位置的 high-precision,real-time 估算的操作,则可能会损害用户体验。因此,请不要尝试在绘画周围放置虚拟框架,或以动画版本本身替代绘画。但是,使用带有箭头的文本标签大致指向图像在空间中的位置是很棒的。

  • 1

    首先,我并没有完全按照您的意图进行操作,所以这只是我要实施的地方...

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

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

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

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

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

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

    奇怪的是,我们也可以使用 SpriteKit 获取参考图像的尺寸,并且新放置的SKNode s 最终将以SCNNode s 的方式最终位于检测到的图像的中心。这意味着我们应该能够创建一个新的SKNode(对于图像为SKSpriteNode或对于标签为SKLabelNode),并将其转换偏移参考图像高度的一半以获得图像的顶部中心。放置节点后,它似乎应该贴在海报上(ARKit 并不完美,因此会发生一些移动)。

相关问题