涛声依旧在 发表于 2024-6-14 21:35:40

【ARFoundation自学04】AR Tracked Image 图像追踪辨认与对应类的调用

https://img-blog.csdnimg.cn/direct/0c68305d3648424da0bb789105f2a037.gif
图像辨认是很常用的AR功能!AR foundation 可以帮助我们轻松实现!
1.安装插件

起首照旧在资源包中导入ARfoundation 。然后搭建基本的AR ARFoundation框架!
2.创建AR session 和XR origin结构!

https://img-blog.csdnimg.cn/direct/87127d9f2805416db20e82ab04191a75.png
3.然后在XR Origin 物体身上添加AR Tracker Image Manager组件!

这个组件主要负责实现目标图像的辨认和追踪! 但是我们需要思考,辨认那个图片,辨认后让谁追踪图片!他的主要原理是,创建一个图像库Library(Serialized Library),把要被辨认的图都放进去,将来全部在库内里的图都可以被单独辨认!
然后,辨认图片后,为了让我们能够看到追踪图片的视觉效果,AR Tracker Image Manager会为辨认后的图像克隆一份Tracked Image Prefab 用于跟随辨认到的图像一起运动(移动或者旋转)!如何创建一个Tracked Image Prefab ,我们反面再讲!
https://img-blog.csdnimg.cn/direct/e931bfadfbd64ca29188b96c9c039c48.png
https://img-blog.csdnimg.cn/direct/efb6cba36e754a6892d43e5984d9d83d.png
4.创建图像辨认库

这个图像库,需要用户安装插件后,在Assets 堆栈面板空白点右键-Create-XR-Reference Image Library ,然后点击创建的图像库,在右侧属性面板添加将来要被辨认的图!
https://img-blog.csdnimg.cn/direct/b7d14456dd374bac9cfbd4e5faea33b3.png
https://img-blog.csdnimg.cn/direct/b41e9af33c1f44898c0abbd88c6dedef.png

这些图将来都会可辨认!
5.创建辨认后追踪的预制体(UI、模子等)Tracked Image Prefab  

这个预制体是图像辨认专用的,因此需要挂载AR Tracked Image 组件!任何物体挂在了这个组件都可以作为辨认图像后的追踪预制体!
那么我们自己的模子放在那里才可以追踪图片呢?只需要放在搭载AR Tracked Image 组件的父物体下面即可!
比方创建一个空物体,挂载AR Tracked Image 组件。把我们的模子放在这个空物体的子物体下面即可!
https://img-blog.csdnimg.cn/direct/e28b4da62a52459ea21dcabde994c54a.png
https://img-blog.csdnimg.cn/direct/462643e877e748cca6e647f91950ea20.png
6. 把创建好可辨认图像库以及追踪预制体赋值给 AR Tracker Image Manager组件!完毕

https://img-blog.csdnimg.cn/direct/05e7acb9b3304444a14531330855bb76.png
最后 AR Tracker Image Manager组件!另有个属性Max Number Of Moving Image 是最大可跟踪的动态图像数量!
7.其他参数先容

图像跟踪技术,是指通过图像处置惩罚技术对摄像头中拍摄到的2D图像进行定位,并对其姿态进行跟踪的技术。图像跟踪技术的基础是图像辨认,图像辨认是指辨认和检测出数字图像或视频中对象或特性的技术,图像辨认技术是信息时代的一门重要技术,其产生目的是为了让计算机代替人类去处置惩罚大量的图形图像及真实物体信息,因而成为其他许多重要技术的基

[*] 最大并发跟踪数(Max Simultaneous Tracked Images):
指定同时能够被辨认和跟踪的图像数量。由于图像跟踪较为耗能,限定此数值可以避免性能标题。
    2. 参考图像(Reference Image)    


[*]这些是您盼望AR应用能够辨认的特定2D图像。比如,一张海报或标志。您需要事先在Unity项目中定义这些图像作为资源。别2D图像的过程实际是一个特性值对比的过程,ARFoundation将从摄像头中获取的图像信息与参考图像库的图像特性点信息进行对比,存储在参考图像库中的用于对比的图像就叫做参考图像。一旦对比成功,真实环境中的图像将与参考图像库的参考图像建立对应关系,每一个真实2D图像的姿态信息也一并被检测。
      3.参考图像库(Reference Image Library)    :
