Skip to content
Snippets Groups Projects
WorldAnalysisARFoundationModuleImage.cs 10.3 KiB
Newer Older
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using System.IO;
using Unity.XR.CoreUtils;
using static WorldAnalysisARFoundationModule;
using ETSI.ARF.OpenAPI.WorldStorage;

public class WorldAnalysisARFoundationModuleImage : WorldAnalysisARFoundationModule
{
    /// <summary>
    /// ARFoundation Image tracking management
    /// </summary>
    private ARTrackedImageManager m_trackedImageManager;
    /// <summary>
    /// Name of all images that have been added to the library
    /// </summary>
    private List<string> m_trackedImageInLibrary; 
    /// <summary>
    /// List of tracked images with tracking infos
    /// </summary>
    private Dictionary<string,TrackableInfo> m_trackedImages = new Dictionary<string,TrackableInfo>();
    /// <summary>
    /// Link between trackable name and uuid
    private Dictionary<Guid, string> m_uuidToName  = new Dictionary<Guid, string >();

    /// <summary>
    /// Initialize image tracking module
    /// </summary>
    public void Initialize()
    {
        XROrigin origin = UnityEngine.Object.FindAnyObjectByType<XROrigin>();
        m_trackedImageManager = origin.gameObject.AddComponent<ARTrackedImageManager>();
        m_trackedImageInLibrary = new List<string>();
        m_trackedImageManager.trackedImagePrefab = (GameObject)Resources.Load("ARFImageTrackingPrefab");
        m_trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged;
    }

