ImageTracking_CloudRecognition

演示如何使用云识别功能。

  • 演示如何创建和使用本地缓存

  • 演示如何在跟踪时停止网络请求

运行前配置

使用云识别需配置服务器访问信息,这些信息可以在EasyAR官网的开发中心中 云识别管理页面 中获得。在Unity中输入这些信息有两种方法:

一种是全局配置,所有使用全局配置的云识别场景都会使用这个配置。从Unity菜单中选择<EasyAR -> Sense -> Configuration>并在Inspector面板中输入从开发中心获取的信息。

../../_images/image_61.png ../../_images/image_61_1.png

另一种是在场景中配置,它只对当前场景有效。

../../_images/image_61_2.png

用法

../../_images/image_211.png
标记1:显示系统状态和操作提示。
标记2:结束/开始云识别。
标记3:清空当前云识别的Targets和缓存。

详解

触发云识别

从EasyAR Sense 4.1开始,所有 CloudRecognizer 的请求都由用户代码触发,SDK内部将不再触发从服务器发送和接收数据的调用。也就是说你可以自由控制请求的频次。

CloudRecognizer.Resolve(iFrame, (result) => {});

最低请求间隔限制为300ms。

周期性地触发云识别

建议在 ARSession.FrameChange 事件处理中触发云识别,这个事件会在frame图像数据变化的时候发生。可以在事件中获取 InputFrame

public void StartAutoResolve(float resolveRate)
{
    ...
    Session.FrameChange += AutoResolve;
    ...
}

private void AutoResolve(OutputFrame oframe, Matrix4x4 displayCompensation)
{
    ...
    using (var iFrame = oframe.inputFrame())
    {
        CloudRecognizer.Resolve(iFrame, (result) => {});
    }
}

在这个sample中,如果target在被跟踪或是前一次的resolve未完成或时间间隔未到达预设值(1s)的时候,resolve不会被调用。

private void AutoResolve(OutputFrame oframe, Matrix4x4 displayCompensation)
{
    var time = Time.time;
    if (isTracking || resolveInfo.Running || time - resolveInfo.ResolveTime < autoResolveRate)
    {
        return;
    }

    resolveInfo.ResolveTime = time;
    resolveInfo.Running = true;
    ...
}

云识别结果

云识别是EasyAR的独立功能,可以在不与图像跟踪一起使用。如果在识别之后不需要跟踪,直接使用resolve结果即可。

CloudRecognizer.Resolve(iFrame, (result) =>
{
    ...
    resolveInfo.CloudStatus = result.getStatus();
    ...
    var target = result.getTarget();
    ...
});

跟踪云返回的target

如果需要跟踪服务器识别到的图像,就需要用到result中的target信息。

在这个sample中target被clone了一份,因为它会在ImageTargetController中被引用,因此需要保留一份内部object的引用。

CloudRecognizer.Resolve(iFrame, (result) =>
{
    ...
    var target = result.getTarget();
    if (target.OnSome)
    {
        using (var targetValue = target.Value)
        {
            ...
            LoadCloudTarget(targetValue.Clone());
        }
    }
});

创建一个有 ImageTargetControllerGameObject ,并将resolve回调中的 ImageTarget 赋给 ImageTargetController.TargetSource ,这样controller可以使用 ImageTargetController.DataSource.Target 来初始化。

private void LoadCloudTarget(ImageTarget target)
{
    ...
    var go = new GameObject(uid);
    targetObjs.Add(go);
    var targetController = go.AddComponent<ImageTargetController>();
    targetController.SourceType = ImageTargetController.DataSource.Target;
    targetController.TargetSource = target;
    LoadTargetIntoTracker(targetController);

    targetController.TargetLoad += (loadedTarget, result) =>
    {
        ...
        AddCubeOnTarget(targetController);
    };

    ...
}

没有必要多次加载同一个target,并且这样做也会失败。

if (!loadedCloudTargetUids.Contains(targetValue.uid()))
{
    LoadCloudTarget(targetValue.Clone());
}

使用离线缓存

一个好的实践是,将识别到的target保持在文件存储中,以便在下次启动时加载。这会对下次应用启动时的识别有好处,可以减少响应时间,而且可以在离线状态下使用识别过的target。

可以使用 ImageTarget.save 将target保存为 .etd 文件。

target.save(OfflineCachePath + "/" + target.uid() + ".etd")

然后可以在下次运行时通过 ImageTargetController.DataSource.TargetDataFile 类型的数据使用 .etd 文件创建target。

private void LoadOfflineTarget(string file)
{
    var go = new GameObject(Path.GetFileNameWithoutExtension(file) + "-offline");
    targetObjs.Add(go);
    var targetController = go.AddComponent<ImageTargetController>();
    targetController.SourceType = ImageTargetController.DataSource.TargetDataFile;
    targetController.TargetDataFileSource.PathType = PathType.Absolute;
    targetController.TargetDataFileSource.Path = file;
    LoadTargetIntoTracker(targetController);

    targetController.TargetLoad += (loadedTarget, result) =>
    {
        ...
        loadedCloudTargetUids.Add(loadedTarget.uid());
        cachedTargetCount++;
        AddCubeOnTarget(targetController);
    };
}