参考图像库用来存储一系列的参考图像用于对比,每一个图像跟踪程序都必须有一个参考图像库,但需要注意的是,参考图像库中存储的实际是参考图像的特性值信息而不是原始图像,这有助于进步对比速率与鲁棒性。参考图像库越大,图像对比就会越慢,发起参考图像库的图像不要超过1000张。跟踪组件提供方(Provider)    ARFoundation是架构在底层SDK图像跟踪API之上的,也即是说ARFoundation并不具体负责图像辨认过程的算法,它只提供一个接口,具体图像辨认由算法提供方提供。
8.动态修改TrackedImageManager的状态:


[*]在代码中,你可以访问TrackedImageManager实例并根据需要动态改变其行为。比方,你可以暂时禁用全部图像的跟踪,或者针对特定图像进行开关控制 public class ImageTrackingController : MonoBehaviour
{
    public TrackedImageManager trackedImageManager;
   
    public void DisableImageTracking()
    {
      if (trackedImageManager != null)
      {
            trackedImageManager.enabled = false; // 关闭图像跟踪
      }
    }

    public void EnableImageTracking()
    {
      if (trackedImageManager != null)
      {
            trackedImageManager.enabled = true; // 启用图像跟踪
      }
    }
} 8.多图像跟踪
对于多图像跟踪,你可能不只盼望实例化一个Prefab,而是根据辨认到的差别图像实例化差别的Prefab。这时,传统的方式是为每个图像在数据集中指定对应的Prefab。但ARFoundation本身并不直接支持每个图像直接关联差别Prefab,因此需要一些额外的编程逻辑来实现这一功能。
实现逻辑

编写自定义脚本:
创建一个C#脚本,用于处置惩罚图像辨认后的逻辑。在这个脚本中,你需要监听TrackedImageUpdated事件,该事件会在图像被辨认或跟踪状态改变时触发。
using System.Collections; // 引入集合相关的命名空间,用于列表(List)和字典(Dictionary)等数据结构
using System.Collections.Generic;
using UnityEngine; // 引入Unity引擎的基本功能
using UnityEngine.XR.ARSubsystems; // 引入AR相关子系统的命名空间
using UnityEngine.XR.ARFoundation; // 引入ARFoundation命名空间,提供AR功能的支持
using TMPro; // 引入TextMeshPro文本组件支持

public class ImageTrackControl : MonoBehaviour // 定义一个新的类,继承自MonoBehaviour,这是Unity中脚本的基础类
{
    // 公开变量,用于在Inspector面板中指定图像识别的核心组件
    public ARTrackedImageManager MyImageManagerCom;

    // 定义字符串变量,用于存储当前状态信息
    string myString;

    // 引用TextMeshProUGUI组件,用于在界面上显示文本信息
    public TextMeshProUGUI textdis;
    public TextMeshProUGUI textdis2;

    // 创建一个列表,用来存储预先准备好的图像追踪预制体
    public List<GameObject> MyImagePre = new List<GameObject>();

    // 使用Dictionary存储每个跟踪图的ID与其对应的预制体GameObject
    public Dictionary<string, GameObject> DityprefabMap = new Dictionary<string, GameObject>();

    // 临时对象变量,用于存储实例化后的游戏对象
    GameObject tempobj;

    // 当此脚本所属的游戏对象被唤醒时调用
    private void Awake()
    {
      // 确保MyImageManagerCom有被指定,若未指定,则尝试从该脚本所挂载的游戏对象上获取ARTrackedImageManager组件
      if (MyImageManagerCom == null)
      {
            MyImageManagerCom = this.transform.GetComponent<ARTrackedImageManager>();
      }

      // 初始化字典,将图像名称与预制体关联起来
      // 注意:这里假设MyImagePre列表已经被正确填充了相应的预制体
      DityprefabMap.Add("a", MyImagePre);
      DityprefabMap.Add("b", MyImagePre);
      DityprefabMap.Add("c", MyImagePre);
      DityprefabMap.Add("d", MyImagePre);
      DityprefabMap.Add("e", MyImagePre);
    }

    // 当脚本或其所属的游戏对象变为可用时调用
    private void OnEnable()
    {
      // 绑定ARTrackedImageManager的trackedImagesChanged事件,当跟踪到的图像发生变化时会调用MyImChanged方法
      if (MyImageManagerCom != null)
      {
            MyImageManagerCom.trackedImagesChanged += MyImChanged;
      }
    }

    // 当脚本或其所属的游戏对象变为不可用时调用
    private void OnDisable()
    {
      // 解绑ARTrackedImageManager的trackedImagesChanged事件,防止内存泄漏或在对象不可用时继续接收事件
      if (MyImageManagerCom != null)
      {
            MyImageManagerCom.trackedImagesChanged -= MyImChanged;
      }
    }

