ARFoundation之路-圖像跟蹤

版權聲明:Davidwang原創文章,嚴禁用於任何商業途徑,授權後方可轉載。

  圖像跟蹤技術,是指通過圖像處理技術對攝像頭中拍攝到的2D圖像進行定位,並對其姿態進行跟蹤的技術。圖像跟蹤技術的基礎是圖像識別,圖像識別是指識別和檢測出數字圖像或視頻中對象或特徵的技術,圖像識別技術是信息時代的一門重要技術,其產生目的是爲了讓計算機代替人類去處理大量的圖形圖像及真實物體信息,因而成爲其他許多重要技術的基礎。

(一)圖像跟蹤基本操作

  在ARFoundation中,圖像跟蹤系統依據參考圖像爲庫的圖像信息嘗試在周圍環境中檢測到匹配的2D圖像並跟蹤之,在ARFoundation的圖像跟蹤處理中,有一些特定的術語如下表所示。

術語 描述
參考圖像(Reference Image) 識別2D圖像的過程實際是一個特徵值對比的過程,ARFoundation將從攝像頭中獲取的圖像信息與參考圖像庫的圖像特徵點信息進行對比,存儲在參考圖像庫中的用於對比的圖像就叫做參考圖像。一旦對比成功,真實環境中的圖像將與參考圖像庫的參考圖像建立對應關係,每一個真實2D圖像的姿態信息也一併被檢測。
參考圖像庫(Reference Image Library) 參考圖像庫用來存儲一系列的參考圖像用於對比,每一個圖像跟蹤程序都必須有一個參考圖像庫,但需要注意的是,參考圖像庫中存儲的實際是參考圖像的特徵值信息而不是原始圖像,這有助於提高對比速度與魯棒性。參考圖像庫越大,圖像對比就會越慢,建議參考圖像庫的圖像不要超過1000張。
跟蹤組件提供方(Provider) ARFoundation是架構在底層SDK圖像跟蹤API之上的,也即是說ARFoundation並不具體負責圖像識別過程的算法,它只提供一個接口,具體圖像識別由算法提供方提供。

  在ARFoundation中,圖像跟蹤的操作使用分成兩步,第一步是建立一個參考圖像庫,第二步是在場景中掛載AR Tracked Image Manager組件,並將一個需要實例化的Prefab賦給其Tracked Image Prefab即可,下面我們來具體操作。

  按上述步驟,在Unity中新建一個工程,第一步建立一個參考圖像庫,首先在Project窗口中的ImageLib文件夾下點擊鼠標右鍵並依次選擇Create->XR->Reference Image Library新建一個參考圖像庫,並命名爲RefImageLib,如下圖所示。

在這裏插入圖片描述
  選擇新建的RefImageLib參考圖像庫,在Inspector窗口中,點擊“Add Image”添加參考圖像,將參考圖像拖到圖像框中,如下圖所示。

在這裏插入圖片描述
  在上圖中,每一個參考圖像除了圖片信息外都有若干屬性,其具體含義見下表。

屬性 描述
Name 一個標識參考圖像的名字,這個名字在做圖像對比時沒有作用,但在比對匹配成功後我們可以通過參考圖像名字獲知是哪個參考圖像,參考圖像名字是可以重複的,因爲在跟蹤時,跟蹤系統還會給每一個參考圖像一個referenceImage.guid值,這個值是唯一的。
Specify Size 這是個可選值,爲加速圖像檢測識別過程,一些底層SDK要求提供一個2D待檢測圖像的物理尺寸,所以如果要設置,這個值一定會是一個大於0的長寬值對,當一個值發生變化時,Unity會根據參考圖像的比例自動調整另一個值。
Keep Texture at Runtime 一個默認的紋理,這個紋理可以用於修改Prefab的外觀。

  在完成上述工作之後,在Hierarchy窗口中選擇AR Session Origin,併爲其掛載AR Tracked Image Manager組件,將第一步製作的RefImageLib參考圖像庫拖到Reference Library屬性中,並設置相應的Prefab,如下圖所示。

