/// <summary> /// Updates the display in the editor if <see cref="ArucoObject" /> has been changed. /// </summary> protected virtual void Update() { #if UNITY_EDITOR if (!EditorApplication.isPlayingOrWillChangePlaymode) { if (lastArucoObjectOnValidate != ArucoObject) { if (ArucoObject != null) { var currentArucoObject = ArucoObject; arucoObject = lastArucoObjectOnValidate; SetArucoObject(currentArucoObject); } else { ResetImage(); } lastArucoObjectOnValidate = ArucoObject; } // The Aruco Object may initialize after the displayer, so we can display the image the frame after if (ArucoObject != null && Image == null) { UpdateImage(); } // Keep the image plane at the same position if (ImagePlane != null) { PlaceImagePlane(); } } #endif }
/// <summary> /// Remove an ArUco object to the <see cref="ArucoObjects"/> list. /// </summary> /// <param name="arucoObject">The ArUco object to remove.</param> public virtual void Remove(ArucoObject arucoObject) { // Find the list with the same dictionary than the ArUco object to remove Dictionary <int, ArucoObject> arucoObjectsCollection = null; foreach (var arucoObjectDictionary in ArucoObjects) { if (arucoObjectDictionary.Key.Name == arucoObject.Dictionary.Name || arucoObjectDictionary.Key == arucoObject.Dictionary) { arucoObjectsCollection = arucoObjectDictionary.Value; break; } } if (arucoObjectsCollection == null) { throw new ArgumentException("Can't remove the ArUco object: not found.", "arucoObject"); } // Remove the ArUco object arucoObjectsCollection.Remove(arucoObject.ArucoHashCode); ArucoObjectRemoved(arucoObject); // Unsuscribe to property events on the aruco object arucoObject.PropertyUpdating -= ArucoObject_PropertyUpdating; arucoObject.PropertyUpdated -= ArucoObject_PropertyUpdated; // If the list is empty, remove it with its dictionary if (arucoObjectsCollection.Count == 0) { ArucoObjects.Remove(arucoObject.Dictionary); DictionaryRemoved(arucoObject.Dictionary); } }
/// <summary> /// Place and orient an ArUco object. /// </summary> protected void PlaceArucoObject(ArucoObject arucoObject, Cv.Core.Vec3d rvec, Cv.Core.Vec3d tvec, int cameraId, float positionFactor = 1f) { GameObject arucoGameObject = arucoObject.gameObject; Camera camera = arucoTracker.ArucoCamera.ImageCameras[cameraId]; // Place and orient the object to match the marker Transform arucoObjectTransform = arucoGameObject.transform; arucoGameObject.transform.SetParent(camera.transform); arucoGameObject.transform.localPosition = tvec.ToPosition() * positionFactor; arucoGameObject.transform.localRotation = rvec.ToRotation(); arucoGameObject.transform.SetParent(arucoObjectTransform); // Adjust the object position Vector3 cameraOpticalCenter = arucoTracker.ArucoCamera.CameraParameters.OpticalCenters[cameraId]; Vector3 imageCenter = new Vector3(0.5f, 0.5f, arucoGameObject.transform.position.z); Vector3 opticalCenter = new Vector3(cameraOpticalCenter.x, cameraOpticalCenter.y, arucoGameObject.transform.position.z); Vector3 opticalShift = camera.ViewportToWorldPoint(opticalCenter) - camera.ViewportToWorldPoint(imageCenter); // TODO: fix the position shift orientation Vector3 positionShift = opticalShift // Take account of the optical center not in the image center + arucoGameObject.transform.up * arucoGameObject.transform.localScale.y / 2; // Move up the object to coincide with the marker arucoGameObject.transform.localPosition += positionShift; //print(arucoGameObject.name + " - imageCenter: " + imageCenter.ToString("F3") + "; opticalCenter: " + opticalCenter.ToString("F3") // + "; positionShift: " + (arucoGameObject.transform.rotation * opticalShift).ToString("F4")); arucoGameObject.SetActive(true); }
/// <summary> /// Before the ArUco object's properties will be updated, restore the game object's scale of this object. /// </summary> public virtual void RestoreGameObjectScale(ArucoObject arucoObject) { if (arucoObject.MarkerSideLength != 0) { arucoObject.gameObject.transform.localScale /= arucoObject.MarkerSideLength; } }
protected override void PlaceImagePlane() { base.PlaceImagePlane(); if (ImagePlane != null && ArucoObject != null) { var scale = ArucoObject.GetGameObjectScale(); ImagePlane.transform.localScale = pixelsToMetersFactor * new Vector3(scale.x, scale.z, scale.y); } }
/// <summary> /// Update the gameObject's transform of an ArUco object. /// </summary> /// <param name="arucoObject">The ArUco object to place.</param> /// <param name="rvec">The estimated rotation vector of the ArUco object.</param> /// <param name="tvec">The estimated translation vector of the ArUco object.</param> /// <param name="cameraId">The id of the camera to use. The gameObject is placed and oriented relative to this camera.</param> /// <param name="positionFactor">Factor on the position vector.</param> protected void PlaceArucoObject(ArucoObject arucoObject, Cv.Vec3d rvec, Cv.Vec3d tvec, int cameraId, float positionFactor = 1f) { GameObject arucoGameObject = arucoObject.gameObject; arucoGameObject.transform.SetParent(arucoTracker.ArucoCamera.ImageCameras[cameraId].transform); arucoGameObject.transform.localPosition = tvec.ToPosition() * positionFactor; arucoGameObject.transform.localRotation = rvec.ToRotation(); arucoGameObject.SetActive(true); }
/// <summary> /// Update the gameObject's transform of an ArUco object. /// </summary> /// <param name="arucoObject">The ArUco object to place.</param> /// <param name="rvec">The estimated rotation vector of the ArUco object.</param> /// <param name="tvec">The estimated translation vector of the ArUco object.</param> /// <param name="cameraId">The id of the camera to use. The gameObject is placed and oriented relative to this camera.</param> /// <param name="positionFactor">Factor on the position vector.</param> protected void PlaceArucoObject(ArucoObject arucoObject, Cv.Vec3d rvec, Cv.Vec3d tvec, int cameraId, float positionFactor = 1f) { GameObject arucoGameObject = arucoObject.gameObject; arucoGameObject.transform.SetParent(arucoTracker.ArucoCamera.ImageCameras[cameraId].transform); arucoGameObject.transform.localPosition = tvec.ToPosition() * positionFactor + arucoGameObject.transform.up * arucoGameObject.transform.localScale.y / 2; // Move up the object to coincide with the marker; arucoGameObject.transform.localRotation = rvec.ToRotation(); arucoGameObject.SetActive(true); }
/// <summary> /// Adjust the game object's scale of the ArUco object according to its MarkerSideLength property. /// </summary> /// <param name="arucoObject"></param> protected override void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { base.ArucoObject_PropertyUpdated(arucoObject); if (arucoObject.GetType() == typeof(ArucoMarker)) { MarkerTracker.AdjustGameObjectScale(arucoObject); } else { additionalTrackers[arucoObject.GetType()].AdjustGameObjectScale(arucoObject); } }
/// <summary> /// Places, rotates and scales the image plane. /// </summary> protected virtual void PlaceImagePlane() { if (ArucoObject != null) { var scale = ArucoObject.GetGameObjectScale(); ImagePlane.transform.SetParent(null); ImagePlane.transform.localScale = new Vector3(scale.x, scale.z, scale.y); // Because it's rotated up ImagePlane.transform.SetParent(transform); } ImagePlane.transform.localPosition = Vector3.zero; ImagePlane.transform.forward = -transform.up; // Rotated up }
protected virtual void SetArucoObject(ArucoObject arucoObject) { if (ArucoObject != null) { ArucoObject.PropertyUpdated -= ArucoObject_PropertyUpdated; } this.arucoObject = arucoObject; if (ArucoObject != null) { ArucoObject.PropertyUpdated += ArucoObject_PropertyUpdated; } }
// ArucoObjectController methods /// <summary> /// Activates the tracker associated with the <paramref name="arucoObject"/> and configure its gameObject. /// </summary> /// <param name="arucoObject">The added ArUco object.</param> protected virtual void ArucoObjectsController_ArucoObjectAdded(ArucoObject arucoObject) { if (arucoObject.GetType() != typeof(ArucoMarker)) { ArucoObjectTracker tracker = null; if (!additionalTrackers.TryGetValue(arucoObject.GetType(), out tracker)) { throw new ArgumentException("No tracker found for the type '" + arucoObject.GetType() + "'.", "arucoObject"); } else if (!tracker.IsActivated) { tracker.Activate(this); } } }
// ArucoObjectDisplayer methods /// <summary> /// Calls <see cref="ArucoObjectDisplayer.Create"/>, <see cref="ArucoObjectDisplayer.Display"/> and <see cref="ArucoObjectDisplayer.Save"/>. /// </summary> protected override void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { base.ArucoObject_PropertyUpdated(arucoObject); #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) { #endif if (SaveImage) { Save(); } #if UNITY_EDITOR } #endif }
// Methods /// <summary> /// Save the <see cref="ImageTexture"/> on a image file in the <see cref="OutputFolder"/> with <see cref="ImageFilename"/> as filename. /// </summary> public virtual void Save() { if (ImageFilename == null || ImageFilename.Length == 0) { ImageFilename = ArucoObject.GenerateName() + ".png"; } string outputFolderPath = Path.Combine((Application.isEditor) ? Application.dataPath : Application.persistentDataPath, OutputFolder); if (!Directory.Exists(outputFolderPath)) { Directory.CreateDirectory(outputFolderPath); } string imageFilePath = outputFolderPath + ImageFilename; File.WriteAllBytes(imageFilePath, ImageTexture.EncodeToPNG()); }
// Methods /// <summary> /// Creates <see cref="Image" /> and <see cref="ImageTexture" /> from <see cref="ArucoObject" />. /// </summary> public virtual void CreateImage() { Image = ArucoObject.Draw(); if (Image != null) { // Vertical flip to correctly display the image on the texture var verticalFlipCode = 0; var imageForTexture = Image.Clone(); Cv.Flip(imageForTexture, imageForTexture, verticalFlipCode); // Load the image to the texture var markerDataSize = (int)(Image.ElemSize() * Image.Total()); ImageTexture = new Texture2D(Image.Cols, Image.Rows, TextureFormat.RGB24, false); ImageTexture.LoadRawTextureData(imageForTexture.DataIntPtr, markerDataSize); ImageTexture.Apply(); } }
// ArucoObjectController methods /// <summary> /// Suscribe to the property events of an ArUco object, and hide its gameObject since it has not been detected yet. /// </summary> /// <param name="arucoObject">The new ArUco object to suscribe.</param> protected virtual void ArucoObjectController_ArucoObjectAdded(ArucoObject arucoObject) { ArucoObjectTracker tracker = null; if (arucoObject.GetType() != typeof(ArucoMarker) && !additionalTrackers.TryGetValue(arucoObject.GetType(), out tracker)) { Debug.LogError("No tracker found for the type '" + arucoObject.GetType() + "'. Removing the object '" + arucoObject.gameObject.name + "' from the tracking list."); Remove(arucoObject); return; } if (tracker != null && !tracker.IsActivated) { tracker.Activate(this); } arucoObject.gameObject.SetActive(false); }
// MonoBehaviour methods /// <summary> /// Calls <see cref="SetArucoObject"/> to display the <see cref="ArucoObject"/> only in play mode. /// </summary> protected virtual void Start() { #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) { #endif if (DisplayInPlayMode && ArucoObject) { var currentArucoObject = ArucoObject; arucoObject = null; SetArucoObject(currentArucoObject); } else { enabled = false; } #if UNITY_EDITOR } #endif }
// Methods /// <summary> /// Add an ArUco object to the <see cref="ArucoObjects"/> list. /// </summary> /// <param name="arucoObject">The ArUco object to add.</param> public virtual void Add(ArucoObject arucoObject) { // Make sure the object is started and initialized arucoObject.gameObject.SetActive(true); // Try to find a list with the same dictionary than the new ArUco object Dictionary <int, ArucoObject> arucoObjectsCollection = null; foreach (var arucoObjectDictionary in ArucoObjects) { if (arucoObjectDictionary.Key.Name == arucoObject.Dictionary.Name || arucoObjectDictionary.Key == arucoObject.Dictionary) { arucoObjectsCollection = arucoObjectDictionary.Value; break; } } // If not found, create the new list attached to this dictionary if (arucoObjectsCollection == null) { arucoObjectsCollection = new Dictionary <int, ArucoObject>(); ArucoObjects.Add(arucoObject.Dictionary, arucoObjectsCollection); DictionaryAdded(arucoObject.Dictionary); } // Return if the ArUco object is already in the list else { if (arucoObjectsCollection.ContainsKey(arucoObject.ArucoHashCode)) { return; } } // Suscribe to property events on the aruco object arucoObject.PropertyUpdating += ArucoObject_PropertyUpdating; arucoObject.PropertyUpdated += ArucoObject_PropertyUpdated; // Add the ArUco object to the list arucoObjectsCollection.Add(arucoObject.ArucoHashCode, arucoObject); ArucoObjectAdded(arucoObject); }
protected virtual void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { Create(); if (DrawImage) { Draw(); } #if UNITY_EDITOR if (Application.isPlaying) { #endif if (SaveImage) { Save(); } #if UNITY_EDITOR } #endif }
/// <summary> /// Deactivates the tracker associated with the <paramref name="arucoObject" /> if it was the last one of this type. /// </summary> /// <param name="arucoObject">The removed</param> protected virtual void ArucoObjectsController_ArucoObjectRemoved(ArucoObject arucoObject) { ArucoObjectTracker tracker = null; if (arucoObject.GetType() == typeof(ArucoMarker) || !additionalTrackers.TryGetValue(arucoObject.GetType(), out tracker)) { return; } if (tracker.IsActivated) { var deactivateTracker = true; // Try to find at leat one object of the same type as arucoObject foreach (var arucoObjectDictionary in ArucoObjects) { foreach (var arucoObject2 in arucoObjectDictionary.Value) { if (arucoObject2.GetType() == arucoObject.GetType()) { deactivateTracker = false; break; } } if (!deactivateTracker) { break; } } if (deactivateTracker) { tracker.Deactivate(); } } }
// ArucoObjectController methods /// <summary> /// Activate the tracker associated with the <paramref name="arucoObject"/> and configure its gameObject. /// </summary> /// <param name="arucoObject">The added ArUco object.</param> protected virtual void ArucoObjectsController_ArucoObjectAdded(ArucoObject arucoObject) { if (arucoObject.GetType() == typeof(ArucoMarker)) { return; } // Activate the tracker if necessary ArucoObjectTracker tracker = null; if (!additionalTrackers.TryGetValue(arucoObject.GetType(), out tracker)) { throw new System.ArgumentException("No tracker found for the type '" + arucoObject.GetType() + "'.", "arucoObject"); } else if (!tracker.IsActivated) { tracker.Activate(this); } // Configure the game object AdjustGameObjectScale(arucoObject); arucoObject.gameObject.SetActive(false); }
// ArucoObjectDisplayer methods /// <summary> /// Calls <see cref="ArucoObjectDisplayer.UpdateImage"/> then <see cref="SaveImage"/> if <see cref="AutoSaveImage"/> /// is set. Also set <see cref="ImageFilename"/> in the editor. /// </summary> protected override void UpdateImage() { base.UpdateImage(); #if UNITY_EDITOR if (automaticFilename) { ImageFilename = ArucoObject.GenerateName(); } #endif #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) { #endif if (AutoSaveImage) { SaveImage(); } #if UNITY_EDITOR } #endif }
/// <summary> /// Updates the display in the Unity Editor if arucoObject has been changed. /// </summary> protected virtual void Update() { #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) { if (lastArucoObjectOnValidate != ArucoObject) { if (ArucoObject != null) { var currentArucoObject = ArucoObject; arucoObject = lastArucoObjectOnValidate; SetArucoObject(currentArucoObject); } else { Reset(); } lastArucoObjectOnValidate = ArucoObject; } PlaceImagePlane(); } #endif }
/// <summary> /// Add again the updated ArUco object. /// </summary> /// <param name="arucoObject">The updated ArUco object.</param> // TODO: find a more elegant way to adjust the aruco object list from the aruco object's dictionary and hashcode changes. protected virtual void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { Add(arucoObject); }
// Start is called before the first frame update void Start() { ao = MarkerObject.GetComponent <ArucoObject>(); text = GetComponent <TextMeshProUGUI>(); }
/// <summary> /// Before the ArUco object's properties will be updated, remove it from the ArUco objects list. /// </summary> /// <param name="arucoObject">The updated ArUco object.</param> protected virtual void ArucoObject_PropertyUpdating(ArucoObject arucoObject) { Remove(arucoObject); }
/// <summary> /// Adjust the game object's scale of the ArUco object according to its MarkerSideLength property. /// </summary> protected override void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { AdjustGameObjectScale(arucoObject); }
/// <summary> /// Calls <see cref="Create"/> and <see cref="Display"/>. /// </summary> protected virtual void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { Create(); Display(); }
protected virtual void ArucoObjectController_ArucoObjectRemoved(ArucoObject arucoObject) { // TODO }
/// <summary> /// Calls <see cref="UpdateImage" />. /// </summary> protected virtual void ArucoObject_PropertyUpdated(ArucoObject arucoObject) { UpdateImage(); }