    // 自定义事件处理方法,响应ARTrackedImageManager的trackedImagesChanged事件
    private void MyImChanged(ARTrackedImagesChangedEventArgs obj)
    {
      // 输出日志,表明图像跟踪状态已改变
      Debug.Log("追踪的图像发生变化了,调用委托函数");

      // 遍历新增的图像
      foreach (var trackedImage in obj.added)
      {
            // 更新UI显示新增的图像名称
            textdis.text = "新增图像: " + trackedImage.referenceImage.name;

            // 根据图像的名称从字典中获取对应的预制体,并在该图像的位置和旋转状态下实例化
            tempobj = Instantiate(DityprefabMap, trackedImage.transform.position, trackedImage.transform.rotation, trackedImage.transform);

            // 更新UI显示已克隆的预制体名称
            textdis2.text = "克隆了对象" + DityprefabMap.name;
      }

      // 下面注释掉的部分原本用于处理更新和移除的图像,可以根据需要启用并实现相应逻辑
      // ...
      
      // 遍历移除的图像,这里只是记录了移除的信息,实际操作如销毁相关游戏对象的逻辑可以根据需求添加
      foreach (var trackedImage in obj.removed)
      {
            myString = "移除图像: " + trackedImage.referenceImage.name;
            Debug.Log("移除图像: " + trackedImage.name);
      }
    }
} 下面是一些关于上面代码中核心类和方法的详细解释:

没有调用过ARTrackedImageManager 类下面的相关方法,尤其是 trackedImagesChanged,他是什么意思呢,什么作用? 
trackedImagesChanged 是 ARFoundation 中 ARTrackedImageManager 类的一个事件(可以去复习C#中的委托和事件的利用)。这个事件在图像跟踪状态发生改变时被触发!就会调用这个事件委托的函数(自己定义的)
在程序中如何利用呢?
trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged; 此中 trackedImageManager 是AR Tracked Image Manager 组件的对应类变量。存储了你场景中的一个AR Tracked Image Manager 组件!
   += OnTrackedImagesChanged;
这个意思是这个  trackedImageManager.trackedImagesChanged ,组件调用了一个委托事件trackedImagesChanged(代表辨认的物体有变革,不管是位置、旋转、照旧内容都算),一旦有变革我就委托一个函数去实行一个事情。trackedImageManager.trackedImagesChanged+=OnTrackedImagesChanged;
OnTrackedImagesChanged;就是一个自己定义的函数,被委托了!下面是这个函数的尺度定义
    private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        //具体事情
    }
函数内里有个形式参数,这个参数是必须带的,就像我让你去办一件事,去把车门打开,我得给你钥匙,这个钥匙就是形式参数 ARTrackedImagesChangedEventArgs eventArgs!
ARTrackedImagesChangedEventArgs eventArgs
ARTrackedImagesChangedEventArgs 是 ARFoundation 提供的一个类,它作为参数传递给 ARTrackedImageManager 的 trackedImagesChanged 事件处置惩罚方法。这个类封装了在图像跟踪状态发生改变时的全部相关信息,包括哪些图像被新增、哪些被更新以及哪些被移除。通过分析 eventArgs 参数,开发者可以得知当前跟踪图像聚集的具体变革情况,并据此做出相应的逻辑处置惩罚,比如实例化或销毁游戏对象、更新UI、实行特定动画等。
ARTrackedImagesChangedEventArgs 类包含三个主要的属性:

