iOS AR開發(fā)基礎(chǔ)03 | Hello World (基于AR的平面檢測(cè)、人臉檢測(cè)和物體識(shí)別功能)

本篇寫一個(gè) AR demo,demo包含三個(gè)部分的內(nèi)容:

  • 基于后置攝像頭的平面檢測(cè)
  • 基于前置攝像頭的人臉追蹤
  • 基于后置攝像頭的圖像識(shí)別

寫在前面

本文在一個(gè)項(xiàng)目中實(shí)現(xiàn)引言部分提到的三個(gè)AR功能,由于是一個(gè)AR 的 Hello World 項(xiàng)目,本文只編寫實(shí)現(xiàn) AR 功能的核心代碼,和 3D 渲染相關(guān)的內(nèi)容本文只展示代碼,不會(huì)詳細(xì)講解,后面的文章會(huì)做系統(tǒng)的講解。

本文內(nèi)容結(jié)構(gòu)如下:

本文內(nèi)容結(jié)構(gòu)

本文源碼地址。

1. 搭建第一個(gè)AR項(xiàng)目

1.1 搭建過程

本文的開發(fā)環(huán)境:Xcode9.3 + iPhone X真機(jī) iOS 11.3。

打開Xcode > Create a new Xcode project > Augmented Reality App > next,如下圖:

新建項(xiàng)目-1

應(yīng)用名稱命名為 HelloWorld,開發(fā)語言選swift,Content Technology 選擇 SceneKit,然后點(diǎn)擊下一步:

新建項(xiàng)目-2

1.2 項(xiàng)目結(jié)構(gòu)和默認(rèn)添加的源碼解讀

打開剛剛新建的工程,和Single View App模板相比,使用 Augmented Reality App 模板新建的工程,初始化內(nèi)容有如下差異:

  • 添加了 art.scnassets 資源文件夾,里面放著資源文件,打開 ship.scn 文件,是一個(gè)3D的飛船模型
  • 打開 Main.storyboard ,默認(rèn)給啟動(dòng)頁的 ViewController對(duì)象 添加了一個(gè)ARSCNView實(shí)例
  • ViewController.swift 文件中添加了一些添加3D飛船模型的代碼

現(xiàn)在直接在真機(jī)上 run 這個(gè)項(xiàng)目,效果如下:

項(xiàng)目演示

Amazing,飛船渲染在我們的真實(shí)世界中了,看看是怎么通過代碼加載進(jìn)來的。

1. 查看 main.storyboard,發(fā)現(xiàn)系統(tǒng)為我們的 ViewController 實(shí)例的 view 添加了一個(gè)ARSCNView類型的subview,并將其設(shè)置為ViewController的屬性。

    @IBOutlet var sceneView: ARSCNView!

2. 查看 ViewController.swift 的 viewDidLoad: 方法:

sceneView.delegate = self

這行代碼給 sceneView 設(shè)置 ARSCNViewDelegate 代理,在ViewController 中就可以獲取 sceneView 的渲染狀態(tài)回調(diào)。

sceneView.showsStatistics = true

showsStatistics 是 SCNView 的一個(gè)性能統(tǒng)計(jì)屬性,設(shè)置為 true 之后,sceneView 底部就會(huì)顯示一個(gè)sceneView 的性能統(tǒng)計(jì)狀態(tài)欄,點(diǎn)擊上面的加號(hào)之后,這個(gè)狀態(tài)欄會(huì)展開,上面的 gif 有展示這個(gè)過程。

// Create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!
      
 // Set the scene to the view       
sceneView.scene = scene

這兩行代碼從我們資源文件夾 art.scnassets 中讀取資源文件 ship.scn ,把這個(gè)文件轉(zhuǎn)換為一個(gè)名為 scene 的 SCNScene 實(shí)例,然后將這個(gè)場(chǎng)景設(shè)置為 sceneView 的 scene 屬性。
這樣我們加載這個(gè)包含飛船的場(chǎng)景到真實(shí)世界中。

3. 查看 ViewController.swift 的 viewWillAppear: 方法,在視圖即將出現(xiàn)的時(shí)候,初始化一個(gè) ARWorldTrackingConfiguration 實(shí)例 configuration ,然后用這個(gè)configuration 運(yùn)行 ARSession對(duì)象。

// Create a session configuration
let configuration = ARWorldTrackingConfiguration()

// Run the view's session
sceneView.session.run(configuration)