在這裏插入圖片描述
  參考圖像庫也可以在運行時添加,但AR Tracked Image Manager一開始跟蹤圖像,參考圖像庫就不能爲null。Max Number of Moving Images屬性指定了最大的可跟蹤的動態圖像數,因爲動態圖像跟蹤是一個非常消耗CPU性能的任務,過多的動態圖像跟蹤會導致應用卡頓。最後需要說明的是,動態圖像跟蹤與底層SDK的算法有非常大的關係,不同的底層對跟蹤圖像有不同的要求,對指定平臺具體應用需要參閱該平臺的SDK資料。ARFoundation目前不支持動態添加參考圖像,這對很多圖像跟蹤類應用是比較大的限制。

  編譯運行,效果如下圖所示,在Android上進行測試,觀察實例化的虛擬對象,發現模型在Y軸上旋轉了180度,這個是在實例化時ARCore底層SDK導致的,爲解決這個問題,在ARCore中實例化虛擬時需要在Y軸再旋轉180度,但在ARFoundation中,實例化虛擬物體是由AR Tracked Image Manager自動完成的,無法干預,所以目前在ARFoundation中解決這個問題只有把模型進行180度旋轉再使用。

在這裏插入圖片描述

(二)圖像跟蹤啓用與禁用

  在ARFoundation中,實例化出來的虛擬對象並不會隨着被跟蹤物體的消失而消失,而是會繼續停留在原來的位置上,這有時就會變得很不合適。而且,圖像跟蹤是一個非常消耗性能的操作,在不使用圖像跟蹤時一定要把圖像跟蹤功能關閉。參考平面檢測功能的關閉與啓用,類似的我們可以編寫如下代碼來控制圖像跟蹤的啓用與禁用以及所跟蹤對象的顯示與隱藏。

    public Text m_TogglePlaneDetectionText;
    private ARTrackedImageManager mARTrackedImageManager;
    void Awake()
    {
        mARTrackedImageManager = GetComponent<ARTrackedImageManager>();
    }

    #region 啓用與禁用圖像跟蹤
    public void ToggleImageTracking()
    {
        mARTrackedImageManager.enabled = !mARTrackedImageManager.enabled;

        string planeDetectionMessage = "";
        if (mARTrackedImageManager.enabled)
        {
            planeDetectionMessage = "禁用圖像跟蹤";
            SetAllImagesActive(true);
        }
        else
        {
            planeDetectionMessage = "啓用圖像跟蹤";
            SetAllImagesActive(false);
        }

        if (m_TogglePlaneDetectionText != null)
            m_TogglePlaneDetectionText.text = planeDetectionMessage;
    }

    void SetAllImagesActive(bool value)
    {
        foreach (var img in mARTrackedImageManager.trackables)
            img.gameObject.SetActive(value);
    }
    #endregion

  與平面檢測開啓與關閉類似,我們使用一個按鈕來控制,運行效果如下圖所示。

在這裏插入圖片描述

(三)多圖像跟蹤

  在AR Tracked Image Manager組件中,有一個Tracked Image Prefab屬性,這個屬性即爲需要實例化的虛擬對象。默認,ARFoundation是支持多圖像跟蹤的,如下圖所示。
在這裏插入圖片描述
  但在AR應用運行時,只能有AR Tracked Image Manager組件運行(多個AR Tracked Image Manager組件會導致跟蹤衝突),即只能設置一個Tracked Image Prefab,即不能實例化多個虛擬對象,這將極大的限制跟蹤圖像的實際應用,所以爲了實例化多個虛擬對象,我們只能動態的修改Tracked Image Prefab屬性。經過測試,我們發現在ARFoundation中,AR Tracked Image Manager組件在trackedImagesChanged事件觸發之前就已經實例化了虛擬對象,因此的解決思路是:在AR Tracked Image Manager組件Tracked Image Prefab中設置第一個需要實例化的Prefab,然後在trackedImagesChanged事件裏捕捉到圖像added操作,更改Tracked Image Prefab爲下一個需要實例化的Prefab,這樣來達到動態調整虛擬對象的目的。如正常設置Tracked Image Prefab爲Spider Prefab,在檢測到Spider圖像後,我們將Tracked Image Prefab修改爲Cat Prefab,這樣,再檢測到Cat圖像後就會實例化Cat Prefab了。具體代碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class MultiImageTracking : MonoBehaviour
{
    ARTrackedImageManager ImgTrackedmanager;
    public GameObject[] ObjectPrefabs;

    private void Awake()
    {
        ImgTrackedmanager = GetComponent<ARTrackedImageManager>();
    }


    private void OnEnable()
    {
        ImgTrackedmanager.trackedImagesChanged += OnTrackedImagesChanged;
    }
    void OnDisable()
    {
        ImgTrackedmanager.trackedImagesChanged -= OnTrackedImagesChanged;
    }
    void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        foreach (var trackedImage in eventArgs.added)
        {
            OnImagesChanged(trackedImage.referenceImage.name);
        }
       // foreach (var trackedImage in eventArgs.updated)
       // {
       //     OnImagesChanged(trackedImage.referenceImage.name);
       // }
    }

    private void OnImagesChanged(string referenceImageName)
    {
        if (referenceImageName == "Spider")
        {           
            ImgTrackedmanager.trackedImagePrefab = ObjectPrefabs[1];
            Debug.Log("Tracked Name is .." + referenceImageName);
            Debug.Log("Prefab Name is .." + ImgTrackedmanager.trackedImagePrefab.name);
        }
        if (referenceImageName == "Cat")
        {
            ImgTrackedmanager.trackedImagePrefab = ObjectPrefabs[0];
        }
    }
}

  編譯運行,效果如下圖所示。