[*] added: 这是一个 NativeArray<ARTrackedImage> 范例的聚集,包含了全部新被辨认和开始跟踪的图像。每一个 ARTrackedImage 对象代表一个被跟踪的图像,包含了图像的 ID、巨细、位置、旋转等信息。
[*] updated: 同样是 NativeArray<ARTrackedImage> 范例,包含了全部已跟踪图像中状态有所更新的图像(比如位置、姿态的微小变动)。开发者可以通过这些信息来实时调整已存在的AR内容,确保与实际跟踪状态同步。
[*] removed: 包含了 NativeArray<ARTrackedImage> 范例的聚集,表现那些不再被跟踪的图像,可能是由于图像离开视野、遮挡或是其他缘故原由导致跟踪丢失。对于这些图像,开发者通常需要清算与其关联的任何游戏对象或资源,以避免内存走漏或视觉混乱。
在实际应用中,通过检查和相应 eventArgs 的这三个聚集,开发者可以机动地管理AR体验中的图像跟踪逻辑,包管应用的动态性和相应性。
完备示例代码:
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class ImageTrackingEventHandler : MonoBehaviour
{
    public ARTrackedImageManager trackedImageManager;

    void Start()
    {
      // 确保ARTrackedImageManager组件已经附加到了某个游戏对象上
      if (trackedImageManager == null)
      {
            trackedImageManager = GetComponent<ARTrackedImageManager>();
      }

      // 订阅trackedImagesChanged事件
      if (trackedImageManager != null)
      {
            trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged;
      }
    }

    // 当跟踪的图像集合发生变化时,此方法会被调用
    private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
      // 遍历新增的图像
      foreach (var trackedImage in eventArgs.added)
      {
            Debug.Log("新增图像: " + trackedImage.name);
            // 这里可以添加实例化预设体或其他处理逻辑
      }

      // 遍历更新的图像
      foreach (var trackedImage in eventArgs.updated)
      {
            Debug.Log("更新图像: " + trackedImage.name);
            // 可以在这里根据需要更新图像相关联的游戏对象状态
      }

      // 遍历移除的图像
      foreach (var trackedImage in eventArgs.removed)
      {
            Debug.Log("移除图像: " + trackedImage.name);
            // 这里可以添加销毁相关联游戏对象的逻辑
      }
    }

    void OnDestroy()
    {
      // 当这个脚本被销毁时,取消订阅事件,防止内存泄漏
      if (trackedImageManager != null)
      {
            trackedImageManager.trackedImagesChanged -= OnTrackedImagesChanged;
      }
    }
} 9. 再来一份代码,实现图像不在被追踪是清除克隆的Image prefab
using UnityEngine; // 引入Unity基本库,提供游戏对象、组件、物理等基础功能
using UnityEngine.XR.ARFoundation; // 引入ARFoundation库,用于AR开发的核心功能
using UnityEngine.XR.ARSubsystems; // 引入AR子系统,提供低级AR支持
using System.Collections.Generic; // 引入通用集合类型,如List、Dictionary等

public class TrackedImageHandler : MonoBehaviour // 定义一个Unity组件类,可以挂载到游戏对象上
{
    public ARTrackedImageManager trackedImageManager; // 公开的ARTrackedImageManager组件引用,用于管理图像跟踪
    public GameObject imagePrefab; // 公开的GameObject预设体,当识别到图像时将克隆这个预设体

    private Dictionary<XRCameraImageId, GameObject> trackedObjects = new Dictionary<TrackableId, GameObject>(); // 私有的字典,用于存储跟踪图像ID与对应实例化游戏对象的关系

    void Start() // Unity生命周期方法,当游戏对象启动时调用
    {
      if (trackedImageManager == null) // 检查是否已经指定了trackedImageManager
      {
            trackedImageManager = FindObjectOfType<ARTrackedImageManager>(); // 如果没有指定,则尝试在场景中自动查找ARTrackedImageManager组件
      }

      if (trackedImageManager != null) // 确保找到了ARTrackedImageManager
      {
            trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged; // 注册事件处理器,当跟踪图像变化时调用OnTrackedImagesChanged方法
      }
      else
      {
            Debug.LogError("ARTrackedImageManager not found in the scene."); // 如果找不到ARTrackedImageManager,打印错误信息
      }
    }

    void OnDestroy() // Unity生命周期方法,在游戏对象被销毁前调用
    {
      if (trackedImageManager != null) // 确保trackedImageManager仍然有效
      {
            trackedImageManager.trackedImagesChanged -= OnTrackedImagesChanged; // 解除事件注册,避免内存泄漏
      }
    }

    void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs) // 处理图像跟踪状态变化的事件方法
    {
      foreach (var trackedImage in eventArgs.removed) // 遍历移除的图像
      {
            GameObject trackedObject;
            if (trackedObjects.TryGetValue(trackedImage.trackableId, out trackedObject)) // 尝试从字典中获取关联的GameObject
            {
                Destroy(trackedObject); // 销毁关联的GameObject
                trackedObjects.Remove(trackedImage.trackableId); // 从字典中移除该条目
            }
      }

      foreach (var trackedImage in eventArgs.added) // 遍历新增的图像
      {
            if (!trackedObjects.ContainsKey(trackedImage.trackableId)) // 确保这个图像还没有被处理过
            {
                GameObject newObject = Instantiate(imagePrefab, trackedImage.transform.position, trackedImage.transform.rotation); // 实例化预设体,并放置在图像的位置和旋转
                trackedObjects.Add(trackedImage.trackableId, newObject); // 记录新实例化对象与图像ID的关联
            }
      }

      foreach (var trackedImage in eventArgs.updated) // 遍历更新的图像(位置或状态改变)
      {
            GameObject trackedObject;
            if (trackedObjects.TryGetValue(trackedImage.trackableId, out trackedObject)) // 获取关联的GameObject
            {
                trackedObject.transform.position = trackedImage.transform.position; // 更新游戏对象的位置
                trackedObject.transform.rotation = trackedImage.transform.rotation; // 更新游戏对象的旋转
            }
      }
    }
} 其他部分读者根据前面的理解应该能看懂!
解释一下此中的 if (trackedObjects.TryGetValue(trackedImage.trackableId, out trackedObject)) { Destroy(trackedObject); trackedObjects.Remove(trackedImage.trackableId); }

