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> /// All url of images that have allready been downloaded /// </summary> private List<string> m_allDownloadedImages; /// <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 /// </summary> 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_allDownloadedImages = new List<string>(); m_trackedImageManager.trackedImagePrefab = (GameObject)Resources.Load("ARFImageTrackingPrefab"); m_trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged; } /// <summary> /// found /// </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]; } } return null; } /// <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 if (!found && url.Length == 0) { Debug.LogWarning("Try to Track Image " + fileName + " but file not found"); return false; } if (!found) { // Do not download the same image twice if (!m_allDownloadedImages.Contains(url)) { // 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> private async void LoadTextureFromURL(string url, string fileName, float imageWidthInMeters) { Debug.Log("Download image from url "+ url); m_allDownloadedImages.Add(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; } }