4. 查看 ViewController.swift 的 viewWillDisappear:方法,在視圖消失的時(shí)候,停止這個(gè)session,和 session.run 成對(duì)出現(xiàn)。

 // Pause the view's session
 sceneView.session.pause()

但是!我們發(fā)現(xiàn)這個(gè)飛船是在viewDidLoad的時(shí)候加載的,并沒有融入對(duì)世界理解,也沒有交互!

看完系統(tǒng)默認(rèn)添加的代碼之后,在編寫AR 代碼之前,我們先給我們 Demo 搭建一個(gè)簡(jiǎn)單的視圖框架。

1.3 利用storyboard快速構(gòu)建Demo視圖層級(jí)

新建三個(gè)ViewController,繼承自UIViewController,每一個(gè)控制器代表一個(gè)功能:

  • TKWorldTrackingViewController 負(fù)責(zé)實(shí)現(xiàn)平面檢測(cè)功能
  • TKFaceTrackingViewController 負(fù)責(zé)實(shí)現(xiàn)人臉檢測(cè)相關(guān)功能
  • TKImageRecognizeViewController 負(fù)責(zé)實(shí)現(xiàn)物體識(shí)別相關(guān)功能

并利用storyboard快速構(gòu)建整個(gè)如圖層級(jí),關(guān)于storyboard的使用,本教程不做闡述,完成之后如下如所示:

利用storyboard快速構(gòu)建demo視圖層級(jí)

此時(shí),準(zhǔn)備完畢,下面正式編寫AR代碼!??!

2. 開發(fā) World Tracking 功能

首先在 TKWorldTrackingViewController 中引入 ARKit。

import ARKit

添加sceneView屬性,用來展示AR視圖。

 var sceneView : SCNView!

viewDidLoad: 中初始化 sceneView,并作為 subview 添加到view上。

 override func viewDidLoad() {
        super.viewDidLoad()

        sceneView = SCNView(frame: view.bounds)
        view.addSubview(sceneView)
        
    }

sceneView 已經(jīng)初始化完成,現(xiàn)在需要運(yùn)行 sceneView 的 AR會(huì)話,我們希望在 當(dāng)前 view 出現(xiàn)的時(shí)候運(yùn)行會(huì)話。在 viewWillAppear:中創(chuàng)建一個(gè)ARWorldTrackingConfiguration 實(shí)例 configuration ,然后用 configuration 運(yùn)行 AR session。

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
      
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = .horizontal
        sceneView.session.run(configuration)
    }

在當(dāng)前 view 消失的時(shí)候,在viewWillDisappear:中停止AR session。

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Pause the view's session
        sceneView.session.pause()
    }

到目前為止,AR session 已經(jīng)成功運(yùn)行了,接下來要做的就是,在檢測(cè)到平面之后,接收并處理ARKit發(fā)出來的通知。

實(shí)現(xiàn)思路如下:

  • 上一遍文章關(guān)于世界追蹤的描述中有提到過,ARKit 檢測(cè)到平面后,會(huì)在場(chǎng)景中添加錨點(diǎn)
  • 遵守 sceneView 的 ARSCNViewDelegate ,實(shí)現(xiàn) renderer(: didAdd: for:)方法,當(dāng)場(chǎng)景中添加錨點(diǎn)的時(shí)候,viewcontroller 就可以收到通知
  • 判斷新添加錨點(diǎn)的類型,如果是 ARPlaneAnchor 類型,就認(rèn)為檢測(cè)到平面了

TKWorldTrackingViewController 遵守 ARSCNViewDelegate。

class TKWorldTrackingViewController: UIViewController,ARSCNViewDelegate

然后在viewDidLoad:中添加如下代碼:

sceneView.delegate = self

并實(shí)現(xiàn)代理方法,判斷當(dāng)前新增的錨點(diǎn)類型,如果是 ARPlaneAnchor,就在當(dāng)前錨點(diǎn)出添加一個(gè) box。

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
     // 1. 判斷當(dāng)前新增的錨點(diǎn)類型
     guard anchor is ARPlaneAnchor else { return }        

     // 2. 在檢測(cè)到的平面處添加 box
     let box = SCNBox(width: 0.08, height: 0.08, length: 0.08, chamferRadius: 0)
     let boxNode = SCNNode(geometry: box)
     node.addChildNode(boxNode)
    
}

此時(shí),代碼寫完了,在真機(jī)上 run 項(xiàng)目,點(diǎn)擊 “Demo1:平面檢測(cè)” 按鈕,效果如下:

World Tracking 平面檢測(cè)演示

3. 開發(fā) Face Tracking 功能

TKFaceTrackingViewController 中引入 ARKit、添加 sceneView 屬性、在 viewDidLoad: 中初始化 sceneView 的代碼,和上一節(jié)“開發(fā) World Tracking 功能”中一樣。

運(yùn)行 session 代碼如下:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
  
        let configuration = ARFaceTrackingConfiguration()
        sceneView.session.run(configuration)
    }

獲取人臉和添加box方法和上一節(jié)講的類似,代碼如下:

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        
        // 1. 判斷anchor是否為ARFaceAnchor
        guard anchor is ARFaceAnchor else { return }
        
        // 2. 在檢測(cè)到的人臉處添加 box
        let box = SCNBox(width: 0.08, height: 0.08, length: 0.08, chamferRadius: 0)
        let boxNode = SCNNode(geometry: box)
        node.addChildNode(boxNode)
    }

在真機(jī)上 run 項(xiàng)目,點(diǎn)擊 “Demo2:人臉檢測(cè)” 按鈕,效果如下:

Face Tracking演示

4. 開發(fā)基于AR的圖像識(shí)別功能

4.1 將需要識(shí)別的2D圖片導(dǎo)入到項(xiàng)目中

在 Assets.xcassets 文件目錄下新建一個(gè) AR Resource Group 類型的目錄。


新建AR Resource Group.gif

然后將要識(shí)別的對(duì)象,對(duì)應(yīng)的2D圖片拖拽到如下圖片中的紅框位置。

將要識(shí)別的2D圖片添加到project中

接下來的步驟很重要,查看圖片的 Show the Attributes inspector,給圖片設(shè)置大小,這個(gè)值是我們需要識(shí)別的物體在真實(shí)世界中的大?。。?!

這個(gè)值的精度直接決定了識(shí)別效果。

經(jīng)過測(cè)量,我需要識(shí)別的企鵝,高度約為13cm,這里的單位選 Meters,設(shè)置如下。

企鵝高度參數(shù)設(shè)置

4.2 加載上面導(dǎo)入的圖片

TKImageRecognizeViewController 中引入 ARKit、添加 sceneView 屬性、在 viewDidLoad: 中初始化 sceneView 的代碼,和前面“開發(fā) World Tracking 功能”中一樣。

運(yùn)行 session 代碼稍有變化。

ARReferenceImage 提供的 referenceImages(:)方法可以導(dǎo)入項(xiàng)目中 AR Resources 文件夾下的所有圖片,如果項(xiàng)目中沒有這個(gè)文件,會(huì)拋出異常。在viewWillApper: 中添加如下代碼。

    guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else {
            fatalError("AR Resources 資源文件不存在 。")
        }

接著,新建一個(gè)ARWorldTrackingConfiguration實(shí)例,將 referenceImages 賦給 detectionImages屬性。

        let configuration = ARWorldTrackingConfiguration()
        configuration.detectionImages = referenceImages

用上面的 configuration 運(yùn)行AR 會(huì)話。

sceneView.session.run(configuration)

4.3 添加圖像識(shí)別代碼

renderer(: didAdd: for:)方法中處理圖像識(shí)別結(jié)果的回調(diào),在識(shí)別到圖像的位置添加一個(gè)平面,做識(shí)別結(jié)果可視化標(biāo)識(shí)。

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        // 1. 判斷anchor是否為ARImageAnchor
        guard let imageAnchor = anchor as? ARImageAnchor else { return }
        
        // 2. 在檢測(cè)到的物體圖像處添加 plane
        let referenceImage = imageAnchor.referenceImage
        let plane = SCNPlane(width: referenceImage.physicalSize.width,
                             height: referenceImage.physicalSize.height)
        
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = -.pi / 2
        
        // 3. 將plane添加到檢測(cè)到的圖像錨點(diǎn)處
        node.addChildNode(planeNode)  
    }

在真機(jī)上 run 項(xiàng)目,點(diǎn)擊 “Demo3:物體識(shí)別” 按鈕,效果如下:

物體識(shí)別演示

至此,我們完成了關(guān)于我們第一個(gè)AR項(xiàng)目,接下來會(huì)圍繞SceneKit,系統(tǒng)的介紹和 3D 內(nèi)容渲染相關(guān)的內(nèi)容。

感謝
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容