这段代码的作用是从一个字典(trackedObjects)中查找与特定跟踪图像ID(trackedImage.trackableId)关联的游戏对象,并在找到后进行销毁和从字典中移除的利用。让我逐步解释每一部分:

[*] if (trackedObjects.TryGetValue(trackedImage.trackableId, out trackedObject)):

[*]TryGetValue 是 Dictionary 类的一个方法,用于实验从字典中获取与指定键(这里是 trackedImage.trackableId)相关联的值(即游戏对象 GameObject)。
[*]方法有两个参数:键(key,这里是 trackedImage.trackableId)和一个输出参数(out parameter),用于接收查找到的值。如果找到了对应的键值对,这个值会被输出到 out 参数中;如果没有找到,输出参数会被赋予范例的默认值(对于引用范例,如 GameObject,默认值是 null)。
[*]这行代码的意思是实验从 trackedObjects 字典中查找 trackedImage.trackableId 对应的 GameObject,并将找到的对象赋值给 trackedObject 变量。如果找到,TryGetValue 方法会返回 true,让 if 条件成立,实行后续代码。

[*] Destroy(trackedObject);:

[*]如果 trackedObject 不为 null(即字典中找到了与之关联的游戏对象),Destroy 函数会被调用,用来销毁这个 GameObject 实例。在Unity中,Destroy 方法用于清算游戏对象及其全部的子对象,释放它们占用的资源。

[*] trackedObjects.Remove(trackedImage.trackableId);:

[*]在销毁了游戏对象之后,这行代码从 trackedObjects 字典中移除与 trackedImage.trackableId 关联的条目。这样做是为了保持字典的整洁,避免保存不再利用的键值对,也有助于内存管理。

综上所述,这段代码块旨在处置惩罚图像跟踪移除事件,当一个AR跟踪图像不再被辨认或跟踪时,它会安全地销毁与该图像关联的任何游戏对象实例,并清算字典中的纪录,确保系统资源得到公道释放和管理。
课堂代码示例(重点讲解了委托函数以及事件回调):
 

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

public class ImageARmanager : MonoBehaviour
{
    // 检测用户图片识别的状态,
    public ARTrackedImageManager SMImageARMg;
    public ARFaceManager SMImfACEARMg;
    public GameObject muban;

    public UnityEvent OneUserHanshu;
   
    //public UnityEvent oneCallvbackevent;
   private void OnEnable()
    {
      SMImageARMg.trackedImagesChanged += SMImageARMg_trackedImagesChanged1; ;
      SMImfACEARMg.facesChanged += SMImfACEARMg_facesChanged;
    }

    private void SMImageARMg_trackedImagesChanged1(ARTrackedImagesChangedEventArgs obj)
    {
      
    }

    private void SMImfACEARMg_facesChanged(ARFacesChangedEventArgs obj)
    {
      foreach (var oneImage in obj.added)
      {
            Debug.Log("人脸换了");
            OneUserHanshu.Invoke();
      }
    }

    private void SMImageARMg_trackedImagesChanged(ARTrackedImagesChangedEventArgs obj)
    {

      foreach (var oneImage in obj.added)
      {
            Debug.Log("有新图片进入摄像头");
            GameObject.Instantiate(muban, oneImage.transform.position, oneImage.transform.rotation, oneImage.transform);
            OneUserHanshu.Invoke();
      }
      foreach (var oneImage in obj.removed)
      {
            Debug.Log("有图片离开摄像头");


      }   

    }
   
    private void OnDisable()
    {
      SMImageARMg.trackedImagesChanged -= responsive;//卸载这个委托节省性能
    }
}


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【ARFoundation自学04】AR Tracked Image 图像追踪辨认与对应类的调用