前提条件

本篇教学需要你对于我们先前的 ARKit 教学文章有深入了解。如果你对于 ARKit 有点陌生,请先阅读 ARKit 教学系列文章

本次教学亦需要 9.3 或是更新版本的 Xcode,以及运行 iOS 11.3 或更高版本的苹果装置。

事不宜迟,我们开始吧!

你将会制作什麽 App

我们将制作一个 ARKit 图像识别 App。这个 App 在任何时候侦测到可识别的图像,就会执行一连串动画来显示图像中物件的实际位置及大小,并以一个 UILabel 来显示图像的名称。如果你不了解我的意思,下图可以给你一些概念。

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图1

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

开始制作

首先,从这裡下载初始专案吧。初始专案已经有预先设置好的 UI 元件和 Action 方法,这样我们就可以专注在 ARKit 图像识别的核心功能。

注意:如果你希望了解更多关于如何制作专业的 UI 元件,欢迎随时参阅 《iOS 11 App
程式设计实战心法》
及更进阶的 《iOS 11 App程式设计进阶攻略》

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图2

下载了初始专案后,在你的 iOS 装置上 Build 及 Run。App 会要求允许使用你的相机,点击 OK 来允许相机权限。

好,现在让我们为 ARKit 图像识别功能准备些图片吧。

使 ARKit 可以进行图像识别

为了让 ARKit 识别图像,你先要提供两项东西:

  1. App 需要识别的图像
  2. 图像物件的实际大小

我们就从提供图像开始吧。在初始专案中,点击 Assets.xcassets。接著,你应该能够看到 AR Resources 群组。点击群组,你会看到裡面有三张图像。

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图3

你也可以拖曳自己的图片到这个群组之中。但一定要给图像一个描述性名称。

就如早前提到的,将图像放入专案中只是准备 ARKit 图像识别的第一步。此外,你还需要提供图像物件的实际大小。

下一个段落将谈谈关于图像物件的实际大小。

图像物件的实际大小

ARKit 需要知道图像物件的实际大小,来计算相机与图像之间的距离。物件大小不正确会导致 ARImageAnchor 错误计算物件与相机之间的距离。

记得在每次为 ARKit 识别添加新的图像时,都要提供图像物件的实际大小。这个数值应该反映出图像物件被测量时的大小。例如,”Book” 图像物件有以下的实际大小:

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图4

这是在一台 15.4 吋的 MacBook Pro 的 Preview 预览时,Book 图像物件的实际大小。你可以在图像的 Attributes Inspector 栏位中设定大小。

图像属性

ARKit 的图像识别能力可能会随图像的属性而改变。看看 AR Resources 群组裡的图像,你会看到 “Book” 图像有两个警告讯息。在添加图像时,请注意一下这些警告讯息。图像有高对比区块时,侦测效果会比较好。

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图5

“Snow Mountain” 和 “Trees In The Dark” 图像没有黄色警告,表示 ARKit 认为这些图像容易被识别。

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图6

但无论有或没有黄色警告也好,你最好还是先测试一下你打算使用的图像,从而测试出哪些图像是容易被识别的。

接下来,我们要开始动手写程式码囉。

为图像识别设定组态

我们将设定场景视图的组态 (Configuration) 来侦测 AR Resources 群组裡的图像。组态将须重设追踪,并移除现有的锚点运行选项。以组态执行了场景视图 Session 之后,我们就要更新 UILabel 上的文字描述。

开启 ViewController.swift,然后插入以下的方法到 View Controller 类别:

  1. func resetTrackingConfiguration() {
  2. guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else { return }
  3. let configuration = ARWorldTrackingConfiguration()
  4. configuration.detectionImages = referenceImages
  5. let options: ARSession.RunOptions = [.resetTracking, .removeExistingAnchors]
  6. sceneView.session.run(configuration, options: options)
  7. label.text = "Move camera around to detect images"
  8. }

接下来,分别在 viewWillAppear(_:)resetButtonDidTouch(_:) 方法中呼叫其 resetTrackingConfiguration() 方法。

以 ARImageAnchor 识别图像

现在,我们要以一个白色透明平面覆盖新侦测的图像。这个平面会反映出新侦测图像的形状、大小、和图像到相机间的距离。当一个新节点被映到给定的锚点时,这个平面覆盖 UI 就会显示出来。

注意: 锚点是 ARImageAnchor 的型态,并使用于 renderer(_:didAdd:for:) 方法中。

这样更新 renderer(_:didAdd:for:) 方法:

  1. func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
  2. guard let imageAnchor = anchor as? ARImageAnchor else { return }
  3. let referenceImage = imageAnchor.referenceImage
  4. let imageName = referenceImage.name ?? "no name"
  5. let plane = SCNPlane(width: referenceImage.physicalSize.width, height: referenceImage.physicalSize.height)
  6. let planeNode = SCNNode(geometry: plane)
  7. planeNode.opacity = 0.20
  8. planeNode.eulerAngles.x = -.pi / 2
  9. planeNode.runAction(imageHighlightAction)
  10. }

这个平面节点设定为执行一个 SCNAction 序列,此序列将执行一个淡入与淡出的动画效果。

现在有了这个平面节点和被侦测图像的名称,我们会将平面节点添加至节点参数,并设定 UILabel 的文字,以展示被识别图像的名称。在 planeNode.runAction(imageHighlightAction) 后面插入以下程式码:

  1. node.addChildNode(planeNode)
  2. DispatchQueue.main.async {
  3. self.label.text = "Image detected: \"\(imageName)\""
  4. }

好了!你已经成功建立了一个全新的 ARKit 图像识别 App。

测试示范 App

为了功能示范,你可以将所有 AR Resources 群组内的图像列印出来,或是在 Preview 中开启图片。

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图7

下一阶段,让我们将 3D 物件覆盖在被侦测的图像上吧。

覆盖 3D 物件到被侦测的图像上

我们已经将被侦测图像物件的实际大小和位置视觉化,现在来将 3D 物件覆盖在图像之上吧。

首先,为以下的程式码加入注解,让我们可以专注把 3D 物件覆盖在图像上:

  1. let planeNode = self.getPlaneNode(withReferenceImage: imageAnchor.referenceImage)
  2. planeNode.opacity = 0.0
  3. planeNode.eulerAngles.x = -.pi / 2
  4. planeNode.runAction(self.fadeAction)
  5. node.addChildNode(planeNode)

接著,利用下列程式码取代 TODO: Overlay 3D Object 注解:

  1. let overlayNode = self.getNode(withImageName: imageName)
  2. overlayNode.opacity = 0
  3. overlayNode.position.y = 0.2
  4. overlayNode.runAction(self.fadeAndSpinAction)
  5. node.addChildNode(overlayNode)

现在,在图像侦测的过程中,你会看到 SceneKit 节点执行一个动画,从图像向你的方向淡入、旋转、并淡出。

iOS开发 · iOS音视频开发 - ARKit 教学:2D 图像识别功能 - 图8

总结

恭喜你完成这次的教学内容!我希望你享受这次的教学,并从中学习到一些有价值的东西。欢迎分享本次教学给你的朋友,这样他们也可以从中获益!

最后,你可以在 Github 上 下载完整的 Xcode 专案档

文末推荐:iOS热门文集