使用相机检测物体并使用ARKit在iOS中定位3D物体。

3

我在寻找什么?

我的需求简单来说就是:

  1. 使用ARKit,使用iPhone相机检测一个对象
  2. 找到该对象在虚拟空间中的位置
  3. 使用SceneKit在虚拟空间中放置一个3D对象。该3D对象应该在标记后面。

例如,使用相机检测一个小图像/标记在3D空间中的位置,然后在虚拟空间中在此标记的后面放置另一个3D球模型(因此球将被用户隐藏,因为标记/图像在前面)。

我目前能做到什么?

  1. 我能够使用ARKit检测标记/图像
  2. 我能够在屏幕上定位一个球形3D模型。

我的问题是什么?

我无法将球定位在被检测标记/图像的后面。

当球在标记前面时,球可以正确地隐藏标记。您可以从侧面看到图中的球在标记的前面。如下所示:

enter image description here

但当球在标记的后面时,相反的情况并没有发生。球始终看起来在前面挡住了标记。我希望标记可以隐藏球。因此,场景没有尊重球位置的z深度。如下所示:

enter image description here

代码

请参考注释

override func viewDidLoad() {
    super.viewDidLoad()

    sceneView.delegate = self
    sceneView.autoenablesDefaultLighting = true

    //This loads my 3d model.
    let ballScene = SCNScene(named: "art.scnassets/ball.scn")
    ballNode = ballScene?.rootNode

    //The model I have is too big. Scaling it here.
    ballNode?.scale = SCNVector3Make(0.1, 0.1, 0.1)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    //I am trying to detect a marker/image. So ImageTracking configuration is enough
    let configuration = ARImageTrackingConfiguration()

    //Load the image/marker and set it as tracking image
    //There is only one image in this set
    if let trackingImages = ARReferenceImage.referenceImages(inGroupNamed: "Markers",
                              bundle: Bundle.main) {
        configuration.trackingImages = trackingImages
        configuration.maximumNumberOfTrackedImages = 1
    }

    sceneView.session.run(configuration)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    sceneView.session.pause()
}


func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
    let node = SCNNode()

    if anchor is ARImageAnchor {
        //my image is detected
        if let ballNode = self.ballNode {

            //for some reason changing the y position translate the ball in z direction
            //Positive y value moves it towards the screen (infront the marker)
            ballNode.position = SCNVector3(0.0, -0.02, 0.0)

            //Negative y value moves it away from the screen (behind the marker)
            ballNode.position = SCNVector3(0.0, -0.02, 0.0)
            node.addChildNode(ballNode)
        }
    }

    return node
}

如何使场景遵循z轴位置?换句话说,如何在使用ARKit框架检测到的图像/标记后显示3D模型在其后面?

我正在iOS 12上运行,使用Xcode 10.3。如果需要其他信息,请告诉我。

2个回答

2
要实现这一点,您需要在3D场景中创建一个遮挡器。由于ARReferenceImage具有physicalSize,因此在创建ARImageAnchor时向场景中添加几何体应该很简单。
该几何体将是一个SCNPlane,具有适用于遮挡器的SCNMaterial。我建议使用SCNLightingModelConstant照明模型(它是最便宜的,并且我们实际上不会绘制平面),并将colorBufferWriteMask设置为SCNColorMaskNone。该对象应该是透明的,但仍然在深度缓冲区中写入(这就是它作为遮挡器的工作原理)。
最后,请确保在任何增强对象之前呈现遮挡器,方法是将其renderingOrder设置为-1(如果应用程序已经使用渲染顺序,则可以设置一个更低的值)。

1
在ARKit 3.0中,苹果工程师实现了名为People Occlusion的ZDepth合成技术。由于它需要高度的处理器性能,因此此功能仅适用于A12和A13设备。目前,ARKit ZDepth合成功能还处于初级阶段,因此它只允许您将人物或类似人物的对象叠加在背景上方或下方,而不是通过后置摄像头看到的任何其他对象。而且,我想你知道前置TrueDepth相机 - 它用于面部跟踪,并具有额外的红外传感器来完成此任务。
要打开ZDepth合成功能,请在ARKit 3.0中使用这些实例属性:
var frameSemantics: ARConfiguration.FrameSemantics { get set }

static var personSegmentationWithDepth: ARConfiguration.FrameSemantics { get }

Real code should look like this:

let config = ARWorldTrackingConfiguration()

if let config = mySession.configuration as? ARWorldTrackingConfiguration {
    config.frameSemantics.insert(.personSegmentationWithDepth)
    mySession.run(config)
}

在 alpha 通道分割后,每个通道计算的公式如下所示:
r = Az > Bz ? Ar : Br
g = Az > Bz ? Ag : Bg
b = Az > Bz ? Ab : Bb
a = Az > Bz ? Aa : Ba
  • 其中 Az 是前景图像(3D 模型)的 ZDepth 通道
  • Bz 是背景图像(2D 视频)的 ZDepth 通道
  • Ar、Ag、Ab、Aa 分别代表 3D 模型的红色、绿色、蓝色和 Alpha 通道
  • Br、Bg、Bb、Ba 分别代表 2D 视频的红色、绿色、蓝色和 Alpha 通道

但是在早期版本的 ARKit 中没有 ZDepth 合成功能,因此你只能使用标准的 4 通道合成 OVER operation 将 3D 模型叠加在 2D 背景视频上。

(Argb * Aa) + (Brgb * (1 - Aa))
  • 其中Argb是前景A图像(3D模型)的RGB通道

  • Aa是前景A图像(3D模型)的Alpha通道

  • Brgb是背景B图像(2D视频)的RGB通道

  • (1 - Aa)是前景Alpha通道的反转

因此,如果不使用personSegmentationWithDepth属性,您的3D模型将始终位于2D视频之上。

因此,如果视频中的对象看起来不像人类的手或人体,使用常规ARKit工具时,您无法将该对象从2D视频放置在3D模型上方。

.....

尽管如此,您可以使用MetalAVFoundation框架来完成。请注意——这并不容易。

要从视频流中提取ZDepth数据,您需要以下实例属性:

// Works from iOS 11
var capturedDepthData: AVDepthData? { get }

或者您可以使用这两个实例方法(记住 ZDepth 通道必须是 32 位):

// Works from iOS 13
func generateDilatedDepth(from frame: ARFrame, 
                       commandBuffer: MTLCommandBuffer) -> MTLTexture

func generateMatte(from frame: ARFrame, 
                commandBuffer: MTLCommandBuffer) -> MTLTexture

如果您想了解如何使用Metal进行操作,请阅读this SO post

如需更多信息,请阅读this SO post


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接