Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Steve/instance aggregation #563

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.perception/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Upgrade Notes

### Added
Added support for hierarchical label aggregation in instance segmentation labeler

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ void InitUi()
m_AddNewLabelButton.clicked += () => { AddNewLabel(m_AddedLabels); };

#if UNITY_2020_1_OR_NEWER
m_LabelListView.onSelectionChange += UpdateMoveButtonState;
m_LabelListView.selectionChanged += UpdateMoveButtonState;
#else
m_LabelListView.onSelectionChanged += UpdateMoveButtonState;
#endif
Comment on lines 137 to 141
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives me an error on 2021.3.x. (official perception supported version). Looking at the api, it looks like selectionChanged is only supported on 2023.1 and later, do you happen to be using that one?

To make it work, we can change the whole block to

#if UNITY_2023_1_OR_NEWER
            m_LabelListView.selectionChanged +=  UpdateMoveButtonState;
#elif UNITY_2020_1_OR_NEWER
            m_LabelListView.onSelectionChange +=  UpdateMoveButtonState;
#else
            m_LabelListView.onSelectionChanged +=  UpdateMoveButtonState;
#endif

This should make the tests pass too.

Expand Down
6 changes: 3 additions & 3 deletions com.unity.perception/Editor/Unity.Perception.Editor.api
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,23 @@ namespace UnityEngine.Perception.Settings