    /// <summary>
    /// <param name="uuid">name of the image trackable</param>
    public TrackableInfo GetPoseTrackable(Guid uuid)
        if (m_uuidToName.ContainsKey(uuid))
            string name = m_uuidToName[uuid];
            if (m_trackedImages.ContainsKey(name))
            {
                return m_trackedImages[name];
            }
    }

    /// <summary>
    /// Add a trackable : need to be an image
    /// </summary>
    /// <param name="trackable">Image trackable</param>
    /// <returns>Supported or not</returns>
    public bool AddTrackable(Trackable trackable)
    {
        /// Here : we don't check if the trackable is allready added, AddImageToLibrary does it
        if (trackable.TrackableType != ETSI.ARF.OpenAPI.WorldStorage.TrackableType.IMAGE_MARKER)
        {
            return false;
        }

        // Check if an image url is provided
        string url = "" ;
        if (trackable.KeyvalueTags.ContainsKey("url"))
        {
            foreach(string s in trackable.KeyvalueTags["url"])
            {
                // first one
                url = s ;
                break ;
            }
        }

        bool resul = AddImageToLibrary(trackable.Name, url, (float)trackable.TrackableSize[0]);
        if (resul)
        {
            m_uuidToName[trackable.UUID] = trackable.Name;
        }
        return resul;
    }

    /// <summary>
    ///  Initialize capability object with image tracking
    /// </summary>
    public ETSI.ARF.OpenAPI.WorldAnalysis.Capability  GetSupportedCapability()
    {
        ETSI.ARF.OpenAPI.WorldAnalysis.Capability capabilityImage = new ETSI.ARF.OpenAPI.WorldAnalysis.Capability();
        capabilityImage.TrackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.IMAGE_MARKER;
        ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructure encodingInformation = new ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructure();
        encodingInformation.DataFormat = ETSI.ARF.OpenAPI.WorldAnalysis.EncodingInformationStructureDataFormat.OTHER;
        encodingInformation.Version = "1.01";
        capabilityImage.EncodingInformation = encodingInformation;
        capabilityImage.Framerate = 30; // Not particularly specified on ARKit and ARCore
        capabilityImage.Latency = 0; // Not particularly specified on ARKit and ARCore
        capabilityImage.Accuracy = 1; // Not particularly specified on ARKit and ARCore
        return capabilityImage;
    }

    /// <summary>
    /// Event manager for when an image is tracked
    /// </summary>
    /// <param name="eventArgs">the event</param>
    private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    {
        foreach (var trackedImage in eventArgs.updated)
        {
            Vector3 position = trackedImage.transform.position;
            Quaternion rotation = trackedImage.transform.rotation;

            TrackableInfo info = new TrackableInfo();
            info.name = trackedImage.referenceImage.name;
            if (trackedImage.trackingState == TrackingState.Tracking)
            {
                info.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.OK;
                info.confidence = 100;
            }
            else if (trackedImage.trackingState == TrackingState.Limited)
            {
                info.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.OK;
                info.confidence = 50;
            }
            else
            {
                info.state = ETSI.ARF.OpenAPI.WorldAnalysis.PoseEstimationState.FAILURE; //ADD NOT_TRACKED ?
                info.confidence = 0;
            }
            info.timeStamp = unchecked((int)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
            info.position = position;
            info.rotation = rotation;
            info.trackableType = ETSI.ARF.OpenAPI.WorldAnalysis.TrackableType.IMAGE_MARKER;
            m_trackedImages[info.name] = info;
        }
    }

    /// <summary>
    /// Add a new image to the arfoundation library
    /// </summary>
    /// <param name="fileName">file name</param>
    /// </param name="url"> potential url</param>
    /// <param name="imageWidthInMeters">image width in meters</param>
    /// <returns>image found or not</returns>
    protected bool AddImageToLibrary(string fileName, string url, float imageWidthInMeters)
    {
        // check if image is in the library
        if (m_trackedImageInLibrary.Contains(fileName))
        {
            Debug.Log("Image allready added to library");
            return true;
        }
#if UNITY_EDITOR
        string imagePath = Application.streamingAssetsPath + "/" + fileName;
#else
        string imagePath = Application.persistentDataPath  + "/" + fileName ;
#endif

        bool found = CheckImageExist(ref imagePath); // try to find in jpg or png
        {
            Debug.LogWarning("Try to Track Image " + fileName + "  but file not found");
            return false; 
        }

        if (!found)
        {
            // Here we don't check if url exists and still return true: could be improve
            LoadTextureFromURL(url, fileName, imageWidthInMeters);
        }
        else 
        {
            //Load texture and add it to arfoundation library
            LoadTextureFromMemory(imagePath, fileName, imageWidthInMeters);
        }
        return true;
    }

    /// <summary>
    /// Add a new image from local memory to the arfoundation library
    /// </summary>
    /// </param name="imagePath"> local image path</param>
    /// <param name="fileName">file name</param>
    /// <param name="imageWidthInMeters">image width in meters</param>
    private void LoadTextureFromMemory(string imagePath, string fileName, float imageWidthInMeters)
    {
        var rawData = File.ReadAllBytes(imagePath);
        Texture2D tex = new Texture2D(2, 2);
        tex.LoadImage(rawData);
        AddTextureToLibrary(tex, fileName, imageWidthInMeters);
    }

    /// <summary>
    /// Add a new image from a given url to the arfoundation library
    /// </summary>
    /// </param name="url"> url to the image to download</param>
    /// <param name="fileName">file name</param>
    /// <param name="imageWidthInMeters">image width in meters</param>
    public async void LoadTextureFromURL(string url, string fileName, float imageWidthInMeters)
    {
        Debug.Log("Download image from url "+ url);
        KeyValuePair<string , string> downloaded = await WorldAnalysisARFoundationHelper.DownloadFileHTTP(url);
        LoadTextureFromMemory(downloaded.Key, fileName, imageWidthInMeters);
    }

    /// <summary>
    /// Add a texture2D to the list of tracked image of the ARFoundation image library
    /// </summary>
    /// <param name="imageToAdd">texture to track</param>
    /// <param name="imageName">name of the trackable</param>
    /// <param name="imageWidthInMeters">with of the image in meters</param>
    private async void AddTextureToLibrary(Texture2D imageToAdd, string imageName, float imageWidthInMeters)
    {
        while (ARSession.state == ARSessionState.CheckingAvailability  || ARSession.state == ARSessionState.None)
        {
            await System.Threading.Tasks.Task.Delay(100);
        }
        IReferenceImageLibrary library;
        if (m_trackedImageManager.referenceLibrary == null)
        {
            library = m_trackedImageManager.CreateRuntimeLibrary(); // Create library if it does not exist
        }
        else
        {
            library = m_trackedImageManager.referenceLibrary;
        }

        if (library is MutableRuntimeReferenceImageLibrary mutableLibrary)
        {
            if (!m_trackedImageInLibrary.Contains(imageName))
            {
                mutableLibrary.ScheduleAddImageWithValidationJob(imageToAdd, imageName, imageWidthInMeters); // check if that does not fail?
                m_trackedImageInLibrary.Add(imageName);
            }
        }
        else
        {
            Debug.LogError("Error adding image: Library is not mutableLibrary");
        }
        m_trackedImageManager.referenceLibrary = library;
        if (!m_trackedImageManager.enabled) m_trackedImageManager.enabled = true; // Necessary?
    }

    /// <summary>
    /// Check if image exist in jpg or png format and modify string with path
    /// </summary>
    /// <param name="path">path to check without extension</param>
    /// <returns>image exists or not</returns>
    private bool CheckImageExist(ref string path)
    {
        string tempPath = path + ".png";
        if (File.Exists(tempPath))
        {
            path = tempPath;
            return true; 
        }
        else
        {
            tempPath = path + ".jpg";
            if (File.Exists(tempPath))
            {
                path = tempPath;
                return true; 
            }
        }
        return false; 
    }
}