在這裏插入圖片描述
在這裏插入圖片描述

  讀者可能已經看到,這種方式其實有個很大的弊端,即必須要按順序檢測圖像,因爲我們無法在用戶檢測圖像之前預測用戶可能會檢測的2D圖像。爲解決這個問題,就不能讓AR Tracked Image Manager組件實例化對象,而由我們自己負責虛擬對象的實例化。將AR Tracked Image Manager組件下的Tracked Image Prefab屬性清空,爲MultiImageTracking腳本的ObjectPrefabs數組賦上相應的值,並編寫如下代碼。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class MultiImageTracking : MonoBehaviour
{
    ARTrackedImageManager ImgTrackedmanager;
    public GameObject[] ObjectPrefabs;

    private void Awake()
    {
        ImgTrackedmanager = GetComponent<ARTrackedImageManager>();
    }

    private void OnEnable()
    {
        ImgTrackedmanager.trackedImagesChanged += OnTrackedImagesChanged;
    }
    void OnDisable()
    {
        ImgTrackedmanager.trackedImagesChanged -= OnTrackedImagesChanged;
    }
    void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        foreach (var trackedImage in eventArgs.added)
        {
            OnImagesChanged(trackedImage);
        }
       // foreach (var trackedImage in eventArgs.updated)
       // {
       //     OnImagesChanged(trackedImage.referenceImage.name);
       // }
    }

    private void OnImagesChanged(ARTrackedImage referenceImage)
    {
        if (referenceImage.referenceImage.name == "Spider")
        {
            Instantiate(ObjectPrefabs[0], referenceImage.transform);
        }
        if (referenceImage.referenceImage.name == "Cat")
        {
            Instantiate(ObjectPrefabs[1], referenceImage.transform);
        }
    }
}

  在上述代碼中,我們根據參考圖像的名字在被檢測圖像的位置實例化虛擬對象,實現了我們的要求,不再要求用戶按順序掃描圖像。但現在又有一個新問題,我們不可能使用if-else或者switch-case語句遍歷所有可能的模型,因此我們改用動態加載模型的方式,編寫代碼如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class MultiImageTracking : MonoBehaviour
{
    ARTrackedImageManager ImgTrackedManager;
    private Dictionary<string, GameObject> mPrefabs =  new Dictionary<string, GameObject>();

    private void Awake()
    {
        ImgTrackedManager = GetComponent<ARTrackedImageManager>();
    }

    void Start()
    {        
        mPrefabs.Add("Cat", Resources.Load("Cat") as GameObject);
        mPrefabs.Add("Spider", Resources.Load("Spider") as GameObject);
    }

    private void OnEnable()
    {
        ImgTrackedManager.trackedImagesChanged += OnTrackedImagesChanged;
    }
    void OnDisable()
    {
        ImgTrackedManager.trackedImagesChanged -= OnTrackedImagesChanged;
    }
    void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        foreach (var trackedImage in eventArgs.added)
        {
            OnImagesChanged(trackedImage);
        }
       // foreach (var trackedImage in eventArgs.updated)
       // {
       //     OnImagesChanged(trackedImage.referenceImage.name);
       // }
    }

    private void OnImagesChanged(ARTrackedImage referenceImage)
    {
        Debug.Log("Image name:"+ referenceImage.referenceImage.name);
        Instantiate(mPrefabs[referenceImage.referenceImage.name], referenceImage.transform);
    }
}

  爲了實現代碼所描述功能,我們還要完成兩項工作,第一項工作是將Prefabs放到Resources文件夾中方便動態加載,第二項工作是保證mPrefabs中的key與RefImageLib參考圖像庫中的參考圖像名一致。至此,我們已實現自由的多圖像多模型功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章