在AR中使用视觉分析

在AR中使用视觉分析
管理视觉资源用以增强Core ML图片分类器的执行效率,同时使用SpriteKit在AR中展示图片分类器的结果.

Demo下载地址

概述
上面的Demo运行在现实追踪的ARKit session中,同时在SpriteKit视图中展示内容。Demo中使用Vision库给Core ML分类器模型传递相机图片。在屏幕角落展示分类识别结果,判断分类器是否识别物体。在分类器对一个图片产生了分类结果后,用户可以点击屏幕去标记文字到AR虚拟坐标空间.

运行AR Session和处理相机图片
在ViewController类中处理所有操作。ARKit通过相机抓取视屏帧和传递它们到 session(_:didUpdate:) 方法中,然后调用classifyCurrentImage()方法去运行Vision图片分类器。

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    // Do not enqueue other buffers for processing while another Vision task is still running.
    // The camera stream has only a finite amount of buffers available; holding too many buffers for analysis would starve the camera.
    guard currentBuffer == nil, case .normal = frame.camera.trackingState else {
        return
    }
    
    // Retain the image buffer for Vision processing.
    self.currentBuffer = frame.capturedImage
    classifyCurrentImage()
}


执行实时的连续图片处理
classifyCurrentImage()方法使用视图控制器currentBuffer属性去监测Vision当前正在处理的一张图片是否在另一个Vision任务开始之前。

// Most computer vision tasks are not rotation agnostic so it is important to pass in the orientation of the image with respect to device.
let orientation = CGImagePropertyOrientation(UIDevice.current.orientation)

let requestHandler = VNImageRequestHandler(cvPixelBuffer: currentBuffer!, orientation: orientation)
visionQueue.async {
    do {
        // Release the pixel buffer when done, allowing the next buffer to be processed.
        defer { self.currentBuffer = nil }
        try requestHandler.perform([self.classificationRequest])
    } catch {
        print("Error: Vision request failed with error \"\(error)\"")
    }
}

确保同时只有一个buffer被处理分析,这样可以保证好的执行。相机能回收有限的像素buffer池,所以持有太多的buff去处理分析可能会导致相机异常和关闭capture session。传递多个buffer到Vision中,可能降低处理每个图像的处理进度,增加等待时间和降低CPU和GPU渲染AR视觉开销。

另外,我们可以在代码中设置 usesCPUOnly 属性来处理Vision请求,这样可以释放GPU来处理渲染。

在AR中显示结果
processClassifications(for:error:) 方法中保存了最匹配的结果标签。设置两个主要的步骤可以让用户在AR场景中去标记真实世界的位置。

  • 第一步,点击手势会触发 placeLabelAtLocation(sender:)方法,在这个方法中我们使用ARKit的 hitTest(_:types:) 方法去预估真实世界的position,同时在那个position增加一个锚点给AR Session。
@IBAction func placeLabelAtLocation(sender: UITapGestureRecognizer) {
    let hitLocationInView = sender.location(in: sceneView)
    let hitTestResults = sceneView.hitTest(hitLocationInView, types: [.featurePoint, .estimatedHorizontalPlane])
    if let result = hitTestResults.first {
        
        // Add a new anchor at the tap location.
        let anchor = ARAnchor(transform: result.worldTransform)
        sceneView.session.add(anchor: anchor)
        
        // Track anchor ID to associate text with the anchor after ARKit creates a corresponding SKNode.
        anchorLabels[anchor.identifier] = identifierString
    }
}
  • 第二步,在ARKit自动地为最新增加的锚点创建了SpriteKit节点后,view(_:didAdd:for:)代理方法提供了节点内容。
func view(_ view: ARSKView, didAdd node: SKNode, for anchor: ARAnchor) {
    guard let labelText = anchorLabels[anchor.identifier] else {
        fatalError("missing expected associated label for anchor")
    }
    let label = TemplateLabelNode(text: labelText)
    node.addChild(label)
}

 

你可能感兴趣的:(iOS)