diff --git a/com.unity.perception/CHANGELOG.md b/com.unity.perception/CHANGELOG.md index 42634126a..ddfcf8c0d 100644 --- a/com.unity.perception/CHANGELOG.md +++ b/com.unity.perception/CHANGELOG.md @@ -51,6 +51,8 @@ The newly added `LabelManager` class now enables custom Labelers to access the l Improved UI for `KeypointTemplate` and added useful default colors for keypoint and skeleton definitions. +Added the ability to switch ground-truth labeling on or off for an object at runtime by enabling or disabling its `Labeling` component. + ### Changed Renamed all appearances of the term `KeyPoint` within types and names to `Keypoint`. diff --git a/com.unity.perception/Runtime/GroundTruth/InstanceIdToColorMapping.cs b/com.unity.perception/Runtime/GroundTruth/InstanceIdToColorMapping.cs index 69790b3ae..444fce598 100644 --- a/com.unity.perception/Runtime/GroundTruth/InstanceIdToColorMapping.cs +++ b/com.unity.perception/Runtime/GroundTruth/InstanceIdToColorMapping.cs @@ -26,7 +26,7 @@ public static class InstanceIdToColorMapping const uint k_HslCount = 64; const uint k_ColorsPerAlpha = 256 * 256 * 256; const uint k_InvalidPackedColor = 255; // packed uint for color (0, 0, 0, 255); - static readonly Color32 k_InvalidColor = new Color(0, 0, 0, 255); + public static readonly Color32 invalidColor = new Color(0, 0, 0, 255); static readonly float k_GoldenRatio = (1 + Mathf.Sqrt(5)) / 2; const int k_HuesInEachValue = 30; @@ -143,7 +143,7 @@ public static Color32 GetColorFromPackedColor(uint color) /// Returns true if the ID was mapped to a non-black color, otherwise returns false public static bool TryGetColorFromInstanceId(uint id, out Color32 color) { - color = k_InvalidColor; + color = invalidColor; if (id > maxId) return false; var packed = GetColorForId(id); diff --git a/com.unity.perception/Runtime/GroundTruth/Labelers/KeypointLabeler.cs b/com.unity.perception/Runtime/GroundTruth/Labelers/KeypointLabeler.cs index fdedc2c29..ec7acf0ec 100644 --- a/com.unity.perception/Runtime/GroundTruth/Labelers/KeypointLabeler.cs +++ b/com.unity.perception/Runtime/GroundTruth/Labelers/KeypointLabeler.cs @@ -332,20 +332,21 @@ bool DoesTemplateContainJoint(JointLabel jointLabel) void ProcessLabel(Labeling labeledEntity) { - // Cache out the data of a labeled game object the first time we see it, this will - // save performance each frame. Also checks to see if a labeled game object can be annotated. - if (!m_KnownStatus.ContainsKey(labeledEntity.instanceId)) + if (idLabelConfig.TryGetLabelEntryFromInstanceId(labeledEntity.instanceId, out var labelEntry)) { - var cached = new CachedData() + // Cache out the data of a labeled game object the first time we see it, this will + // save performance each frame. Also checks to see if a labeled game object can be annotated. + if (!m_KnownStatus.ContainsKey(labeledEntity.instanceId)) { - status = false, - animator = null, - keypoints = new KeypointEntry(), - overrides = new List<(JointLabel, int)>() - }; + var cached = new CachedData() + { + status = false, + animator = null, + keypoints = new KeypointEntry(), + overrides = new List<(JointLabel, int)>() + }; + - if (idLabelConfig.TryGetLabelEntryFromInstanceId(labeledEntity.instanceId, out var labelEntry)) - { var entityGameObject = labeledEntity.gameObject; cached.keypoints.instance_id = labeledEntity.instanceId; @@ -373,55 +374,55 @@ void ProcessLabel(Labeling labeledEntity) cached.status = true; } } - } - m_KnownStatus[labeledEntity.instanceId] = cached; - } + m_KnownStatus[labeledEntity.instanceId] = cached; + } - var cachedData = m_KnownStatus[labeledEntity.instanceId]; + var cachedData = m_KnownStatus[labeledEntity.instanceId]; - if (cachedData.status) - { - var animator = cachedData.animator; - var keypoints = cachedData.keypoints.keypoints; - - // Go through all of the rig keypoints and get their location - for (var i = 0; i < activeTemplate.keypoints.Length; i++) + if (cachedData.status) { - var pt = activeTemplate.keypoints[i]; - if (pt.associateToRig) + var animator = cachedData.animator; + var keypoints = cachedData.keypoints.keypoints; + + // Go through all of the rig keypoints and get their location + for (var i = 0; i < activeTemplate.keypoints.Length; i++) { - var bone = animator.GetBoneTransform(pt.rigLabel); - if (bone != null) + var pt = activeTemplate.keypoints[i]; + if (pt.associateToRig) { - var loc = ConvertToScreenSpace(bone.position); - keypoints[i].index = i; - keypoints[i].x = loc.x; - keypoints[i].y = loc.y; - keypoints[i].state = 2; + var bone = animator.GetBoneTransform(pt.rigLabel); + if (bone != null) + { + var loc = ConvertToScreenSpace(bone.position); + keypoints[i].index = i; + keypoints[i].x = loc.x; + keypoints[i].y = loc.y; + keypoints[i].state = 2; + } } } - } - // Go through all of the additional or override points defined by joint labels and get - // their locations - foreach (var (joint, idx) in cachedData.overrides) - { - var loc = ConvertToScreenSpace(joint.transform.position); - keypoints[idx].index = idx; - keypoints[idx].x = loc.x; - keypoints[idx].y = loc.y; - keypoints[idx].state = 2; - } + // Go through all of the additional or override points defined by joint labels and get + // their locations + foreach (var (joint, idx) in cachedData.overrides) + { + var loc = ConvertToScreenSpace(joint.transform.position); + keypoints[idx].index = idx; + keypoints[idx].x = loc.x; + keypoints[idx].y = loc.y; + keypoints[idx].state = 2; + } - cachedData.keypoints.pose = "unset"; + cachedData.keypoints.pose = "unset"; - if (cachedData.animator != null) - { - cachedData.keypoints.pose = GetPose(cachedData.animator); - } + if (cachedData.animator != null) + { + cachedData.keypoints.pose = GetPose(cachedData.animator); + } - m_AsyncAnnotations[m_CurrentFrame].keypoints[labeledEntity.instanceId] = cachedData.keypoints; + m_AsyncAnnotations[m_CurrentFrame].keypoints[labeledEntity.instanceId] = cachedData.keypoints; + } } } diff --git a/com.unity.perception/Runtime/GroundTruth/Labeling/LabelEntryMatchCache.cs b/com.unity.perception/Runtime/GroundTruth/Labeling/LabelEntryMatchCache.cs index bafdd69a6..825f85aa7 100644 --- a/com.unity.perception/Runtime/GroundTruth/Labeling/LabelEntryMatchCache.cs +++ b/com.unity.perception/Runtime/GroundTruth/Labeling/LabelEntryMatchCache.cs @@ -58,17 +58,24 @@ void IGroundTruthGenerator.SetupMaterialProperties(MaterialPropertyBlock mpb, Re if (m_IdLabelConfig.TryGetMatchingConfigurationEntry(labeling, out _, out var index)) { Debug.Assert(index < k_DefaultValue, "Too many entries in the label config"); - if (m_InstanceIdToLabelEntryIndexLookup.Length <= instanceId) + if (labeling.enabled) { - var oldLength = m_InstanceIdToLabelEntryIndexLookup.Length; - m_InstanceIdToLabelEntryIndexLookup.Resize((int)instanceId + 1, NativeArrayOptions.ClearMemory); + if (m_InstanceIdToLabelEntryIndexLookup.Length <= instanceId) + { + var oldLength = m_InstanceIdToLabelEntryIndexLookup.Length; + m_InstanceIdToLabelEntryIndexLookup.Resize((int)instanceId + 1, NativeArrayOptions.ClearMemory); - for (var i = oldLength; i < instanceId; i++) - m_InstanceIdToLabelEntryIndexLookup[i] = k_DefaultValue; + for (var i = oldLength; i < instanceId; i++) + m_InstanceIdToLabelEntryIndexLookup[i] = k_DefaultValue; + } + m_InstanceIdToLabelEntryIndexLookup[(int)instanceId] = (ushort)index; + } + else if (m_InstanceIdToLabelEntryIndexLookup.Length > instanceId) + { + m_InstanceIdToLabelEntryIndexLookup[(int)instanceId] = k_DefaultValue; } - m_InstanceIdToLabelEntryIndexLookup[(int)instanceId] = (ushort)index; } - else if (m_InstanceIdToLabelEntryIndexLookup.Length > (int)instanceId) + else if (m_InstanceIdToLabelEntryIndexLookup.Length > instanceId) { m_InstanceIdToLabelEntryIndexLookup[(int)instanceId] = k_DefaultValue; } diff --git a/com.unity.perception/Runtime/GroundTruth/Labeling/LabelManager.cs b/com.unity.perception/Runtime/GroundTruth/Labeling/LabelManager.cs index 4516fc2c2..d2a22d014 100644 --- a/com.unity.perception/Runtime/GroundTruth/Labeling/LabelManager.cs +++ b/com.unity.perception/Runtime/GroundTruth/Labeling/LabelManager.cs @@ -81,15 +81,6 @@ public bool Deactivate(IGroundTruthGenerator generator) return m_ActiveGenerators.Remove(generator); } - /// - /// Registers a labeling component - /// - /// the component to register - internal void Register(Labeling labeling) - { - m_LabelsPendingRegistration.Add(labeling); - } - /// /// Unregisters a labeling component /// diff --git a/com.unity.perception/Runtime/GroundTruth/Labeling/Labeling.cs b/com.unity.perception/Runtime/GroundTruth/Labeling/Labeling.cs index 60e834e4f..7a9182024 100644 --- a/com.unity.perception/Runtime/GroundTruth/Labeling/Labeling.cs +++ b/com.unity.perception/Runtime/GroundTruth/Labeling/Labeling.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using UnityEditor; using UnityEngine.Serialization; @@ -34,14 +35,19 @@ public class Labeling : MonoBehaviour /// public uint instanceId { get; private set; } - void Awake() + void OnDestroy() { - labelManager.Register(this); + labelManager.Unregister(this); } - void OnDestroy() + void OnEnable() { - labelManager.Unregister(this); + RefreshLabeling(); + } + + void OnDisable() + { + RefreshLabeling(); } void Reset() diff --git a/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/InstanceSegmentationCrossPipelinePass.cs b/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/InstanceSegmentationCrossPipelinePass.cs index 9a4033807..c2504c35e 100644 --- a/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/InstanceSegmentationCrossPipelinePass.cs +++ b/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/InstanceSegmentationCrossPipelinePass.cs @@ -69,7 +69,10 @@ public override void SetupMaterialProperties( if (!found) Debug.LogError($"Could not get a unique color for {instanceId}"); - mpb.SetVector(k_SegmentationIdProperty, (Color)color); + if (labeling.enabled) + mpb.SetVector(k_SegmentationIdProperty, (Color)color); + else + mpb.SetVector(k_SegmentationIdProperty, (Color) InstanceIdToColorMapping.invalidColor); #if PERCEPTION_DEBUG Debug.Log($"Assigning id. Frame {Time.frameCount} id {id}"); #endif diff --git a/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/SemanticSegmentationCrossPipelinePass.cs b/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/SemanticSegmentationCrossPipelinePass.cs index d2c981cc9..3e237b605 100644 --- a/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/SemanticSegmentationCrossPipelinePass.cs +++ b/com.unity.perception/Runtime/GroundTruth/RenderPasses/CrossPipelinePasses/SemanticSegmentationCrossPipelinePass.cs @@ -65,19 +65,25 @@ public override void SetupMaterialProperties( { var entry = new SemanticSegmentationLabelEntry(); var found = false; - foreach (var l in m_LabelConfig.labelEntries) + if (labeling.enabled) { - if (labeling.labels.Contains(l.label)) + foreach (var l in m_LabelConfig.labelEntries) { - entry = l; - found = true; - break; + if (labeling.labels.Contains(l.label)) + { + entry = l; + found = true; + break; + } } } // Set the labeling ID so that it can be accessed in ClassSemanticSegmentationPass.shader if (found) mpb.SetVector(k_LabelingId, entry.color); + else + mpb.SetVector(k_LabelingId, Color.black); + } } } diff --git a/com.unity.perception/Tests/Runtime/GroundTruthTests/LabelEntryMatchCacheTests.cs b/com.unity.perception/Tests/Runtime/GroundTruthTests/LabelEntryMatchCacheTests.cs index 1bce8bd89..318226ec8 100644 --- a/com.unity.perception/Tests/Runtime/GroundTruthTests/LabelEntryMatchCacheTests.cs +++ b/com.unity.perception/Tests/Runtime/GroundTruthTests/LabelEntryMatchCacheTests.cs @@ -131,5 +131,43 @@ public IEnumerator TryGet_ReturnsFalse_ForNonMatchingLabel_WhenAllObjectsAreDest } } + [UnityTest] + public IEnumerator TryGet_ReturnsFalse_ForMatchingLabelWithDisabledLabelingComponent() + { + var label = "label"; + var labeledPlane = TestHelper.CreateLabeledPlane(label: label); + AddTestObjectForCleanup(labeledPlane); + var config = ScriptableObject.CreateInstance(); + var labeling = labeledPlane.GetComponent(); + + config.Init(new[] + { + new IdLabelEntry() + { + id = 1, + label = label + }, + }); + using (var cache = new LabelEntryMatchCache(config, Allocator.Persistent)) + { + labeling.enabled = false; + //allow label to be registered + yield return null; + Assert.IsFalse(cache.TryGetLabelEntryFromInstanceId(labeledPlane.GetComponent().instanceId, out var labelEntry, out var index)); + Assert.AreEqual(-1, index); + + labeling.enabled = true; + yield return null; + Assert.IsTrue(cache.TryGetLabelEntryFromInstanceId(labeledPlane.GetComponent().instanceId, out labelEntry, out index)); + Assert.AreEqual(0, index); + Assert.AreEqual(config.labelEntries[0], labelEntry); + + labeling.enabled = false; + yield return null; + Assert.IsFalse(cache.TryGetLabelEntryFromInstanceId(labeledPlane.GetComponent().instanceId, out labelEntry, out index)); + Assert.AreEqual(-1, index); + } + } + } } diff --git a/com.unity.perception/Tests/Runtime/GroundTruthTests/SegmentationGroundTruthTests.cs b/com.unity.perception/Tests/Runtime/GroundTruthTests/SegmentationGroundTruthTests.cs index be6653396..61643b133 100644 --- a/com.unity.perception/Tests/Runtime/GroundTruthTests/SegmentationGroundTruthTests.cs +++ b/com.unity.perception/Tests/Runtime/GroundTruthTests/SegmentationGroundTruthTests.cs @@ -256,6 +256,50 @@ void OnSegmentationImageReceived(NativeArray data) Assert.AreEqual(1, timesSegmentationImageReceived); } + [UnityTest] + public IEnumerator SemanticSegmentationPass_WithMatchingButDisabledLabel_ProducesBlack() + { + int timesSegmentationImageReceived = 0; + var expectedPixelValue = new Color32(0, 0, 0, 255); + void OnSegmentationImageReceived(NativeArray data) + { + timesSegmentationImageReceived++; + CollectionAssert.AreEqual(Enumerable.Repeat(expectedPixelValue, data.Length), data.ToArray()); + } + + var cameraObject = SetupCameraSemanticSegmentation(a => OnSegmentationImageReceived(a.data), false); + + var gameObject = TestHelper.CreateLabeledPlane(); + gameObject.GetComponent().enabled = false; + AddTestObjectForCleanup(gameObject); + yield return null; + //destroy the object to force all pending segmented image readbacks to finish and events to be fired. + DestroyTestObject(cameraObject); + Assert.AreEqual(1, timesSegmentationImageReceived); + } + + [UnityTest] + public IEnumerator InstanceSegmentationPass_WithMatchingButDisabledLabel_ProducesBlack() + { + int timesSegmentationImageReceived = 0; + var expectedPixelValue = new Color32(0, 0, 0, 255); + void OnSegmentationImageReceived(NativeArray data) + { + CollectionAssert.AreEqual(Enumerable.Repeat(expectedPixelValue, data.Length), data); + timesSegmentationImageReceived++; + } + + var cameraObject = SetupCameraInstanceSegmentation((frame, data, renderTexture) => OnSegmentationImageReceived(data)); + + var gameObject = TestHelper.CreateLabeledPlane(); + gameObject.GetComponent().enabled = false; + AddTestObjectForCleanup(gameObject); + yield return null; + //destroy the object to force all pending segmented image readbacks to finish and events to be fired. + DestroyTestObject(cameraObject); + Assert.AreEqual(1, timesSegmentationImageReceived); + } + [UnityTest] public IEnumerator SemanticSegmentationPass_WithEmptyFrame_ProducesBlack([Values(false, true)] bool showVisualizations) {