歡迎來到第六回 ARKit 教學系列的文章!本週,我們將談談關於擴增實境 (Augmented Reality, AR) 中的圖像識別功能。自 iOS 11.3 起,ARKit 就能識別 2D 圖像。如果你有興趣學習製作 ARKit 圖像識別 App,這篇教學文章就很適合你了。
Many AR experiences can be enhanced by using known features of the user’s environment to trigger the appearance of virtual content. For example, a museum app might show a virtual curator when the user points their device at a painting, or a board game might place virtual pieces when the player points their device at a game board. In iOS 11.3 and later, you can add such features to your AR experience by enabling image recognition in ARKit: Your app provides known 2D images, and ARKit tells you when and where those images are detected during an AR session.
- Apple Documentation
前提條件
本篇教學需要你對於我們先前的 ARKit 教學文章有深入了解。如果你對於 ARKit 有點陌生,請先閱讀 ARKit 教學系列文章。
本次教學亦需要 9.3 或是更新版本的 Xcode,以及運行 iOS 11.3 或更高版本的蘋果裝置。
事不宜遲,我們開始吧!
你將會製作什麼 App
我們將製作一個 ARKit 圖像識別 App。這個 App 在任何時候偵測到可識別的圖像,就會執行一連串動畫來顯示圖像中物件的實際位置及大小,並以一個 UILabel 來顯示圖像的名稱。如果你不了解我的意思,下圖可以給你一些概念。
開始製作
首先,從這裡下載初始專案吧。初始專案已經有預先設置好的 UI 元件和 Action 方法,這樣我們就可以專注在 ARKit 圖像識別的核心功能。
程式設計實戰心法》 及更進階的 《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 就會顯示出來。
這樣更新 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 專案檔