前提条件
本篇教学需要你对于我们先前的 ARKit 教学文章有深入了解。如果你对于 ARKit 有点陌生,请先阅读 ARKit 教学系列文章。
本次教学亦需要 9.3 或是更新版本的 Xcode,以及运行 iOS 11.3 或更高版本的苹果装置。
事不宜迟,我们开始吧!
你将会制作什麽 App
我们将制作一个 ARKit 图像识别 App。这个 App 在任何时候侦测到可识别的图像,就会执行一连串动画来显示图像中物件的实际位置及大小,并以一个 UILabel 来显示图像的名称。如果你不了解我的意思,下图可以给你一些概念。
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!
开始制作
首先,从这裡下载初始专案吧。初始专案已经有预先设置好的 UI 元件和 Action 方法,这样我们就可以专注在 ARKit 图像识别的核心功能。
注意:如果你希望了解更多关于如何制作专业的 UI 元件,欢迎随时参阅 《iOS 11 App
程式设计实战心法》 及更进阶的 《iOS 11 App程式设计进阶攻略》。
下载了初始专案后,在你的 iOS 装置上 Build 及 Run。App 会要求允许使用你的相机,点击 OK 来允许相机权限。
好,现在让我们为 ARKit 图像识别功能准备些图片吧。
使 ARKit 可以进行图像识别
为了让 ARKit 识别图像,你先要提供两项东西:
- App 需要识别的图像
- 图像物件的实际大小
我们就从提供图像开始吧。在初始专案中,点击 Assets.xcassets。接著,你应该能够看到 AR Resources 群组。点击群组,你会看到裡面有三张图像。
你也可以拖曳自己的图片到这个群组之中。但一定要给图像一个描述性名称。
就如早前提到的,将图像放入专案中只是准备 ARKit 图像识别的第一步。此外,你还需要提供图像物件的实际大小。
下一个段落将谈谈关于图像物件的实际大小。
图像物件的实际大小
ARKit 需要知道图像物件的实际大小,来计算相机与图像之间的距离。物件大小不正确会导致 ARImageAnchor
错误计算物件与相机之间的距离。
记得在每次为 ARKit 识别添加新的图像时,都要提供图像物件的实际大小。这个数值应该反映出图像物件被测量时的大小。例如,”Book” 图像物件有以下的实际大小:
这是在一台 15.4 吋的 MacBook Pro 的 Preview 预览时,Book 图像物件的实际大小。你可以在图像的 Attributes Inspector 栏位中设定大小。
图像属性
ARKit 的图像识别能力可能会随图像的属性而改变。看看 AR Resources 群组裡的图像,你会看到 “Book” 图像有两个警告讯息。在添加图像时,请注意一下这些警告讯息。图像有高对比区块时,侦测效果会比较好。
“Snow Mountain” 和 “Trees In The Dark” 图像没有黄色警告,表示 ARKit 认为这些图像容易被识别。
但无论有或没有黄色警告也好,你最好还是先测试一下你打算使用的图像,从而测试出哪些图像是容易被识别的。
接下来,我们要开始动手写程式码囉。
为图像识别设定组态
我们将设定场景视图的组态 (Configuration) 来侦测 AR Resources 群组裡的图像。组态将须重设追踪,并移除现有的锚点运行选项。以组态执行了场景视图 Session 之后,我们就要更新 UILabel 上的文字描述。
开启 ViewController.swift
,然后插入以下的方法到 View Controller
类别:
func resetTrackingConfiguration() {
guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else { return }
let configuration = ARWorldTrackingConfiguration()
configuration.detectionImages = referenceImages
let options: ARSession.RunOptions = [.resetTracking, .removeExistingAnchors]
sceneView.session.run(configuration, options: options)
label.text = "Move camera around to detect images"
}
接下来,分别在 viewWillAppear(_:)
和 resetButtonDidTouch(_:)
方法中呼叫其 resetTrackingConfiguration()
方法。
以 ARImageAnchor 识别图像
现在,我们要以一个白色透明平面覆盖新侦测的图像。这个平面会反映出新侦测图像的形状、大小、和图像到相机间的距离。当一个新节点被映到给定的锚点时,这个平面覆盖 UI 就会显示出来。
注意: 锚点是 ARImageAnchor 的型态,并使用于 renderer(_:didAdd:for:) 方法中。
这样更新 renderer(_:didAdd:for:)
方法:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
let referenceImage = imageAnchor.referenceImage
let imageName = referenceImage.name ?? "no name"
let plane = SCNPlane(width: referenceImage.physicalSize.width, height: referenceImage.physicalSize.height)
let planeNode = SCNNode(geometry: plane)
planeNode.opacity = 0.20
planeNode.eulerAngles.x = -.pi / 2
planeNode.runAction(imageHighlightAction)
}
这个平面节点设定为执行一个 SCNAction
序列,此序列将执行一个淡入与淡出的动画效果。
现在有了这个平面节点和被侦测图像的名称,我们会将平面节点添加至节点参数,并设定 UILabel 的文字,以展示被识别图像的名称。在 planeNode.runAction(imageHighlightAction)
后面插入以下程式码:
node.addChildNode(planeNode)
DispatchQueue.main.async {
self.label.text = "Image detected: \"\(imageName)\""
}
好了!你已经成功建立了一个全新的 ARKit 图像识别 App。
测试示范 App
为了功能示范,你可以将所有 AR Resources 群组内的图像列印出来,或是在 Preview 中开启图片。
下一阶段,让我们将 3D 物件覆盖在被侦测的图像上吧。
覆盖 3D 物件到被侦测的图像上
我们已经将被侦测图像物件的实际大小和位置视觉化,现在来将 3D 物件覆盖在图像之上吧。
首先,为以下的程式码加入注解,让我们可以专注把 3D 物件覆盖在图像上:
let planeNode = self.getPlaneNode(withReferenceImage: imageAnchor.referenceImage)
planeNode.opacity = 0.0
planeNode.eulerAngles.x = -.pi / 2
planeNode.runAction(self.fadeAction)
node.addChildNode(planeNode)
接著,利用下列程式码取代 TODO: Overlay 3D Object 注解:
let overlayNode = self.getNode(withImageName: imageName)
overlayNode.opacity = 0
overlayNode.position.y = 0.2
overlayNode.runAction(self.fadeAndSpinAction)
node.addChildNode(overlayNode)
现在,在图像侦测的过程中,你会看到 SceneKit 节点执行一个动画,从图像向你的方向淡入、旋转、并淡出。
总结
恭喜你完成这次的教学内容!我希望你享受这次的教学,并从中学习到一些有价值的东西。欢迎分享本次教学给你的朋友,这样他们也可以从中获益!
最后,你可以在 Github 上 下载完整的 Xcode 专案档