namespace UnityEngine.Perception.UIElements
{
public class UIntField : UnityEditor.UIElements.TextValueField<System.UInt32>
public class UIntField : UnityEngine.UIElements.TextValueField<System.UInt32>
{
public static readonly string inputUssClassName;
public static readonly string labelUssClassName;
public static readonly string ussClassName = @"unity-uint-field";
public UIntField() {}
public UIntField(int maxLength) {}
public UIntField(string label, int maxLength = -1) {}
public override void ApplyInputDeviceDelta(Vector3 delta, UnityEditor.UIElements.DeltaSpeed speed, System.UInt32 startValue);
public override void ApplyInputDeviceDelta(Vector3 delta, UnityEngine.UIElements.DeltaSpeed speed, System.UInt32 startValue);
public static System.UInt32 ClampInput(long input);
protected override System.UInt32 StringToValue(string str);
protected override string ValueToString(System.UInt32 v);
public class UxmlFactory : UnityEngine.UIElements.UxmlFactory<UIntField, UIntField.UxmlTraits>
{
public UxmlFactory() {}
}
public class UxmlTraits : UnityEditor.UIElements.TextValueFieldTraits<System.UInt32, UxmlUIntAttributeDescription>
public class UxmlTraits : UnityEngine.UIElements.TextValueFieldTraits<System.UInt32, UxmlUIntAttributeDescription>
{
public UxmlTraits() {}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using JetBrains.Annotations;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
Expand Down Expand Up @@ -49,6 +50,14 @@ public sealed class InstanceSegmentationLabeler : CameraLabeler, IOverlayPanelPr
/// </summary>
public IdLabelConfig idLabelConfig;

/// <summary>
/// Should child objects, defined by their label hierarchy be reported as an individual instance, or as
/// a part of their parent object. If this value is true, the children will be reported as a part of their
/// parent.
/// </summary>
[Tooltip("Should the instance segmentation capture the single instance of the parent gameobject, or the individual sub-components")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[Tooltip("Should the instance segmentation capture the single instance of the parent gameobject, or the individual sub-components")]
[Tooltip("Should the instance segmentation capture the single instance of the parent GameObject, or the individual labelled sub-components")]

public bool aggregateChildren = false;

///<inheritdoc/>
public override string description => InstanceSegmentationDefinition.labelDescription;

Expand Down Expand Up @@ -115,6 +124,44 @@ protected override void Cleanup()
}
}

NativeArray<Color32> GetSegmentationColors(int frame)
{
var instanceIndices = LabelManager.singleton.instanceIds;
var max = uint.MinValue;
foreach (var i in instanceIndices)
{
if (i > max) max = i;
}

var activeColors = new NativeArray<Color32>((int)(max + 1), Allocator.Temp, NativeArrayOptions.ClearMemory);
Copy link
Collaborator

@mkamalza mkamalza Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One little concern that I have here is that the array of instance ids can grow indefinitely in projects that use object caching, because we only remove instance ids when objects are destroyed.
Therefore, with object caching, the activeColors array can get quite big in projects that go for thousands of iterations with tens of labelled objects in each frame (like Escher and it's more advanced version Hosta).

With 1000 iterations and 20 labelled objects each iteration, activeColors could be around 80KBs. May not be a big deal, but just something to keep in mind.


for (var i = 0; i < max + 1; i++)
{
activeColors[i] = InstanceIdToColorMapping.GetColorFromInstanceId((uint)i);
}

if (!aggregateChildren) return activeColors;

if (!PerceptionCamera.savedHierarchies.TryGetValue(frame, out var hierarchyInformation))
{
Debug.LogError($"Could not get the scene hierarchy info for the current frame: {frame}");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Debug.LogError($"Could not get the scene hierarchy info for the current frame: {frame}");
Debug.LogError($"Instance segmentation could not get the scene hierarchy info for the current frame: {frame}. Will not aggregate labelled children.");

return activeColors;
}

foreach (var i in instanceIndices)
{
if (!hierarchyInformation.hierarchy.TryGetValue(i, out var node))
{
continue;
}

var idx = (int)(node?.parentInstanceId ?? i);
activeColors[(int)i] = activeColors[idx];
}

return activeColors;
}

/// <inheritdoc/>
protected override void OnEndRendering(ScriptableRenderContext ctx)
{
Expand All @@ -126,9 +173,10 @@ protected override void OnEndRendering(ScriptableRenderContext ctx)

// Create a compute buffer that maps instanceIndices to unique instance segmentation colors.
var instanceIndices = LabelManager.singleton.instanceIds;
var instanceSegmentationColors = LabelManager.singleton.instanceSegmentationColors;
var colors = GetSegmentationColors(Time.frameCount);

var colorBuffer = new ComputeBuffer(instanceIndices.Length, sizeof(uint));
cmd.SetBufferData(colorBuffer, instanceSegmentationColors.AsArray(), 0, 0, colorBuffer.count);
cmd.SetBufferData(colorBuffer, colors, 0, 0, colorBuffer.count);

// Use a compute shader to map each pixel instance index to a unique color
// to create the instance segmentation color texture.
Expand All @@ -149,6 +197,7 @@ protected override void OnEndRendering(ScriptableRenderContext ctx)
colorBuffer.Dispose();
});

colors.Dispose();
ctx.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
Expand All @@ -159,23 +208,76 @@ void OnRenderedObjectInfosCalculated(
SceneHierarchyInformation hierarchyInfo
)
{
var instances = new List<InstanceSegmentationEntry>();
// In order to support aggregate segmentation, we need to use the parent values, if requested,
// of the instances to report the instance type for color

var instances = new Dictionary<int, InstanceSegmentationEntry>();

foreach (var objectInfo in renderedObjectInfos)
{
if (!idLabelConfig.TryGetLabelEntryFromInstanceId(objectInfo.instanceId, out var labelEntry))
if (!hierarchyInfo.hierarchy.TryGetValue(objectInfo.instanceId, out var node))
{
Debug.LogError($"Could not find hierarchy info for instance id: {objectInfo.instanceId}");
continue;
}

// If collecting aggregate info, use the parent id, else use the objectInfo.instanceId
var idx = aggregateChildren ? node?.parentInstanceId ?? objectInfo.instanceId : objectInfo.instanceId;
var intIdx = (int)idx;

// Ok, this exists because we have no good way to look up label information at runtime, if a label
// is not associated with an object that is captured in objectrenderinfo pass. This is seen routinely
// in parent geometry using hierarchical labeling not having geometry of its own, but just aggregating
// labeled child objects. To support this we have to create a way to query labels from the id label config.
// This is not the most performant way to do this, things that we could do in the future to speed this up
// * rework the way we are calculating labeled data
// * cache this
// Also, this only supports a 1-1 mapping of int id to label and label to int id. Re-using labels will
// break this but right now is *probably* supported in perception. We need to revisit that and codify
// that label strings need to be unique.
var labelMap = new Dictionary<string, int>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we try to cache this? We don't supporting updating of label configs at runtime any way.
Although then we'd need to implement a hash or sth to check to make sure the config is unchanged, in case we decide to support runtime config updating at some point.

var labels = idLabelConfig.GetAnnotationSpecification();
foreach (var l in labels)
{
labelMap[l.label_name] = l.label_id;
}

instances.Add(new InstanceSegmentationEntry
if (!instances.ContainsKey(intIdx))
{
instanceId = (int)objectInfo.instanceId,
labelId = labelEntry.id,
labelName = labelEntry.label,
color = objectInfo.instanceColor
});
var registeredLabels = LabelManager.singleton.registeredLabels;
var targetNodes = registeredLabels.Where(x => x.instanceId == idx).ToList();

if (targetNodes.Count != 1)
{
Debug.LogWarning($"Something went wrong when trying to find the node for label {idx}, query came back with {targetNodes.Count} entries");
continue;
}

var targetNode = targetNodes.First();

if (!InstanceIdToColorMapping.TryGetColorFromInstanceId(idx, out var color))
{
Debug.LogWarning($"Could not find the instance color for ID: {idx}");
color = Color.black;
}

var labelName = targetNode.labels.First();
if (!labelMap.TryGetValue(labelName, out var labelId))
{
Debug.LogWarning($"Could not find a labelId for the label: {labelName}");
}

instances[intIdx] = new InstanceSegmentationEntry
{
instanceId = intIdx,
labelId = labelId,
labelName = labelName,
color = color
};
}
}

m_PendingEntries[frame] = instances;
m_PendingEntries[frame] = instances.Values.ToList();

ReportFrameIfReady(frame);
}
Expand Down
1 change: 1 addition & 0 deletions com.unity.perception/Runtime/Unity.Perception.Runtime.api
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ namespace UnityEngine.Perception.GroundTruth.Labelers

[UnityEngine.Scripting.APIUpdating.MovedFrom(@"UnityEngine.Perception.GroundTruth")] public sealed class InstanceSegmentationLabeler : CameraLabeler, IOverlayPanelProvider
{
[Tooltip(@"Should the instance segmentation capture the single instance of the parent gameobject, or the individual sub-components")] public bool aggregateChildren = false;
[Tooltip(@"The id to associate with instance segmentation annotations in the dataset.")] public string annotationId = @"instance segmentation";
public UnityEngine.Perception.GroundTruth.LabelManagement.IdLabelConfig idLabelConfig;
public System.Action<int, Unity.Collections.NativeArray<Color32>, RenderTexture> imageReadback;
Expand Down
Loading