图像识别是很常用的AR功能!AR foundation 可以帮助我们轻松实现!
1.安装插件
首先还是在资源包中导入ARfoundation 。然后搭建基本的AR ARFoundation框架!
2.创建AR session 和XR origin结构!
3.然后在XR Origin 物体身上添加AR Tracker Image Manager组件!
这个组件主要负责实现目标图像的识别和追踪! 但是我们需要思考,识别那个图片,识别后让谁追踪图片!他的主要原理是,创建一个图像库Library(Serialized Library),把要被识别的图都放进去,将来所有在库里面的图都可以被单独识别!
然后,识别图片后,为了让我们能够看到追踪图片的视觉效果,AR Tracker Image Manager会为识别后的图像克隆一份Tracked Image Prefab 用于跟随识别到的图像一起运动(移动或者旋转)!如何创建一个Tracked Image Prefab ,我们后面再讲!
4.创建图像识别库
这个图像库,需要用户安装插件后,在Assets 仓库面板空白点右键-Create-XR-Reference Image Library ,然后点击创建的图像库,在右侧属性面板添加将来要被识别的图!
这些图将来都会可识别!
5.创建识别后追踪的预制体(UI、模型等)Tracked Image Prefab
这个预制体是图像识别专用的,因此需要挂载AR Tracked Image 组件!任何物体挂在了这个组件都可以作为识别图像后的追踪预制体!
那么我们自己的模型放在那里才可以追踪图片呢?只需要放在搭载AR Tracked Image 组件的父物体下面即可!
例如创建一个空物体,挂载AR Tracked Image 组件。把我们的模型放在这个空物体的子物体下面即可!
6. 把创建好可识别图像库以及追踪预制体赋值给 AR Tracker Image Manager组件!完毕
最后 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[0]);
DityprefabMap.Add("b", MyImagePre[1]);
DityprefabMap.Add("c", MyImagePre[2]);
DityprefabMap.Add("d", MyImagePre[3]);
DityprefabMap.Add("e", MyImagePre[4]);
}
// 当脚本或其所属的游戏对象变为可用时调用
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.referenceImage.name], trackedImage.transform.position, trackedImage.transform.rotation, trackedImage.transform);
// 更新UI显示已克隆的预制体名称
textdis2.text = "克隆了对象" + DityprefabMap[trackedImage.referenceImage.name].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跟踪图像不再被识别或跟踪时,它会安全地销毁与该图像关联的任何游戏对象实例,并清理字典中的记录,确保系统资源得到合理释放和管理。