浏览代码

Merge pull request #121 from Unity-Technologies/labeling-improvements

New Labeling and Label Config UI
/main
GitHub 4 年前
当前提交
245b8d01
共有 56 个文件被更改,包括 3221 次插入222 次删除
  1. 305
      com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs
  2. 2
      com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs.meta
  3. 929
      com.unity.perception/Editor/GroundTruth/LabelingEditor.cs
  4. 2
      com.unity.perception/Editor/GroundTruth/LabelingEditor.cs.meta
  5. 141
      com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs
  6. 4
      com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs.meta
  7. 20
      com.unity.perception/Editor/Randomization/Uss/Styles.uss
  8. 1
      com.unity.perception/Runtime/GroundTruth/Labelers/BoundingBoxLabeler.cs
  9. 3
      com.unity.perception/Runtime/GroundTruth/Labeling/IdLabelConfig.cs
  10. 25
      com.unity.perception/Runtime/GroundTruth/Labeling/LabelConfig.cs
  11. 37
      com.unity.perception/Runtime/GroundTruth/Labeling/Labeling.cs
  12. 13
      com.unity.perception/Runtime/GroundTruth/Labeling/SemanticSegmentationLabelConfig.cs
  13. 8
      com.unity.perception/Editor/AssetPreparation.meta
  14. 396
      com.unity.perception/Editor/GroundTruth/AddToConfigWindow.cs
  15. 3
      com.unity.perception/Editor/GroundTruth/AddToConfigWindow.cs.meta
  16. 459
      com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs
  17. 3
      com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs.meta
  18. 8
      com.unity.perception/Editor/GroundTruth/Uss.meta
  19. 8
      com.unity.perception/Editor/GroundTruth/Uxml.meta
  20. 11
      com.unity.perception/Editor/AssetPreparation/AssetPreparationTools.cs.meta
  21. 24
      com.unity.perception/Editor/AssetPreparation/AssetPreparationTools.cs
  22. 3
      com.unity.perception/Editor/GroundTruth/Uss/Styles.uss.meta
  23. 228
      com.unity.perception/Editor/GroundTruth/Uss/Styles.uss
  24. 13
      com.unity.perception/Editor/GroundTruth/Uxml/AddToConfigWindow.uxml
  25. 10
      com.unity.perception/Editor/GroundTruth/Uxml/AddToConfigWindow.uxml.meta
  26. 7
      com.unity.perception/Editor/GroundTruth/Uxml/AddedLabelElement.uxml
  27. 3
      com.unity.perception/Editor/GroundTruth/Uxml/AddedLabelElement.uxml.meta
  28. 7
      com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelNotPresent.uxml
  29. 3
      com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelNotPresent.uxml.meta
  30. 8
      com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelPresent.uxml
  31. 3
      com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelPresent.uxml.meta
  32. 38
      com.unity.perception/Editor/GroundTruth/Uxml/Labeling_Main.uxml
  33. 10
      com.unity.perception/Editor/GroundTruth/Uxml/Labeling_Main.uxml.meta
  34. 7
      com.unity.perception/Editor/GroundTruth/Uxml/SuggestedLabelElement.uxml
  35. 3
      com.unity.perception/Editor/GroundTruth/Uxml/SuggestedLabelElement.uxml.meta
  36. 13
      com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementForAddingLabelsFrom.uxml
  37. 3
      com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementForAddingLabelsFrom.uxml.meta
  38. 10
      com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml.meta
  39. 14
      com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml
  40. 3
      com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml.meta
  41. 14
      com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml
  42. 3
      com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml.meta
  43. 53
      com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml
  44. 3
      com.unity.perception/Editor/Icons/ChevronDown.png
  45. 142
      com.unity.perception/Editor/Icons/ChevronDown.png.meta
  46. 6
      com.unity.perception/Editor/Icons/ChevronDownPadded.png
  47. 142
      com.unity.perception/Editor/Icons/ChevronDownPadded.png.meta
  48. 4
      com.unity.perception/Editor/Icons/ChevronUp.png
  49. 142
      com.unity.perception/Editor/Icons/ChevronUp.png.meta
  50. 4
      com.unity.perception/Editor/Icons/ChevronUpPadded.png
  51. 142
      com.unity.perception/Editor/Icons/ChevronUpPadded.png.meta
  52. 0
      /com.unity.perception/Editor/Icons.meta
  53. 0
      /com.unity.perception/Editor/Icons

305
com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs


using System;
using Unity.Mathematics;
using UnityEditorInternal;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
class IdLabelConfigEditor : Editor
class IdLabelConfigEditor : LabelConfigEditor<IdLabelEntry>
ReorderableList m_LabelsList;
const float k_Margin = 5f;
protected override void InitUiExtended()
{
m_StartingIdEnumField.RegisterValueChangedCallback(evt =>
{
var id = (int) ((StartingLabelId) evt.newValue);
serializedObject.FindProperty(nameof(IdLabelConfig.startingLabelId)).enumValueIndex = id;
serializedObject.ApplyModifiedProperties();
AutoAssignIds();
});
m_AutoIdToggle.RegisterValueChangedCallback(evt =>
{
serializedObject.FindProperty(nameof(IdLabelConfig.autoAssignIds)).boolValue = evt.newValue;
m_StartingIdEnumField.SetEnabled(evt.newValue);
serializedObject.ApplyModifiedProperties();
if (!evt.newValue)
{
ChangesHappeningInForeground = true;
RefreshListDataAndPresentation();
//if evt.newValue is true, the auto assign function will perform the above refresh, so no need to do this twice
//refresh is needed because the id textfields of the labels need to be enabled or disabled accordingly
}
AutoAssignIdsIfNeeded();
});
m_StartingIdEnumField.SetEnabled(AutoAssign);
public void OnEnable()
{
m_LabelsList = new ReorderableList(this.serializedObject, this.serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName), true, false, true, true);
m_LabelsList.elementHeight = EditorGUIUtility.singleLineHeight * 2 + k_Margin;
m_LabelsList.drawElementCallback = DrawElement;
m_LabelsList.onAddCallback += OnAdd;
m_LabelsList.onRemoveCallback += OnRemove;
m_LabelsList.onReorderCallbackWithDetails += OnReorder;
AutoAssignIdsIfNeeded();
m_MoveDownButton.clicked += MoveSelectedItemDown;
m_MoveUpButton.clicked += MoveSelectedItemUp;
void OnReorder(ReorderableList list, int oldIndex, int newIndex)
public override void PostRemoveOperations()
if (!autoAssign)
return;
AutoAssignIds();
AutoAssignIdsIfNeeded();
void OnRemove(ReorderableList list)
void MoveSelectedItemUp()
if (list.index != -1)
list.serializedProperty.DeleteArrayElementAtIndex(list.index);
var selectedIndex = m_LabelListView.selectedIndex;
if (selectedIndex > 0)
{
var currentProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(ILabelEntry.label));
var topProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex - 1)
.FindPropertyRelative(nameof(ILabelEntry.label));
if (autoAssign)
AutoAssignIds();
var tmpString = topProperty.stringValue;
topProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
if (!AutoAssign)
{
var currentIdProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(IdLabelEntry.id));
var topIdProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex - 1)
.FindPropertyRelative(nameof(IdLabelEntry.id));
this.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
}
var tmpInt = topIdProperty.intValue;
topIdProperty.intValue = currentIdProperty.intValue;
currentIdProperty.intValue = tmpInt;
}
void OnAdd(ReorderableList list)
{
int maxLabel = Int32.MinValue;
if (list.serializedProperty.arraySize == 0)
maxLabel = -1;
m_LabelListView.selectedIndex = selectedIndex - 1;
for (int i = 0; i < list.serializedProperty.arraySize; i++)
{
var item = list.serializedProperty.GetArrayElementAtIndex(i);
maxLabel = math.max(maxLabel, item.FindPropertyRelative(nameof(IdLabelEntry.id)).intValue);
serializedObject.ApplyModifiedProperties();
RefreshAddedLabels();
m_LabelListView.Refresh();
RefreshListViewHeight();
var index = list.serializedProperty.arraySize;
list.serializedProperty.InsertArrayElementAtIndex(index);
var element = list.serializedProperty.GetArrayElementAtIndex(index);
var idProperty = element.FindPropertyRelative(nameof(IdLabelEntry.id));
idProperty.intValue = maxLabel + 1;
var labelProperty = element.FindPropertyRelative(nameof(IdLabelEntry.label));
labelProperty.stringValue = "";
if (autoAssign)
AutoAssignIds();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
void DrawElement(Rect rect, int index, bool isactive, bool isfocused)
void MoveSelectedItemDown()
var element = m_LabelsList.serializedProperty.GetArrayElementAtIndex(index);
var idProperty = element.FindPropertyRelative(nameof(IdLabelEntry.id));
var labelProperty = element.FindPropertyRelative(nameof(IdLabelEntry.label));
using (var change = new EditorGUI.ChangeCheckScope())
var selectedIndex = m_LabelListView.selectedIndex;
if (selectedIndex > -1 && selectedIndex < m_SerializedLabelsArray.arraySize - 1)
var contentRect = new Rect(rect.position, new Vector2(rect.width, EditorGUIUtility.singleLineHeight));
using (new EditorGUI.DisabledScope(autoAssign))
var currentProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(ILabelEntry.label));
var bottomProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex + 1)
.FindPropertyRelative(nameof(ILabelEntry.label));
var tmpString = bottomProperty.stringValue;
bottomProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
if (!AutoAssign)
var newLabel = EditorGUI.IntField(contentRect, nameof(IdLabelEntry.id), idProperty.intValue);
if (change.changed)
{
idProperty.intValue = newLabel;
if (autoAssign)
AutoAssignIds();
}
var currentIdProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(IdLabelEntry.id));
var bottomIdProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex + 1)
.FindPropertyRelative(nameof(IdLabelEntry.id));
var tmpInt = bottomIdProperty.intValue;
bottomIdProperty.intValue = currentIdProperty.intValue;
currentIdProperty.intValue = tmpInt;
m_LabelListView.selectedIndex = selectedIndex + 1;
serializedObject.ApplyModifiedProperties();
RefreshAddedLabels();
m_LabelListView.Refresh();
RefreshListViewHeight();
using (var change = new EditorGUI.ChangeCheckScope())
}
protected override void SetupPresentLabelsListView()
{
base.SetupPresentLabelsListView();
VisualElement MakeItem() =>
new IdLabelElementInLabelConfig(this, m_SerializedLabelsArray);
void BindItem(VisualElement e, int i)
var contentRect = new Rect(rect.position + new Vector2(0, EditorGUIUtility.singleLineHeight), new Vector2(rect.width, EditorGUIUtility.singleLineHeight));
var newLabel = EditorGUI.TextField(contentRect, nameof(IdLabelEntry.label), labelProperty.stringValue);
if (change.changed)
if (e is IdLabelElementInLabelConfig addedLabel)
labelProperty.stringValue = newLabel;
addedLabel.indexInList = i;
addedLabel.labelTextField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(IdLabelEntry.label)));
addedLabel.labelIdTextField.value = m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(IdLabelEntry.id)).intValue.ToString();
m_LabelListView.bindItem = BindItem;
m_LabelListView.makeItem = MakeItem;
bool autoAssign => serializedObject.FindProperty(nameof(IdLabelConfig.autoAssignIds)).boolValue;
protected override IdLabelEntry CreateLabelEntryFromLabelString(SerializedProperty serializedArray,
string labelToAdd)
{
int maxLabel = Int32.MinValue;
if (serializedArray.arraySize == 0)
maxLabel = -1;
public override void OnInspectorGUI()
{
serializedObject.Update();
var autoAssignIdsProperty = serializedObject.FindProperty(nameof(IdLabelConfig.autoAssignIds));
using (var change = new EditorGUI.ChangeCheckScope())
for (int i = 0; i < serializedArray.arraySize; i++)
EditorGUILayout.PropertyField(autoAssignIdsProperty, new GUIContent("Auto Assign IDs"));
if (change.changed && autoAssignIdsProperty.boolValue)
AutoAssignIds();
var item = serializedArray.GetArrayElementAtIndex(i);
maxLabel = math.max(maxLabel, item.FindPropertyRelative(nameof(IdLabelEntry.id)).intValue);
if (autoAssignIdsProperty.boolValue)
return new IdLabelEntry
using (var change = new EditorGUI.ChangeCheckScope())
{
var startingLabelIdProperty = serializedObject.FindProperty(nameof(IdLabelConfig.startingLabelId));
EditorGUILayout.PropertyField(startingLabelIdProperty, new GUIContent("Starting Label ID"));
if (change.changed)
AutoAssignIds();
}
}
id = maxLabel + 1,
label = labelToAdd
};
}
m_LabelsList.DoLayoutList();
this.serializedObject.ApplyModifiedProperties();
protected override void AppendLabelEntryToSerializedArray(SerializedProperty serializedArray,
IdLabelEntry labelEntry)
{
var index = serializedArray.arraySize;
serializedArray.InsertArrayElementAtIndex(index);
var element = serializedArray.GetArrayElementAtIndex(index);
var idProperty = element.FindPropertyRelative(nameof(IdLabelEntry.id));
idProperty.intValue = labelEntry.id;
var labelProperty = element.FindPropertyRelative(nameof(ILabelEntry.label));
labelProperty.stringValue = labelEntry.label;
public bool AutoAssign => serializedObject.FindProperty(nameof(IdLabelConfig.autoAssignIds)).boolValue;
void AutoAssignIds()
{

return;
var startingLabelId = (StartingLabelId)serializedObject.FindProperty(nameof(IdLabelConfig.startingLabelId)).enumValueIndex;
var startingLabelId =
(StartingLabelId) serializedObject.FindProperty(nameof(IdLabelConfig.startingLabelId)).enumValueIndex;
serializedProperty.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(IdLabelEntry.id)).intValue = nextId;
serializedProperty.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(IdLabelEntry.id)).intValue =
nextId;
serializedObject.ApplyModifiedProperties();
ChangesHappeningInForeground = true;
RefreshListDataAndPresentation();
EditorUtility.SetDirty(target);
}
void AutoAssignIdsIfNeeded()
{
if (AutoAssign)
{
AutoAssignIds();
}
}
public int IndexOfGivenIdInSerializedLabelsArray(int id)
{
for (int i = 0; i < m_SerializedLabelsArray.arraySize; i++)
{
var element = m_SerializedLabelsArray.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(IdLabelEntry.id));
if (element.intValue == id)
{
return i;
}
}
return -1;
}
}
class IdLabelElementInLabelConfig : LabelElementInLabelConfig<IdLabelEntry>
{
protected override string UxmlPath => k_UxmlDir + "IdLabelElementInLabelConfig.uxml";
public TextField labelIdTextField;
public IdLabelElementInLabelConfig(LabelConfigEditor<IdLabelEntry> editor, SerializedProperty labelsArray) :
base(editor, labelsArray)
{
}
protected override void InitExtended()
{
labelIdTextField = this.Q<TextField>("label-id-value");
labelIdTextField.isDelayed = true;
labelIdTextField.SetEnabled(!((IdLabelConfigEditor) m_LabelConfigEditor).AutoAssign);
labelIdTextField.RegisterValueChangedCallback(evt =>
{
if(int.TryParse(evt.newValue, out int parsedId))
{
m_LabelsArray.GetArrayElementAtIndex(indexInList).FindPropertyRelative(nameof(IdLabelEntry.id))
.intValue = parsedId;
if (m_LabelsArray.serializedObject.hasModifiedProperties)
{
m_LabelsArray.serializedObject.ApplyModifiedProperties();
m_LabelConfigEditor.ChangesHappeningInForeground = true;
m_LabelConfigEditor.RefreshListDataAndPresentation();
}
var index = ((IdLabelConfigEditor)m_LabelConfigEditor).IndexOfGivenIdInSerializedLabelsArray(parsedId);
if (index != -1 && index != indexInList)
{
//The listview recycles child visual elements and that causes the RegisterValueChangedCallback event to be called when scrolling.
//Therefore, we need to make sure we are not in this code block just because of scrolling, but because the user is actively changing one of the labels.
//The index check is for this purpose.
Debug.LogWarning("A label with the ID " + evt.newValue + " has already been added to this label configuration.");
}
}
else
{
Debug.LogError("Provided id is not a valid integer. Please provide integer values.");
labelIdTextField.value = evt.previousValue;
}
});
}
}
}

2
com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs.meta


fileFormatVersion: 2
guid: 910dd3186e1c4fad8eb6aca9b9ee0f48
guid: 43cb2a3117353435abe59ca5217974a8
timeCreated: 1585940009

929
com.unity.perception/Editor/GroundTruth/LabelingEditor.cs


using Unity.Entities;
using UnityEditorInternal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using Button = UnityEngine.UIElements.Button;
using Toggle = UnityEngine.UIElements.Toggle;
namespace UnityEditor.Perception.GroundTruth
{

ReorderableList m_LabelsList;
VisualElement m_Root;
VisualElement m_ManualLabelingContainer;
VisualElement m_AutoLabelingContainer;
VisualElement m_FromLabelConfigsContainer;
VisualElement m_SuggestedLabelsContainer;
VisualElement m_SuggestedOnNamePanel;
VisualElement m_SuggestedOnPathPanel;
ListView m_CurrentLabelsListView;
ListView m_SuggestedLabelsListViewFromName;
ListView m_SuggestedLabelsListViewFromPath;
ScrollView m_LabelConfigsScrollView;
PopupField<string> m_LabelingSchemesPopup;
Button m_AddButton;
Button m_AddAutoLabelToConfButton;
Toggle m_AutoLabelingToggle;
Label m_CurrentAutoLabel;
Label m_CurrentAutoLabelTitle;
Label m_AddManualLabelsTitle;
Labeling m_Labeling;
string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
string m_UxmlPath;
List<string> m_SuggestedLabelsBasedOnName = new List<string>();
List<string> m_SuggestedLabelsBasedOnPath = new List<string>();
public List<string> CommonLabels { get; private set; } = new List<string>();
List<Type> m_LabelConfigTypes;
readonly List<ScriptableObject> m_AllLabelConfigsInProject = new List<ScriptableObject>();
readonly List<AssetLabelingScheme> m_LabelingSchemes = new List<AssetLabelingScheme>();
/// <summary>
/// List of separator characters used for parsing asset names for auto labeling or label suggestion purposes
/// </summary>
public static readonly string[] NameSeparators = {".", "-", "_"};
/// <summary>
/// List of separator characters used for parsing asset paths for auto labeling or label suggestion purposes
/// </summary>
public static readonly string[] PathSeparators = {"/"};
void OnEnable()
{
m_LabelConfigTypes = AddToConfigWindow.FindAllSubTypes(typeof(LabelConfig<>));
var mySerializedObject = new SerializedObject(serializedObject.targetObjects[0]);
m_Labeling = mySerializedObject.targetObject as Labeling;
m_UxmlPath = m_UxmlDir + "Labeling_Main.uxml";
m_Root = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree();
m_CurrentLabelsListView = m_Root.Q<ListView>("current-labels-listview");
m_SuggestedLabelsListViewFromName = m_Root.Q<ListView>("suggested-labels-name-listview");
m_SuggestedLabelsListViewFromPath = m_Root.Q<ListView>("suggested-labels-path-listview");
m_LabelConfigsScrollView = m_Root.Q<ScrollView>("label-configs-scrollview");
m_SuggestedOnNamePanel = m_Root.Q<VisualElement>("suggested-labels-from-name");
m_SuggestedOnPathPanel = m_Root.Q<VisualElement>("suggested-labels-from-path");
m_AddButton = m_Root.Q<Button>("add-label");
m_CurrentAutoLabel = m_Root.Q<Label>("current-auto-label");
m_CurrentAutoLabelTitle = m_Root.Q<Label>("current-auto-label-title");
m_AutoLabelingToggle = m_Root.Q<Toggle>("auto-or-manual-toggle");
m_ManualLabelingContainer = m_Root.Q<VisualElement>("manual-labeling");
m_AutoLabelingContainer = m_Root.Q<VisualElement>("automatic-labeling");
m_FromLabelConfigsContainer = m_Root.Q<VisualElement>("from-label-configs");
m_SuggestedLabelsContainer = m_Root.Q<VisualElement>("suggested-labels");
m_AddAutoLabelToConfButton = m_Root.Q<Button>("add-auto-label-to-config");
m_AddManualLabelsTitle = m_Root.Q<Label>("add-manual-labels-title");
var dropdownParent = m_Root.Q<VisualElement>("drop-down-parent");
m_ItIsPossibleToAddMultipleAutoLabelsToConfig = false;
InitializeLabelingSchemes(dropdownParent);
AssesAutoLabelingStatus();
m_FirstItemLabelsArray = serializedObject.FindProperty(nameof(Labeling.labels));
if (serializedObject.targetObjects.Length > 1)
{
var addedTitle = m_Root.Q<Label>("added-labels-title");
addedTitle.text = "Common Labels of Selected Items";
m_SuggestedOnNamePanel.style.display = DisplayStyle.None;
m_AddAutoLabelToConfButton.text = "Add Automatic Labels of All Selected Assets to Config...";
}
else
{
m_AddAutoLabelToConfButton.text = "Add to Label Config...";
}
m_AddAutoLabelToConfButton.clicked += () =>
{
AddToConfigWindow.ShowWindow(CreateUnionOfAllLabels().ToList());
};
m_AddButton.clicked += () =>
{
var labelsUnion = CreateUnionOfAllLabels();
var newLabel = FindNewLabelValue(labelsUnion);
foreach (var targetObject in targets)
{
if (targetObject is Labeling labeling)
{
var serializedLabelingObject2 = new SerializedObject(labeling);
var serializedLabelArray2 = serializedLabelingObject2.FindProperty(nameof(Labeling.labels));
serializedLabelArray2.InsertArrayElementAtIndex(serializedLabelArray2.arraySize);
serializedLabelArray2.GetArrayElementAtIndex(serializedLabelArray2.arraySize-1).stringValue = newLabel;
serializedLabelingObject2.ApplyModifiedProperties();
serializedLabelingObject2.SetIsDifferentCacheDirty();
serializedObject.SetIsDifferentCacheDirty();
}
}
ChangesHappeningInForeground = true;
RefreshManualLabelingData();
};
m_AutoLabelingToggle.RegisterValueChangedCallback(evt =>
{
AutoLabelToggleChanged();
});
ChangesHappeningInForeground = true;
m_Root.schedule.Execute(CheckForModelChanges).Every(30);
}
int m_PreviousLabelsArraySize = -1;
/// <summary>
/// This boolean is used to signify when changes in the model are triggered directly from the inspector UI by the user.
/// In these cases, the scheduled model checker does not need to update the UI again.
/// </summary>
public bool ChangesHappeningInForeground { get; set; }
SerializedProperty m_FirstItemLabelsArray;
void CheckForModelChanges()
{
if (ChangesHappeningInForeground)
{
ChangesHappeningInForeground = false;
m_PreviousLabelsArraySize = m_FirstItemLabelsArray.arraySize;
return;
}
if (m_FirstItemLabelsArray.arraySize != m_PreviousLabelsArraySize)
{
AssesAutoLabelingStatus();
RefreshManualLabelingData();
m_PreviousLabelsArraySize = m_FirstItemLabelsArray.arraySize;
}
}
bool SerializedObjectHasValidLabelingScheme(SerializedObject serObj)
{
var schemeName = serObj.FindProperty(nameof(Labeling.autoLabelingSchemeType)).stringValue;
return IsValidLabelingSchemeName(schemeName);
}
bool IsValidLabelingSchemeName(string schemeName)
{
return schemeName != string.Empty &&
m_LabelingSchemes.FindAll(scheme => scheme.GetType().Name == schemeName).Count > 0;
}
bool m_ItIsPossibleToAddMultipleAutoLabelsToConfig;
void UpdateUiAspects()
{
m_ManualLabelingContainer.SetEnabled(!m_AutoLabelingToggle.value);
m_AutoLabelingContainer.SetEnabled(m_AutoLabelingToggle.value);
m_AddManualLabelsTitle.style.display = m_AutoLabelingToggle.value ? DisplayStyle.None : DisplayStyle.Flex;
m_FromLabelConfigsContainer.style.display = m_AutoLabelingToggle.value ? DisplayStyle.None : DisplayStyle.Flex;
m_SuggestedLabelsContainer.style.display = m_AutoLabelingToggle.value ? DisplayStyle.None : DisplayStyle.Flex;
m_CurrentLabelsListView.style.minHeight = m_AutoLabelingToggle.value ? 70 : 120;
if (!m_AutoLabelingToggle.value || serializedObject.targetObjects.Length > 1 ||
!SerializedObjectHasValidLabelingScheme(new SerializedObject(serializedObject.targetObjects[0])))
{
m_CurrentAutoLabel.style.display = DisplayStyle.None;
m_AddAutoLabelToConfButton.SetEnabled(false);
}
else
{
m_CurrentAutoLabel.style.display = DisplayStyle.Flex;
m_AddAutoLabelToConfButton.SetEnabled(true);
}
if(m_AutoLabelingToggle.value && serializedObject.targetObjects.Length > 1 && m_ItIsPossibleToAddMultipleAutoLabelsToConfig)
{
m_AddAutoLabelToConfButton.SetEnabled(true);
}
if (serializedObject.targetObjects.Length == 1)
{
m_AutoLabelingToggle.text = "Use Automatic Labeling";
}
else
{
m_CurrentAutoLabelTitle.text = "Select assets individually to inspect their automatic labels.";
m_AutoLabelingToggle.text = "Use Automatic Labeling for All Selected Items";
}
}
void UpdateCurrentAutoLabelValue(SerializedObject serObj)
{
var array = serObj.FindProperty(nameof(Labeling.labels));
if (array.arraySize > 0)
{
m_CurrentAutoLabel.text = array.GetArrayElementAtIndex(0).stringValue;
}
}
bool AreSelectedAssetsCompatibleWithAutoLabelScheme(AssetLabelingScheme scheme)
{
foreach (var asset in serializedObject.targetObjects)
{
string label = scheme.GenerateLabel(asset);
if (label == null)
{
return false;
}
}
return true;
}
public void OnEnable()
void InitializeLabelingSchemes(VisualElement parent)
m_LabelsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(Labeling.labels)), true, false, true, true);
m_LabelsList.drawElementCallback = DrawElement;
m_LabelsList.onAddCallback += OnAdd;
m_LabelsList.onRemoveCallback += OnRemove;
m_LabelsList.onReorderCallbackWithDetails += OnReordered;
//this function should be called only once during the lifecycle of the editor element
AssetLabelingScheme labelingScheme = new AssetNameLabelingScheme();
if (AreSelectedAssetsCompatibleWithAutoLabelScheme(labelingScheme)) m_LabelingSchemes.Add(labelingScheme);
labelingScheme = new AssetFileNameLabelingScheme();
if (AreSelectedAssetsCompatibleWithAutoLabelScheme(labelingScheme)) m_LabelingSchemes.Add(labelingScheme);
labelingScheme = new CurrentOrParentsFolderNameLabelingScheme();
if (AreSelectedAssetsCompatibleWithAutoLabelScheme(labelingScheme)) m_LabelingSchemes.Add(labelingScheme);
var descriptions = m_LabelingSchemes.Select(scheme => scheme.Description).ToList();
descriptions.Insert(0, "<Select Scheme>");
m_LabelingSchemesPopup = new PopupField<string>(descriptions, 0) {label = "Labeling Scheme"};
m_LabelingSchemesPopup.style.marginLeft = 0;
parent.Add(m_LabelingSchemesPopup);
m_LabelingSchemesPopup.RegisterValueChangedCallback(evt => AssignAutomaticLabelToSelectedAssets());
void OnRemove(ReorderableList list)
void AutoLabelToggleChanged()
if (list.index != -1)
UpdateUiAspects();
if (!m_AutoLabelingToggle.value)
{
m_ItIsPossibleToAddMultipleAutoLabelsToConfig = false;
foreach (var targetObj in serializedObject.targetObjects)
{
var serObj = new SerializedObject(targetObj);
serObj.FindProperty(nameof(Labeling.useAutoLabeling)).boolValue = false;
if (SerializedObjectHasValidLabelingScheme(serObj))
{
//asset already had a labeling scheme before auto labeling was disabled, which means it has auto label(s) attached. these should be cleared now.
serObj.FindProperty(nameof(Labeling.labels)).ClearArray();
}
serObj.FindProperty(nameof(Labeling.autoLabelingSchemeType)).stringValue = string.Empty;
m_LabelingSchemesPopup.index = 0;
serObj.ApplyModifiedProperties();
serObj.SetIsDifferentCacheDirty();
}
}
ChangesHappeningInForeground = true;
RefreshManualLabelingData();
}
void AssignAutomaticLabelToSelectedAssets()
{
//the 0th index of this popup is "<Select Scheme>" and should not do anything
if (m_LabelingSchemesPopup.index == 0)
{
return;
}
m_ItIsPossibleToAddMultipleAutoLabelsToConfig = true;
var labelingScheme = m_LabelingSchemes[m_LabelingSchemesPopup.index - 1];
foreach (var targetObj in serializedObject.targetObjects)
{
var serObj = new SerializedObject(targetObj);
serObj.FindProperty(nameof(Labeling.useAutoLabeling)).boolValue = true; //only set this flag once the user has actually chosen a scheme, otherwise, we will not touch the flag
serObj.FindProperty(nameof(Labeling.autoLabelingSchemeType)).stringValue = labelingScheme.GetType().Name;
var serLabelsArray = serObj.FindProperty(nameof(Labeling.labels));
serLabelsArray.ClearArray();
serLabelsArray.InsertArrayElementAtIndex(0);
var label = labelingScheme.GenerateLabel(targetObj);
serLabelsArray.GetArrayElementAtIndex(0).stringValue = label;
if (targetObj == serializedObject.targetObjects[0] && serializedObject.targetObjects.Length == 1)
{
UpdateCurrentAutoLabelValue(serObj);
}
serObj.ApplyModifiedProperties();
serObj.SetIsDifferentCacheDirty();
}
UpdateUiAspects();
ChangesHappeningInForeground = true;
RefreshManualLabelingData();
}
void AssesAutoLabelingStatus()
{
var enabledOrNot = true;
if (serializedObject.targetObjects.Length == 1)
{
var serObj = new SerializedObject(serializedObject.targetObjects[0]);
var enabled = serObj.FindProperty(nameof(Labeling.useAutoLabeling)).boolValue;
m_AutoLabelingToggle.value = enabled;
var currentLabelingSchemeName = serObj.FindProperty(nameof(Labeling.autoLabelingSchemeType)).stringValue;
if (IsValidLabelingSchemeName(currentLabelingSchemeName))
{
m_LabelingSchemesPopup.index =
m_LabelingSchemes.FindIndex(scheme => scheme.GetType().Name.ToString() == currentLabelingSchemeName) + 1;
}
UpdateCurrentAutoLabelValue(serObj);
}
else
{
string unifiedLabelingScheme = null;
var allAssetsUseSameLabelingScheme = true;
foreach (var targetObj in serializedObject.targetObjects)
{
var serObj = new SerializedObject(targetObj);
var enabled = serObj.FindProperty(nameof(Labeling.useAutoLabeling)).boolValue;
enabledOrNot &= enabled;
var schemeName = serObj.FindProperty(nameof(Labeling.autoLabelingSchemeType)).stringValue;
if (schemeName == string.Empty)
{
//if any of the selected assets does not have a labeling scheme, they can't all have the same valid scheme
allAssetsUseSameLabelingScheme = false;
}
if (allAssetsUseSameLabelingScheme)
{
if (unifiedLabelingScheme == null)
{
unifiedLabelingScheme = schemeName;
}
else if (unifiedLabelingScheme != schemeName)
{
allAssetsUseSameLabelingScheme = false;
}
}
}
m_AutoLabelingToggle.value = enabledOrNot;
if (allAssetsUseSameLabelingScheme)
{
//all selected assets have the same scheme recorded in their serialized objects
m_LabelingSchemesPopup.index =
m_LabelingSchemes.FindIndex(scheme => scheme.GetType().Name.ToString() == unifiedLabelingScheme) + 1;
m_ItIsPossibleToAddMultipleAutoLabelsToConfig = enabledOrNot;
//if all selected assets have the same scheme recorded in their serialized objects, and they all
//have auto labeling enabled, we can now add all auto labels to a config
}
else
{
//the selected DO NOT have the same scheme recorded in their serialized objects
m_LabelingSchemesPopup.index = 0;
}
}
UpdateUiAspects();
}
HashSet<string> CreateUnionOfAllLabels()
{
HashSet<String> result = new HashSet<string>();
foreach (var obj in targets)
{
if (obj is Labeling labeling)
{
result.UnionWith(labeling.labels);
}
}
return result;
}
string FindNewLabelValue(HashSet<string> labels)
{
string baseLabel = "New Label";
string label = baseLabel;
int count = 1;
while (labels.Contains(label))
{
label = baseLabel + "_" + count++;
}
return label;
}
public override VisualElement CreateInspectorGUI()
{
serializedObject.Update();
m_Labeling = serializedObject.targetObject as Labeling;
RefreshCommonLabels();
RefreshSuggestedLabelLists();
RefreshLabelConfigsList();
SetupListsAndScrollers();
return m_Root;
}
void RefreshLabelConfigsList()
{
List<string> labelConfigGuids = new List<string>();
foreach (var type in m_LabelConfigTypes)
{
labelConfigGuids.AddRange(AssetDatabase.FindAssets("t:"+type.Name));
}
m_AllLabelConfigsInProject.Clear();
foreach (var configGuid in labelConfigGuids)
{
var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(AssetDatabase.GUIDToAssetPath(configGuid));
m_AllLabelConfigsInProject.Add(asset);
}
}
void RemoveAddedLabelsFromSuggestedLists()
{
m_SuggestedLabelsBasedOnName.RemoveAll(s => CommonLabels.Contains(s));
m_SuggestedLabelsBasedOnPath.RemoveAll(s => CommonLabels.Contains(s));
}
void RefreshSuggestedLabelLists()
{
m_SuggestedLabelsBasedOnName.Clear();
m_SuggestedLabelsBasedOnPath.Clear();
//based on name
if (serializedObject.targetObjects.Length == 1)
{
string assetName = serializedObject.targetObject.name;
var pieces = assetName.Split(NameSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
if (pieces.Count > 1)
{
//means the asset name was actually split
m_SuggestedLabelsBasedOnName.Add(assetName);
}
m_SuggestedLabelsBasedOnName.AddRange(pieces);
}
//based on path
string assetPath = GetAssetOrPrefabPath(m_Labeling.gameObject);
//var prefabObject = PrefabUtility.GetCorrespondingObjectFromSource(m_Labeling.gameObject);
if (assetPath != null)
{
var stringList = assetPath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
stringList.Reverse();
m_SuggestedLabelsBasedOnPath.AddRange(stringList);
}
foreach (var targetObject in targets)
var value = labeling.labels[list.index];
foreach (var t in targets)
if (targetObject == target)
continue; //we have already taken care of this one above
assetPath = GetAssetOrPrefabPath(((Labeling)targetObject).gameObject);
//prefabObject = PrefabUtility.GetCorrespondingObjectFromSource(((Labeling)targetObject).gameObject);
if (assetPath != null)
((Labeling)t).labels.Remove(value);
var stringList = assetPath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
m_SuggestedLabelsBasedOnPath = m_SuggestedLabelsBasedOnPath.Intersect(stringList).ToList();
RemoveAddedLabelsFromSuggestedLists();
//Debug.Log("list update, source list count is:" + m_SuggestedLabelsBasedOnPath.Count);
Labeling labeling => (Labeling)target;
public void RefreshManualLabelingData()
{
serializedObject.SetIsDifferentCacheDirty();
serializedObject.Update();
RefreshCommonLabels();
RefreshSuggestedLabelLists();
SetupSuggestedLabelsListViews();
SetupCurrentLabelsListView();
UpdateSuggestedPanelVisibility();
}
void OnAdd(ReorderableList list)
void SetupListsAndScrollers()
foreach (var t in targets)
//Labels that have already been added to the target Labeling component
SetupCurrentLabelsListView();
//Labels suggested by the system, which the user can add
SetupSuggestedLabelsListViews();
//Add labels from Label Configs present in project
SetupLabelConfigsScrollView();
UpdateSuggestedPanelVisibility();
}
void UpdateSuggestedPanelVisibility()
{
m_SuggestedOnNamePanel.style.display = m_SuggestedLabelsBasedOnName.Count == 0 ? DisplayStyle.None : DisplayStyle.Flex;
m_SuggestedOnPathPanel.style.display = m_SuggestedLabelsBasedOnPath.Count == 0 ? DisplayStyle.None : DisplayStyle.Flex;
if (m_SuggestedLabelsBasedOnPath.Count == 0 && m_SuggestedLabelsBasedOnName.Count == 0)
var castedTarget = ((Labeling)t);
castedTarget.labels.Add("");
EditorUtility.SetDirty(castedTarget);
m_SuggestedLabelsContainer.style.display = DisplayStyle.None;
void OnReordered(ReorderableList list, int oldIndex, int newIndex)
void RefreshCommonLabels()
var label = labeling.labels[newIndex];
CommonLabels.Clear();
CommonLabels.AddRange(((Labeling)serializedObject.targetObjects[0]).labels);
foreach (var t in targets)
foreach (var obj in serializedObject.targetObjects)
var l = (Labeling)t;
if (this.labeling == l) continue;
CommonLabels = CommonLabels.Intersect(((Labeling) obj).labels).ToList();
}
}
ReorderLabels(l, label, newIndex);
void SetupCurrentLabelsListView()
{
m_CurrentLabelsListView.itemsSource = CommonLabels;
VisualElement MakeItem() =>
new AddedLabelEditor(this, m_CurrentLabelsListView);
void BindItem(VisualElement e, int i)
{
if (e is AddedLabelEditor addedLabel)
{
addedLabel.indexInList = i;
addedLabel.labelTextField.value = CommonLabels[i];
}
const int itemHeight = 35;
m_CurrentLabelsListView.bindItem = BindItem;
m_CurrentLabelsListView.makeItem = MakeItem;
m_CurrentLabelsListView.itemHeight = itemHeight;
m_CurrentLabelsListView.itemsSource = CommonLabels;
m_CurrentLabelsListView.selectionType = SelectionType.None;
static void ReorderLabels(Labeling labeling, string label, int newIndex)
void SetupSuggestedLabelsListViews()
if (labeling.labels.Contains(label))
SetupSuggestedLabelsBasedOnFlatList(m_SuggestedLabelsListViewFromName, m_SuggestedLabelsBasedOnName);
SetupSuggestedLabelsBasedOnFlatList(m_SuggestedLabelsListViewFromPath, m_SuggestedLabelsBasedOnPath);
}
void SetupSuggestedLabelsBasedOnFlatList(ListView labelsListView, List<string> stringList)
{
labelsListView.itemsSource = stringList;
VisualElement MakeItem() => new SuggestedLabelElement(this);
void BindItem(VisualElement e, int i)
labeling.labels.Remove(label);
if (newIndex < labeling.labels.Count)
labeling.labels.Insert(newIndex, label);
else
labeling.labels.Add(label);
if (e is SuggestedLabelElement suggestedLabel)
{
suggestedLabel.label.text = stringList[i];
}
}
const int itemHeight = 32;
labelsListView.bindItem = BindItem;
labelsListView.makeItem = MakeItem;
labelsListView.itemHeight = itemHeight;
labelsListView.selectionType = SelectionType.None;
}
void SetupLabelConfigsScrollView()
{
m_LabelConfigsScrollView.Clear();
foreach (var config in m_AllLabelConfigsInProject)
{
VisualElement configElement = new LabelConfigElement(this, config);
m_LabelConfigsScrollView.Add(configElement);
static void ReplaceLabel(Labeling labeling, string oldLabel, string newLabel)
/// <summary>
/// Get the path of the given asset in the project, or get the path of the given Scene GameObject's source prefab if any
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string GetAssetOrPrefabPath(UnityEngine.Object obj)
var idx = labeling.labels.IndexOf(oldLabel);
if (idx == -1) return;
labeling.labels[idx] = newLabel;
string assetPath = AssetDatabase.GetAssetPath(obj);
if (assetPath == string.Empty)
{
//this indicates that gObj is a scene object and not a prefab directly selected from the Project tab
var prefabObject = PrefabUtility.GetCorrespondingObjectFromSource(obj);
if (prefabObject)
{
assetPath = AssetDatabase.GetAssetPath(prefabObject);
}
}
return assetPath;
}
private void ReplaceLabel(int index, string newLabel)
class AddedLabelEditor : VisualElement
{
string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
public TextField labelTextField;
public int indexInList;
public AddedLabelEditor(LabelingEditor editor, ListView listView)
labeling.labels[index] = newLabel;
var uxmlPath = m_UxmlDir + "AddedLabelElement.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath).CloneTree(this);
labelTextField = this.Q<TextField>("label-value");
var removeButton = this.Q<Button>("remove-button");
var addToConfigButton = this.Q<Button>("add-to-config-button");
labelTextField.isDelayed = true;
labelTextField.RegisterValueChangedCallback((cEvent) =>
{
//Do not let the user define a duplicate label
if (editor.CommonLabels.Contains(cEvent.newValue) && editor.CommonLabels.IndexOf(cEvent.newValue) != indexInList)
{
//The listview recycles child visual elements and that causes the RegisterValueChangedCallback event to be called when scrolling.
//Therefore, we need to make sure we are not in this code block just because of scrolling, but because the user is actively changing one of the labels.
//The editor.CommonLabels.IndexOf(cEvent.newValue) != m_IndexInList check is for this purpose.
Debug.LogError("A label with the string " + cEvent.newValue + " has already been added to selected objects.");
editor.ChangesHappeningInForeground = true;
editor.RefreshManualLabelingData();
return;
}
bool shouldRefresh = false;
foreach (var targetObject in editor.targets)
{
if (targetObject is Labeling labeling)
{
var indexToModifyInTargetLabelList =
labeling.labels.IndexOf(editor.CommonLabels[indexInList]);
var serializedLabelingObject2 = new SerializedObject(labeling);
var serializedLabelArray2 = serializedLabelingObject2.FindProperty(nameof(Labeling.labels));
serializedLabelArray2.GetArrayElementAtIndex(indexToModifyInTargetLabelList).stringValue = cEvent.newValue;
shouldRefresh = shouldRefresh || serializedLabelArray2.serializedObject.hasModifiedProperties;
serializedLabelingObject2.ApplyModifiedProperties();
serializedLabelingObject2.SetIsDifferentCacheDirty();
}
}
//the value change event is called even when the listview recycles its child elements for re-use during scrolling, therefore, we should check to make sure there are modified properties, otherwise we would be doing the refresh for no reason (reduces scrolling performance)
if (shouldRefresh)
{
editor.ChangesHappeningInForeground = true;
editor.RefreshManualLabelingData();
}
});
addToConfigButton.clicked += () =>
{
AddToConfigWindow.ShowWindow(labelTextField.value);
};
removeButton.clicked += () =>
{
List<string> commonLabels = new List<string>();
commonLabels.Clear();
var firstTarget = editor.targets[0] as Labeling;
if (firstTarget != null)
{
commonLabels.AddRange(firstTarget.labels);
foreach (var obj in editor.targets)
{
commonLabels = commonLabels.Intersect(((Labeling) obj).labels).ToList();
}
foreach (var targetObject in editor.targets)
{
if (targetObject is Labeling labeling)
{
RemoveLabelFromLabelingSerObj(labeling, commonLabels);
}
}
editor.serializedObject.SetIsDifferentCacheDirty();
editor.RefreshManualLabelingData();
}
};
void ReplaceLabelAll(int index, string currentLabel)
void RemoveLabelFromLabelingSerObj(Labeling labeling, List<string> commonLabels)
var oldLabel = labeling.labels[index];
ReplaceLabel(index, currentLabel);
Dictionary<int, int> commonsIndexToLabelsIndex = new Dictionary<int, int>();
foreach (var t in targets)
for (int i = 0; i < labeling.labels.Count; i++)
var l = (Labeling)t;
string label = labeling.labels[i];
if (this.labeling == l) continue;
for (int j = 0; j < commonLabels.Count; j++)
{
string label2 = commonLabels[j];
ReplaceLabel(l, oldLabel, currentLabel);
if (string.Equals(label, label2) && !commonsIndexToLabelsIndex.ContainsKey(j))
{
commonsIndexToLabelsIndex.Add(j, i);
}
}
var serializedLabelingObject2 = new SerializedObject(labeling);
var serializedLabelArray2 = serializedLabelingObject2.FindProperty("labels");
serializedLabelArray2.DeleteArrayElementAtIndex(commonsIndexToLabelsIndex[indexInList]);
serializedLabelingObject2.ApplyModifiedProperties();
serializedLabelingObject2.SetIsDifferentCacheDirty();
}
void DrawElement(Rect rect, int index, bool isactive, bool isfocused)
class SuggestedLabelElement : VisualElement
{
string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
public Label label;
public SuggestedLabelElement(LabelingEditor editor)
using (var change = new EditorGUI.ChangeCheckScope())
var uxmlPath = m_UxmlDir + "SuggestedLabelElement.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath).CloneTree(this);
label = this.Q<Label>("label-value");
var addButton = this.Q<Button>("add-button");
addButton.clicked += () =>
var contentRect = new Rect(rect.x, rect.y, rect.width, rect.height);
foreach (var targetObject in editor.serializedObject.targetObjects)
{
if (targetObject is Labeling labeling)
{
if (labeling.labels.Contains(label.text))
continue; //Do not allow duplicate labels in one asset. Duplicate labels have no use and cause other operations (especially mutlt asset editing) to get messed up
var serializedLabelingObject2 = new SerializedObject(targetObject);
var serializedLabelArray2 = serializedLabelingObject2.FindProperty("labels");
serializedLabelArray2.InsertArrayElementAtIndex(serializedLabelArray2.arraySize);
serializedLabelArray2.GetArrayElementAtIndex(serializedLabelArray2.arraySize-1).stringValue = label.text;
serializedLabelingObject2.ApplyModifiedProperties();
serializedLabelingObject2.SetIsDifferentCacheDirty();
editor.serializedObject.SetIsDifferentCacheDirty();
}
}
editor.ChangesHappeningInForeground = true;
editor.RefreshManualLabelingData();
};
}
}
var value = EditorGUI.DelayedTextField(contentRect, labeling.labels[index]);
class LabelConfigElement : VisualElement
{
string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
bool m_Collapsed = true;
ListView m_LabelsListView;
VisualElement m_CollapseToggle;
public LabelConfigElement(LabelingEditor editor, ScriptableObject config)
{
var uxmlPath = m_UxmlDir + "ConfigElementForAddingLabelsFrom.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath).CloneTree(this);
m_LabelsListView = this.Q<ListView>("label-config-contents-listview");
var openButton = this.Q<Button>("open-config-button");
var configName = this.Q<Label>("config-name");
configName.text = config.name;
m_CollapseToggle = this.Q<VisualElement>("collapse-toggle");
if (change.changed)
openButton.clicked += () =>
{
Selection.SetActiveObjectWithContext(config, null);
};
var propertyInfo = config.GetType().GetProperty(IdLabelConfig.publicLabelEntriesFieldName);
if (propertyInfo != null)
{
var objectList = (IEnumerable) propertyInfo.GetValue(config);
var labelEntryList = objectList.Cast<ILabelEntry>().ToList();
var labelList = labelEntryList.Select(entry => entry.label).ToList();
m_LabelsListView.itemsSource = labelList;
VisualElement MakeItem()
ReplaceLabelAll(index, value);
var element = new SuggestedLabelElement(editor);
element.AddToClassList("label_add_from_config");
return element;
}
if (PrefabUtility.IsPartOfAnyPrefab(target))
void BindItem(VisualElement e, int i)
{
if (e is SuggestedLabelElement suggestedLabel)
EditorUtility.SetDirty(target);
suggestedLabel.label.text = labelList[i];
const int itemHeight = 27;
m_LabelsListView.bindItem = BindItem;
m_LabelsListView.makeItem = MakeItem;
m_LabelsListView.itemHeight = itemHeight;
m_LabelsListView.selectionType = SelectionType.None;
m_CollapseToggle.RegisterCallback<MouseUpEvent>(evt =>
{
m_Collapsed = !m_Collapsed;
ApplyCollapseState();
});
ApplyCollapseState();
public override void OnInspectorGUI()
void ApplyCollapseState()
m_LabelsList.DoLayoutList();
if (m_Collapsed)
{
m_CollapseToggle.AddToClassList("collapsed-toggle-state");
m_LabelsListView.AddToClassList("collapsed");
}
else
{
m_CollapseToggle.RemoveFromClassList("collapsed-toggle-state");
m_LabelsListView.RemoveFromClassList("collapsed");
}
}
}
/// <summary>
/// A labeling scheme based on which an automatic label can be produced for a given asset. E.g. based on asset name, asset path, etc.
/// </summary>
abstract class AssetLabelingScheme
{
/// <summary>
/// The description of how this scheme generates labels. Used in the dropdown menu in the UI.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Generate a label for the given asset
/// </summary>
/// <param name="asset"></param>
/// <returns></returns>
public abstract string GenerateLabel(UnityEngine.Object asset);
}
/// <summary>
/// Asset labeling scheme that outputs the given asset's name as its automatic label
/// </summary>
class AssetNameLabelingScheme : AssetLabelingScheme
{
///<inheritdoc/>
public override string Description => "Use asset name";
///<inheritdoc/>
public override string GenerateLabel(UnityEngine.Object asset)
{
return asset.name;
}
}
/// <summary>
/// Asset labeling scheme that outputs the given asset's file name, including extension, as its automatic label
/// </summary>
class AssetFileNameLabelingScheme : AssetLabelingScheme
{
///<inheritdoc/>
public override string Description => "Use file name with extension";
///<inheritdoc/>
public override string GenerateLabel(UnityEngine.Object asset)
{
string assetPath = LabelingEditor.GetAssetOrPrefabPath(asset);
var stringList = assetPath.Split(LabelingEditor.PathSeparators, StringSplitOptions.RemoveEmptyEntries)
.ToList();
return stringList.Count > 0 ? stringList.Last() : null;
}
}
/// <summary>
/// Asset labeling scheme that outputs the given asset's folder name as its automatic label
/// </summary>
class CurrentOrParentsFolderNameLabelingScheme : AssetLabelingScheme
{
///<inheritdoc/>
public override string Description => "Use the asset's folder name";
///<inheritdoc/>
public override string GenerateLabel(UnityEngine.Object asset)
{
string assetPath = LabelingEditor.GetAssetOrPrefabPath(asset);
var stringList = assetPath.Split(LabelingEditor.PathSeparators, StringSplitOptions.RemoveEmptyEntries)
.ToList();
return stringList.Count > 1 ? stringList[stringList.Count-2] : null;
}
}
}

2
com.unity.perception/Editor/GroundTruth/LabelingEditor.cs.meta


fileFormatVersion: 2
guid: 2e725508a34c40a0938c8d891b371980
guid: 387b8732b87094321af57795df93aec4
timeCreated: 1585933334

141
com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs


using System;
using System.Collections.Generic;
using System.Collections.Generic;
using UnityEditorInternal;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
class SemanticSegmentationLabelConfigEditor : Editor
class SemanticSegmentationLabelConfigEditor : LabelConfigEditor<SemanticSegmentationLabelEntry>
ReorderableList m_LabelsList;
const float k_Margin = 5f;
protected override void InitUiExtended()
{
m_MoveButtons.style.display = DisplayStyle.None;
m_IdSpecificUi.style.display = DisplayStyle.None;
}
static List<Color> s_StandardColors = new List<Color>()
public override void PostRemoveOperations()
{ }
protected override void SetupPresentLabelsListView()
Color.blue,
Color.green,
Color.red,
Color.white,
Color.yellow,
Color.gray
};
base.SetupPresentLabelsListView();
VisualElement MakeItem() =>
new ColoredLabelElementInLabelConfig(this, m_SerializedLabelsArray);
void BindItem(VisualElement e, int i)
{
if (e is ColoredLabelElementInLabelConfig addedLabel)
{
addedLabel.indexInList = i;
addedLabel.labelTextField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.label)));
addedLabel.colorField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color)));
}
}
public void OnEnable()
{
m_LabelsList = new ReorderableList(this.serializedObject, this.serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName), true, false, true, true);
m_LabelsList.elementHeight = EditorGUIUtility.singleLineHeight * 2 + k_Margin;
m_LabelsList.drawElementCallback = DrawElement;
m_LabelsList.onAddCallback += OnAdd;
m_LabelListView.bindItem = BindItem;
m_LabelListView.makeItem = MakeItem;
void OnAdd(ReorderableList list)
protected override SemanticSegmentationLabelEntry CreateLabelEntryFromLabelString(SerializedProperty serializedArray, string labelToAdd)
var standardColorList = new List<Color>(s_StandardColors);
for (int i = 0; i < list.serializedProperty.arraySize; i++)
var standardColorList = new List<Color>(SemanticSegmentationLabelConfig.s_StandardColors);
for (int i = 0; i < serializedArray.arraySize; i++)
var item = list.serializedProperty.GetArrayElementAtIndex(i);
var item = serializedArray.GetArrayElementAtIndex(i);
var index = list.serializedProperty.arraySize;
list.serializedProperty.InsertArrayElementAtIndex(index);
var element = list.serializedProperty.GetArrayElementAtIndex(index);
var labelProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.label));
labelProperty.stringValue = "";
var colorProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color));
if (standardColorList.Any())
colorProperty.colorValue = standardColorList.First();
else
colorProperty.colorValue = Random.ColorHSV(0, 1, .5f, 1, 1, 1);
var foundColor = standardColorList.Any() ? standardColorList.First() : Random.ColorHSV(0, 1, .5f, 1, 1, 1);
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
return new SemanticSegmentationLabelEntry
{
color = foundColor,
label = labelToAdd
};
void DrawElement(Rect rect, int index, bool isactive, bool isfocused)
protected override void AppendLabelEntryToSerializedArray(SerializedProperty serializedArray, SemanticSegmentationLabelEntry semanticSegmentationLabelEntry)
var element = m_LabelsList.serializedProperty.GetArrayElementAtIndex(index);
var index = serializedArray.arraySize;
serializedArray.InsertArrayElementAtIndex(index);
var element = serializedArray.GetArrayElementAtIndex(index);
var labelProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.label));
using (var change = new EditorGUI.ChangeCheckScope())
colorProperty.colorValue = semanticSegmentationLabelEntry.color;
var labelProperty = element.FindPropertyRelative(nameof(ILabelEntry.label));
labelProperty.stringValue = semanticSegmentationLabelEntry.label;
}
public int IndexOfGivenColorInSerializedLabelsArray(Color color)
{
for (int i = 0; i < m_SerializedLabelsArray.arraySize; i++)
var contentRect = new Rect(rect.position, new Vector2(rect.width, EditorGUIUtility.singleLineHeight));
var newLabel = EditorGUI.TextField(contentRect, nameof(SemanticSegmentationLabelEntry.label), labelProperty.stringValue);
if (change.changed)
{
labelProperty.stringValue = newLabel;
}
}
using (var change = new EditorGUI.ChangeCheckScope())
{
var contentRect = new Rect(rect.position + new Vector2(0, EditorGUIUtility.singleLineHeight), new Vector2(rect.width, EditorGUIUtility.singleLineHeight));
var newLabel = EditorGUI.ColorField(contentRect, nameof(SemanticSegmentationLabelEntry.color), colorProperty.colorValue);
if (change.changed)
var element = m_SerializedLabelsArray.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color));
if (element.colorValue == color)
colorProperty.colorValue = newLabel;
return i;
return -1;
}
public override void OnInspectorGUI()
class ColoredLabelElementInLabelConfig : LabelElementInLabelConfig<SemanticSegmentationLabelEntry>
{
protected override string UxmlPath => k_UxmlDir + "ColoredLabelElementInLabelConfig.uxml";
public ColorField colorField;
public ColoredLabelElementInLabelConfig(LabelConfigEditor<SemanticSegmentationLabelEntry> editor, SerializedProperty labelsArray) : base(editor, labelsArray)
{ }
protected override void InitExtended()
serializedObject.Update();
colorField = this.Q<ColorField>("label-color-value");
colorField.RegisterValueChangedCallback((cEvent) =>
{
var index = ((SemanticSegmentationLabelConfigEditor)m_LabelConfigEditor).IndexOfGivenColorInSerializedLabelsArray(cEvent.newValue);
if (index != -1 && index != indexInList)
{
//The listview recycles child visual elements and that causes the RegisterValueChangedCallback event to be called when scrolling.
//Therefore, we need to make sure we are not in this code block just because of scrolling, but because the user is actively changing one of the labels.
//The index check is for this purpose.
m_LabelsList.DoLayoutList();
this.serializedObject.ApplyModifiedProperties();
Debug.LogWarning("A label with the chosen color " + cEvent.newValue + " has already been added to this label configuration.");
}
});
}
}
}

4
com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs.meta


fileFormatVersion: 2
guid: e8cb4fead5b34d41884c1c9a77308c72
timeCreated: 1593454492
guid: c8e809a6323844c71aa3cb751a6ae9a1
timeCreated: 1585940009

20
com.unity.perception/Editor/Randomization/Uss/Styles.uss


.randomization__remove-item-button {
width: 12px;
height: 14px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/X.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/X.png");
}
.randomization__collapse-toggle {

width: 10px;
height: 10px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/FoldoutOpen.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/FoldoutOpen.png");
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/FoldoutClosed.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/FoldoutClosed.png");
}
.randomization__collapse-toggle:hover {

.randomization__chevron-left {
height: 12px;
width: 12px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/ChevronLeft.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/ChevronLeft.png");
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/ChevronRight.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/ChevronRight.png");
}

border-color: #191919;
padding: 2px 4px 2px 4px;
border-color: #808080;
padding: 7px;
border-radius: 4px;
white-space: normal;
margin-top: 4px;
margin-bottom: 4px;

padding: 2px;
}
/* Randomizer classes */
.randomizer__drag-bar {
width: 100px;

width: 16px;
height: 100%;
min-height: 20px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/DragHandle.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/DragHandle.png");
}
.randomizer__drag-handle:hover {

top: 9px;
width: 10px;
height: 10px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/Search.png");
background-image: resource("Packages/com.unity.perception/Editor/Icons/Search.png");
}
.randomizer__menu-search-bar .unity-base-text-field__input {

1
com.unity.perception/Runtime/GroundTruth/Labelers/BoundingBoxLabeler.cs


return;
m_AsyncAnnotations.Remove(frameCount);
using (s_BoundingBoxCallback.Auto())
{
m_BoundingBoxValues.Clear();

3
com.unity.perception/Runtime/GroundTruth/Labeling/IdLabelConfig.cs


using System.Diagnostics.CodeAnalysis;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
namespace UnityEngine.Perception.GroundTruth {
/// <summary>

public class IdLabelConfig : LabelConfig<IdLabelEntry>
{
/// <summary>
/// Whether the inspector will auto-assign ids based on the id of the first element.
/// </summary>

25
com.unity.perception/Runtime/GroundTruth/Labeling/LabelConfig.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Serialization;
namespace UnityEngine.Perception.GroundTruth

/// </summary>
public const string labelEntriesFieldName = nameof(m_LabelEntries);
/// <summary>
/// List of LabelEntry items added to this label configuration
/// </summary>
List<T> m_LabelEntries = new List<T>();
protected List<T> m_LabelEntries = new List<T>();
/// Name of the public accessor for the list of label entries, used for reflection purposes.
/// </summary>
public const string publicLabelEntriesFieldName = nameof(labelEntries);
/// <summary>
/// A sequence of <see cref="ILabelEntry"/> which defines the labels relevant for this configuration and their values.
/// </summary>
public IReadOnlyList<T> labelEntries => m_LabelEntries;

public bool TryGetMatchingConfigurationEntry(Labeling labeling, out T labelEntry)
{
return TryGetMatchingConfigurationEntry(labeling, out labelEntry, out int _);
}
/// <summary>
/// Name of the function that checks whether a given string matches any of the label entries in this label configuration, used for reflection purposes.
/// </summary>
public const string DoesLabelMatchAnEntryName = nameof(DoesLabelMatchAnEntry);
/// <summary>
/// Does the given string match any of the label entries added to this label configuration.
/// </summary>
/// <param name="label"></param>
/// <returns></returns>
public bool DoesLabelMatchAnEntry(string label)
{
return m_LabelEntries.Any(entry => string.Equals(entry.label, label));
}
/// <summary>

37
com.unity.perception/Runtime/GroundTruth/Labeling/Labeling.cs


using System;
using UnityEngine;
using UnityEditor;
using UnityEngine.Serialization;
namespace UnityEngine.Perception.GroundTruth

/// The label names to associate with the GameObject. Modifications to this list after the Update() step of the frame the object is created in are
/// not guaranteed to be reflected by labelers.
/// </summary>
[FormerlySerializedAs("classes")]
public List<string> labels = new List<string>();
[FormerlySerializedAs("classes")] public List<string> labels = new List<string>();
// /// <summary>
// /// A list for backing up the asset's manually added labels, so that if the user switches to auto labeling and back, the previously added labels can be revived
// /// </summary>
// public List<string> manualLabelsBackup = new List<string>();
/// <summary>
/// Whether this labeling component is currently using an automatic labeling scheme. When this is enabled, the asset can have only one label (the automatic one) and the user cannot add more labels.
/// </summary>
public bool useAutoLabeling;
/// <summary>
/// The specific subtype of AssetLabelingScheme that this component is using, if useAutoLabeling is enabled.
/// </summary>
public string autoLabelingSchemeType = string.Empty;
/// <summary>
/// The unique id of this labeling component instance

{
this.instanceId = instanceId;
}
void Awake()
{
m_Entity = World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity();

World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(m_Entity);
}
void Reset()
{
labels.Clear();
useAutoLabeling = false;
autoLabelingSchemeType = string.Empty;
#if UNITY_EDITOR
EditorUtility.SetDirty(gameObject);
#endif
}
/// <summary>
/// Refresh ground truth generation for the labeling of the attached GameObject. This is necessary when the
/// list of labels changes or when renderers or materials change on objects in the hierarchy.

World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<GroundTruthLabelSetupSystem>().RefreshLabeling(m_Entity);
World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<GroundTruthLabelSetupSystem>()
.RefreshLabeling(m_Entity);
}
}
}

13
com.unity.perception/Runtime/GroundTruth/Labeling/SemanticSegmentationLabelConfig.cs


using System;
using System.Collections.Generic;
namespace UnityEngine.Perception.GroundTruth {
/// <summary>

public class SemanticSegmentationLabelConfig : LabelConfig<SemanticSegmentationLabelEntry>
{
/// <summary>
/// List of standard color based on which this type of label configuration assigns new colors to added labels.
/// </summary>
public static readonly List<Color> s_StandardColors = new List<Color>()
{
Color.blue,
Color.green,
Color.red,
Color.white,
Color.yellow,
Color.gray
};
}
/// <summary>

8
com.unity.perception/Editor/AssetPreparation.meta


fileFormatVersion: 2
guid: 0c2b01a759f934aa099b2949e1580c97
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

396
com.unity.perception/Editor/GroundTruth/AddToConfigWindow.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
namespace UnityEditor.Perception.GroundTruth
{
class AddToConfigWindow : EditorWindow
{
VisualElement m_Root;
const string k_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
string m_UxmlPath;
static List<string> s_LabelValues = new List<string>();
static Label s_TitleLabel;
static Label s_Status;
public void SetStatus(string status)
{
s_Status.text = status;
s_Status.style.display = DisplayStyle.Flex;
}
List<string> m_AllLabelConfigGuids = new List<string>();
List<ScriptableObject> m_ConfigsContainingLabel = new List<ScriptableObject>();
List<ScriptableObject> m_ConfigsNotContainingLabel = new List<ScriptableObject>();
static ListView s_PresentConfigsListview;
static ListView s_NonPresentConfigsListview;
static ListView s_SelectedLabelsListview;
static Label s_CurrentlyPresentTitle;
static Label s_OtherConfigsTitle;
List<Type> m_LabelConfigTypes = new List<Type>();
public static void ShowWindow(string labelValue)
{
s_LabelValues.Clear();
s_LabelValues.Add(labelValue);
ShowWindow(s_LabelValues);
}
public static void ShowWindow(List<string> labelValues)
{
s_LabelValues = new List<string>(labelValues);
var window = GetWindow<AddToConfigWindow>();
if (s_Status != null)
{
s_Status.style.display = DisplayStyle.None;
}
if (labelValues.Count == 1)
{
if(s_TitleLabel != null)
s_TitleLabel.text = "Label: \"" + s_LabelValues.First() + "\"";
if (s_PresentConfigsListview != null)
{
s_PresentConfigsListview.style.display = DisplayStyle.Flex;
}
if (s_CurrentlyPresentTitle != null)
{
s_CurrentlyPresentTitle.style.display = DisplayStyle.Flex;
}
if (s_OtherConfigsTitle != null)
{
s_OtherConfigsTitle.text = "Other Label Configs in Project";
}
if (s_NonPresentConfigsListview != null)
{
s_NonPresentConfigsListview.style.height = 150;
}
if (s_SelectedLabelsListview != null)
{
s_SelectedLabelsListview.style.display = DisplayStyle.None;
}
window.titleContent = new GUIContent("Manage Label");
window.minSize = new Vector2(400, 390);
window.maxSize = new Vector2(700, 390);
}
else
{
if(s_TitleLabel != null)
s_TitleLabel.text = "Labels to Add";
if (s_PresentConfigsListview != null)
{
s_PresentConfigsListview.style.display = DisplayStyle.None;
}
if (s_CurrentlyPresentTitle != null)
{
s_CurrentlyPresentTitle.style.display = DisplayStyle.None;
}
if (s_OtherConfigsTitle != null)
{
s_OtherConfigsTitle.text = "All Label Configurations in Project";
}
if (s_NonPresentConfigsListview != null)
{
s_NonPresentConfigsListview.style.height = 250;
}
if (s_SelectedLabelsListview != null)
{
s_SelectedLabelsListview.style.display = DisplayStyle.Flex;
}
window.titleContent = new GUIContent("Manage Labels");
window.minSize = new Vector2(400, 370);
window.maxSize = new Vector2(700, 1000);
}
window.Init();
}
void Init()
{
Show();
m_ConfigsContainingLabel.Clear();
m_LabelConfigTypes = FindAllSubTypes(typeof(LabelConfig<>));
RefreshConfigAssets();
CheckInclusionInConfigs(m_AllLabelConfigGuids, s_LabelValues.Count == 1? s_LabelValues.First() : null);
SetupListViews();
}
void RefreshConfigAssets()
{
AssetDatabase.Refresh();
m_AllLabelConfigGuids.Clear();
foreach (var type in m_LabelConfigTypes)
{
m_AllLabelConfigGuids.AddRange(AssetDatabase.FindAssets("t:"+type.Name));
}
}
void SetupListViews()
{
//configs containing label
if (s_LabelValues.Count == 1)
{
//we are dealing with only one label
s_PresentConfigsListview.itemsSource = m_ConfigsContainingLabel;
VisualElement MakeItem1() => new ConfigElementLabelPresent(this, s_LabelValues.First());
void BindItem1(VisualElement e, int i)
{
if (e is ConfigElementLabelPresent element)
{
element.labelElement.text = m_ConfigsContainingLabel[i].name;
element.labelConfig = m_ConfigsContainingLabel[i];
}
}
s_PresentConfigsListview.itemHeight = 30;
s_PresentConfigsListview.bindItem = BindItem1;
s_PresentConfigsListview.makeItem = MakeItem1;
s_PresentConfigsListview.selectionType = SelectionType.None;
}
//Configs not containing label
s_NonPresentConfigsListview.itemsSource = m_ConfigsNotContainingLabel;
VisualElement MakeItem2() => new ConfigElementLabelNotPresent(this, s_LabelValues);
void BindItem2(VisualElement e, int i)
{
if (e is ConfigElementLabelNotPresent element)
{
element.labelElement.text = m_ConfigsNotContainingLabel[i].name;
element.labelConfig = m_ConfigsNotContainingLabel[i];
}
}
s_NonPresentConfigsListview.itemHeight = 30;
s_NonPresentConfigsListview.bindItem = BindItem2;
s_NonPresentConfigsListview.makeItem = MakeItem2;
s_NonPresentConfigsListview.selectionType = SelectionType.None;
//Selected labels
s_SelectedLabelsListview.itemsSource = s_LabelValues;
VisualElement MakeItem3() => new Label();
void BindItem3(VisualElement e, int i)
{
if (e is Label label)
{
label.text = s_LabelValues[i];
label.style.marginLeft = 2;
label.style.marginRight = 2;
}
}
s_SelectedLabelsListview.itemHeight = 20;
s_SelectedLabelsListview.bindItem = BindItem3;
s_SelectedLabelsListview.makeItem = MakeItem3;
s_SelectedLabelsListview.selectionType = SelectionType.None;
}
public void RefreshLists()
{
CheckInclusionInConfigs(m_AllLabelConfigGuids, s_LabelValues.Count == 1? s_LabelValues.First() : null);
s_PresentConfigsListview.Refresh();
s_NonPresentConfigsListview.Refresh();
}
void OnEnable()
{
m_UxmlPath = k_UxmlDir + "AddToConfigWindow.uxml";
m_Root = rootVisualElement;
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree(m_Root);
s_TitleLabel = m_Root.Q<Label>("title");
s_CurrentlyPresentTitle = m_Root.Q<Label>("currently-present-label");
s_OtherConfigsTitle = m_Root.Q<Label>("other-configs-label");
s_PresentConfigsListview = m_Root.Q<ListView>("current-configs-listview");
s_NonPresentConfigsListview = m_Root.Q<ListView>("other-configs-listview");
s_SelectedLabelsListview = m_Root.Q<ListView>("selected-labels-list");
s_Status = m_Root.Q<Label>("status");
s_Status.style.display = DisplayStyle.None;
}
void CheckInclusionInConfigs(List<string> configGuids, string label = null)
{
m_ConfigsContainingLabel.Clear();
m_ConfigsNotContainingLabel.Clear();
foreach (var configGuid in configGuids)
{
var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(AssetDatabase.GUIDToAssetPath(configGuid));
if (label != null)
{
//means we are dealing with only one label not a set
var methodInfo = asset.GetType().GetMethod(IdLabelConfig.DoesLabelMatchAnEntryName);
if (methodInfo == null)
continue;
object[] parametersArray = new object[1];
parametersArray[0] = label;
var labelExistsInConfig = (bool) methodInfo.Invoke(asset, parametersArray);
if (labelExistsInConfig)
{
m_ConfigsContainingLabel.Add(asset);
}
else
{
m_ConfigsNotContainingLabel.Add(asset);
}
}
else
{
m_ConfigsNotContainingLabel.Add(asset);
}
}
}
public static List<Type> FindAllSubTypes(Type superType)
{
Assembly assembly = Assembly.GetAssembly(superType);
Type[] types = assembly.GetTypes();
List<Type> subclasses = types.Where(t => IsSubclassOfRawGeneric(superType, t)).ToList();
return subclasses;
bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
while (toCheck != null && toCheck != typeof(object)) {
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
}
}
class ConfigElementLabelPresent : VisualElement
{
const string k_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
VisualElement m_Root;
ObjectField m_ConfigObjectField;
public Label labelElement;
public ScriptableObject labelConfig;
public ConfigElementLabelPresent(AddToConfigWindow window, string targetLabel)
{
var uxmlPath = k_UxmlDir + "ConfigElementLabelPresent.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath).CloneTree(this);
labelElement = this.Q<Label>("config-name");
var removeButton = this.Q<Button>("remove-from-config-button");
removeButton.text = "Remove Label";
var openButton = this.Q<Button>("open-config-button");
openButton.clicked += () =>
{
Selection.SetActiveObjectWithContext(labelConfig, null);
};
removeButton.clicked += () =>
{
var editor = Editor.CreateEditor(labelConfig);
if (editor is SemanticSegmentationLabelConfigEditor semanticEditor)
{
semanticEditor.RemoveLabel(targetLabel);
}
else if (editor is IdLabelConfigEditor idEditor)
{
idEditor.RemoveLabel(targetLabel);
}
window.RefreshLists();
Object.DestroyImmediate(editor);
};
}
}
class ConfigElementLabelNotPresent : VisualElement
{
const string k_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
VisualElement m_Root;
ObjectField m_ConfigObjectField;
public Label labelElement;
public ScriptableObject labelConfig;
public ConfigElementLabelNotPresent(AddToConfigWindow window, List<string> targetLabels)
{
var uxmlPath = k_UxmlDir + "ConfigElementLabelPresent.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath).CloneTree(this);
labelElement = this.Q<Label>("config-name");
var addButton = this.Q<Button>("remove-from-config-button");
addButton.text = targetLabels.Count ==1 ? "Add Label" : "Add All Labels";
var openButton = this.Q<Button>("open-config-button");
openButton.clicked += () =>
{
Selection.SetActiveObjectWithContext(labelConfig, null);
};
addButton.clicked += () =>
{
var editor = Editor.CreateEditor(labelConfig);
if (editor is SemanticSegmentationLabelConfigEditor semanticEditor)
{
foreach (var label in targetLabels)
{
semanticEditor.AddLabel(label);
}
}
else if (editor is IdLabelConfigEditor idEditor)
{
foreach (var label in targetLabels)
{
idEditor.AddLabel(label);
}
}
if (targetLabels.Count > 1)
{
window.SetStatus("All Labels Added to " + labelConfig.name);
}
window.RefreshLists();
Object.DestroyImmediate(editor);
};
}
}
}

3
com.unity.perception/Editor/GroundTruth/AddToConfigWindow.cs.meta


fileFormatVersion: 2
guid: 2485979b726fe4f3b829a3a4173a72c6
timeCreated: 1585933334

459
com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs


using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace UnityEditor.Perception.GroundTruth
{
abstract class LabelConfigEditor<T> : Editor where T : ILabelEntry
{
string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
string m_UxmlPath;
int m_AddedLabelsItemHeight = 37;
int m_OtherLabelsItemHeight = 27;
List<string> m_AddedLabels = new List<string>();
protected SerializedProperty m_SerializedLabelsArray;
HashSet<string> m_AllLabelsInProject = new HashSet<string>();
List<string> m_LabelsNotPresentInConfig = new List<string>();
bool m_UiInitialized;
bool m_EditorHasUi;
VisualElement m_Root;
Button m_SaveButton;
Button m_AddNewLabelButton;
Button m_RemoveAllButton;
Button m_AddAllButton;
Button m_ImportFromFileButton;
Button m_ExportToFileButton;
ListView m_NonPresentLabelsListView;
protected ListView m_LabelListView;
protected Button m_MoveUpButton;
protected Button m_MoveDownButton;
protected VisualElement m_MoveButtons;
protected VisualElement m_IdSpecificUi;
protected EnumField m_StartingIdEnumField;
protected Toggle m_AutoIdToggle;
public void OnEnable()
{
m_SerializedLabelsArray = serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName);
m_UiInitialized = false;
ChangesHappeningInForeground = true;
RefreshListDataAndPresentation();
}
int m_PreviousLabelsArraySize = -1;
/// <summary>
/// This boolean is used to signify when changes in the model are triggered directly from the inspector UI by the user.
/// In these cases, the scheduled model checker does not need to update the UI again.
/// </summary>
public bool ChangesHappeningInForeground { get; set; }
void CheckForModelChanges()
{
if (ChangesHappeningInForeground)
{
ChangesHappeningInForeground = false;
m_PreviousLabelsArraySize = m_SerializedLabelsArray.arraySize;
return;
}
if (m_SerializedLabelsArray.arraySize != m_PreviousLabelsArraySize)
{
RefreshListDataAndPresentation();
m_PreviousLabelsArraySize = m_SerializedLabelsArray.arraySize;
}
}
protected abstract void InitUiExtended();
public abstract void PostRemoveOperations();
public override VisualElement CreateInspectorGUI()
{
if (!m_UiInitialized)
{
InitUi();
m_UiInitialized = true;
}
serializedObject.Update();
RefreshListDataAndPresentation();
return m_Root;
}
void InitUi()
{
m_EditorHasUi = true;
m_UxmlPath = m_UxmlDir + "LabelConfig_Main.uxml";
m_Root = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree();
m_LabelListView = m_Root.Q<ListView>("labels-listview");
m_NonPresentLabelsListView = m_Root.Q<ListView>("labels-in-project-listview");
m_SaveButton = m_Root.Q<Button>("save-button");
m_AddNewLabelButton = m_Root.Q<Button>("add-label");
m_RemoveAllButton = m_Root.Q<Button>("remove-all-labels");
m_MoveUpButton = m_Root.Q<Button>("move-up-button");
m_MoveDownButton = m_Root.Q<Button>("move-down-button");
m_MoveButtons = m_Root.Q<VisualElement>("move-buttons");
m_ImportFromFileButton = m_Root.Q<Button>("import-file-button");
m_ExportToFileButton = m_Root.Q<Button>("export-file-button");
m_AddAllButton = m_Root.Q<Button>("add-all-labels-in-project");
m_StartingIdEnumField = m_Root.Q<EnumField>("starting-id-dropdown");
m_AutoIdToggle = m_Root.Q<Toggle>("auto-id-toggle");
m_IdSpecificUi = m_Root.Q<VisualElement>("id-specific-ui");
m_SaveButton.SetEnabled(false);
SetupPresentLabelsListView();
RefreshLabelsMasterList();
RefreshNonPresentLabels();
SetupNonPresentLabelsListView();
InitUiExtended();
UpdateMoveButtonState(null);
m_AddNewLabelButton.clicked += () => { AddNewLabel(m_AddedLabels); };
#if UNITY_2020_1_OR_NEWER
m_LabelListView.onSelectionChange += UpdateMoveButtonState;
#else
m_LabelListView.onSelectionChanged += UpdateMoveButtonState;
#endif
m_RemoveAllButton.clicked += () =>
{
m_SerializedLabelsArray.ClearArray();
serializedObject.ApplyModifiedProperties();
ChangesHappeningInForeground = true;
RefreshListDataAndPresentation();
};
m_AddAllButton.clicked += () =>
{
foreach (var label in m_LabelsNotPresentInConfig)
{
AppendLabelEntryToSerializedArray(m_SerializedLabelsArray, CreateLabelEntryFromLabelString(m_SerializedLabelsArray, label));
}
serializedObject.ApplyModifiedProperties();
ChangesHappeningInForeground = true;
RefreshListDataAndPresentation();
};
m_ImportFromFileButton.clicked += () =>
{
var path = EditorUtility.OpenFilePanel("Import label configuration from file", "", "json");
if (path.Length != 0)
{
var fileContent = File.ReadAllText(path);
var jsonObj = JObject.Parse(fileContent);
ImportFromJson(jsonObj);
}
};
m_ExportToFileButton.clicked += () =>
{
var path = EditorUtility.SaveFilePanel("Export label configuration to file", "", this.name, "json");
if (path.Length != 0)
{
string fileContents = ExportToJson();
var writer = File.CreateText(path);
writer.Write(fileContents);
writer.Flush();
writer.Close();
}
};
m_Root.schedule.Execute(CheckForModelChanges).Every(30);
}
void RefreshLabelsMasterList()
{
m_AllLabelsInProject.Clear();
var allPrefabPaths = GetAllPrefabsInProject();
foreach (var path in allPrefabPaths)
{
var asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
var labeling = asset.GetComponent<Labeling>();
if (labeling)
{
m_AllLabelsInProject.UnionWith(labeling.labels);
}
}
}
void RefreshNonPresentLabels()
{
m_LabelsNotPresentInConfig.Clear();
m_LabelsNotPresentInConfig.AddRange(m_AllLabelsInProject);
m_LabelsNotPresentInConfig.RemoveAll(label => m_AddedLabels.Contains(label));
}
static IEnumerable<string> GetAllPrefabsInProject()
{
var allPaths = AssetDatabase.GetAllAssetPaths();
return allPaths.Where(path => path.EndsWith(".prefab")).ToList();
}
void UpdateMoveButtonState(IEnumerable<object> objectList)
{
var selectedIndex = m_LabelListView.selectedIndex;
m_MoveDownButton.SetEnabled(selectedIndex < m_LabelListView.itemsSource.Count - 1);
m_MoveUpButton.SetEnabled(selectedIndex > 0);
}
public void RefreshListDataAndPresentation()
{
serializedObject.Update();
RefreshAddedLabels();
if (m_EditorHasUi && m_UiInitialized)
{
RefreshNonPresentLabels();
m_NonPresentLabelsListView.Refresh();
RefreshListViewHeight();
m_LabelListView.Refresh();
}
}
void ScrollToBottomAndSelectLastItem()
{
m_LabelListView.selectedIndex = m_LabelListView.itemsSource.Count - 1;
UpdateMoveButtonState(null);
m_Root.schedule.Execute(() => { m_LabelListView.ScrollToItem(-1); })
.StartingIn(
10); //to circumvent the delay in listview's internal scrollview updating its geometry (when new items are added).
}
protected void RefreshAddedLabels()
{
m_AddedLabels.Clear();
m_SerializedLabelsArray = serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName);
for (int i = 0; i < m_SerializedLabelsArray.arraySize; i++)
{
m_AddedLabels.Add(m_SerializedLabelsArray.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(ILabelEntry.label)).stringValue);
}
}
protected virtual void SetupPresentLabelsListView()
{
m_LabelListView.itemsSource = m_AddedLabels;
m_LabelListView.itemHeight = m_AddedLabelsItemHeight;
m_LabelListView.selectionType = SelectionType.Single;
m_LabelListView.RegisterCallback<AttachToPanelEvent>(evt => { RefreshListViewHeight(); });
}
void SetupNonPresentLabelsListView()
{
m_NonPresentLabelsListView.itemsSource = m_LabelsNotPresentInConfig;
VisualElement MakeItem()
{
var element = new NonPresentLabelElement<T>(this);
return element;
}
void BindItem(VisualElement e, int i)
{
if (e is NonPresentLabelElement<T> nonPresentLabel)
{
nonPresentLabel.label.text = m_LabelsNotPresentInConfig[i];
}
}
m_NonPresentLabelsListView.bindItem = BindItem;
m_NonPresentLabelsListView.makeItem = MakeItem;
m_NonPresentLabelsListView.itemHeight = m_OtherLabelsItemHeight;
m_NonPresentLabelsListView.selectionType = SelectionType.None;
}
protected void RefreshListViewHeight()
{
m_LabelListView.style.minHeight =
Mathf.Clamp(m_LabelListView.itemsSource.Count * m_LabelListView.itemHeight, 300, 600);
}
string FindNewLabelString(List<string> labels)
{
string baseLabel = "New Label";
string label = baseLabel;
int count = 1;
while (labels.Contains(label))
{
label = baseLabel + "_" + count++;
}
return label;
}
void AddNewLabel(List<string> presentLabels)
{
AddLabel(FindNewLabelString(presentLabels));
}
public void AddLabel(string labelToAdd)
{
if (m_AddedLabels.Contains(labelToAdd)) //label has already been added, cannot add again
return;
AppendLabelEntryToSerializedArray(m_SerializedLabelsArray, CreateLabelEntryFromLabelString(m_SerializedLabelsArray, labelToAdd));
serializedObject.ApplyModifiedProperties();
RefreshListDataAndPresentation();
if (m_EditorHasUi)
ScrollToBottomAndSelectLastItem();
}
public void RemoveLabel(string labelToRemove)
{
var index = IndexOfStringLabelInSerializedLabelsArray(labelToRemove);
if (index >= 0)
{
m_SerializedLabelsArray.DeleteArrayElementAtIndex(index);
}
serializedObject.ApplyModifiedProperties();
RefreshListDataAndPresentation();
if (m_EditorHasUi)
ScrollToBottomAndSelectLastItem();
}
protected abstract T CreateLabelEntryFromLabelString(SerializedProperty serializedArray, string labelToAdd);
protected abstract void AppendLabelEntryToSerializedArray(SerializedProperty serializedArray, T labelEntry);
void ImportFromJson(JObject jsonObj)
{
Undo.RegisterCompleteObjectUndo(serializedObject.targetObject, "Import new label config");
JsonUtility.FromJsonOverwrite(jsonObj.ToString(), serializedObject.targetObject);
ChangesHappeningInForeground = true;
RefreshListDataAndPresentation();
}
string ExportToJson()
{
return JsonUtility.ToJson(serializedObject.targetObject);
}
public int IndexOfStringLabelInSerializedLabelsArray(string label)
{
for (int i = 0; i < m_SerializedLabelsArray.arraySize; i++)
{
var element = m_SerializedLabelsArray.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(ILabelEntry.label));
if (element.stringValue == label)
{
return i;
}
}
return -1;
}
}
abstract class LabelElementInLabelConfig<T> : VisualElement where T : ILabelEntry
{
protected const string k_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
protected abstract string UxmlPath { get; }
Button m_RemoveButton;
public TextField labelTextField;
public int indexInList;
protected SerializedProperty m_LabelsArray;
protected LabelConfigEditor<T> m_LabelConfigEditor;
protected LabelElementInLabelConfig(LabelConfigEditor<T> editor, SerializedProperty labelsArray)
{
m_LabelConfigEditor = editor;
m_LabelsArray = labelsArray;
Init();
}
void Init()
{
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UxmlPath).CloneTree(this);
labelTextField = this.Q<TextField>("label-value");
m_RemoveButton = this.Q<Button>("remove-button");
labelTextField.isDelayed = true;
InitExtended();
labelTextField.RegisterValueChangedCallback((cEvent) =>
{
int index = m_LabelConfigEditor.IndexOfStringLabelInSerializedLabelsArray(cEvent.newValue);
if (index != -1 && index != indexInList)
{
//The listview recycles child visual elements and that causes the RegisterValueChangedCallback event to be called when scrolling.
//Therefore, we need to make sure we are not in this code block just because of scrolling, but because the user is actively changing one of the labels.
//The index check is for this purpose.
Debug.LogError("A label with the string " + cEvent.newValue + " has already been added to this label configuration.");
m_LabelsArray.GetArrayElementAtIndex(indexInList).FindPropertyRelative(nameof(ILabelEntry.label))
.stringValue = cEvent.previousValue; //since the textfield is bound to this property, it has already changed the property, so we need to revert the property.
m_LabelsArray.serializedObject.ApplyModifiedProperties();
m_LabelConfigEditor.ChangesHappeningInForeground = true;
m_LabelConfigEditor.RefreshListDataAndPresentation();
return;
}
//even though the textfield is already bound to the relevant property, we need to explicitly set the
//property here too in order to make "hasModifiedProperties" return the right value in the next line. Otherwise it will always be false.
m_LabelsArray.GetArrayElementAtIndex(indexInList).FindPropertyRelative(nameof(ILabelEntry.label))
.stringValue = cEvent.newValue;
if (m_LabelsArray.serializedObject.hasModifiedProperties)
{
//the value change event is called even when the listview recycles its child elements for re-use during scrolling, therefore, we should check to make sure there are modified properties, otherwise we would be doing the refresh for no reason (reduces scrolling performance)
m_LabelsArray.serializedObject.ApplyModifiedProperties();
m_LabelConfigEditor.ChangesHappeningInForeground = true;
m_LabelConfigEditor.RefreshListDataAndPresentation();
}
});
m_RemoveButton.clicked += () =>
{
m_LabelsArray.DeleteArrayElementAtIndex(indexInList);
m_LabelConfigEditor.PostRemoveOperations();
m_LabelConfigEditor.serializedObject.ApplyModifiedProperties();
m_LabelConfigEditor.ChangesHappeningInForeground = true;
m_LabelConfigEditor.RefreshListDataAndPresentation();
};
}
protected abstract void InitExtended();
}
class NonPresentLabelElement<T> : VisualElement where T : ILabelEntry
{
string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
public Label label;
public NonPresentLabelElement(LabelConfigEditor<T> editor)
{
var uxmlPath = m_UxmlDir + "SuggestedLabelElement.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath).CloneTree(this);
label = this.Q<Label>("label-value");
var addButton = this.Q<Button>("add-button");
addButton.clicked += () => { editor.AddLabel(label.text); };
}
}
}

3
com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs.meta


fileFormatVersion: 2
guid: ffb8bb7ed73a343f3ae88c68aaf8a84f
timeCreated: 1585940009

8
com.unity.perception/Editor/GroundTruth/Uss.meta


fileFormatVersion: 2
guid: c9dfe1921f7e84112aa13f8a71d7a2f1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
com.unity.perception/Editor/GroundTruth/Uxml.meta


fileFormatVersion: 2
guid: d98210a441f014a5cb3b1f68cfa69326
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

11
com.unity.perception/Editor/AssetPreparation/AssetPreparationTools.cs.meta


fileFormatVersion: 2
guid: c9787ce9a433e4916b180bf69288f689
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

24
com.unity.perception/Editor/AssetPreparation/AssetPreparationTools.cs


using System.IO;
using UnityEngine;
namespace UnityEditor.Perception.AssetPreparation
{
static class AssetPreparationMenuItems
{
/// <summary>
/// Function for creating prefabs from multiple models with one click. Created prefabs will be placed in the same folder as their corresponding model.
/// </summary>
[MenuItem("Assets/Perception/Create Prefabs from Selected Models")]
static void CreatePrefabsFromSelectedModels()
{
foreach (var selection in Selection.gameObjects)
{
var path = AssetDatabase.GetAssetPath(selection);
var tmpGameObject = Object.Instantiate(selection);
var destinationPath = Path.GetDirectoryName(path) + "/" + selection.name + ".prefab";
PrefabUtility.SaveAsPrefabAsset(tmpGameObject, destinationPath);
Object.DestroyImmediate(tmpGameObject);
}
}
}
}

3
com.unity.perception/Editor/GroundTruth/Uss/Styles.uss.meta


fileFormatVersion: 2
guid: dbe9e8ec4b8ef4ff384a22001ff72674
timeCreated: 1601665266

228
com.unity.perception/Editor/GroundTruth/Uss/Styles.uss


.labeling__remove-item-button {
width: 16px;
height: 16px;
background-image: resource('Packages/com.unity.perception/Editor/Icons/X.png');
background-color: #505050;
align-self: center;
}
.labeling__remove-item-button:hover {
background-color: #808080;
}
.added-label {
margin: 3px 4px 3px 4px;
flex-direction: row;
align-content: center;
}
.labeling__added-label-value {
width: 40%;
flex-grow: 1;
color: black;
}
.labeling__added-label-value > .unity-text-field__input {
color: rgb(195, 195, 195);
background-color: #313131;
border-width: 0;
}
.suggested-label {
flex-direction: row;
align-content: center;
margin: 4px 6px 0 6px;
padding: 2px;
padding-left: 5px;
border-radius: 4px;
background-color: #313131;
}
.labeling__suggested-label-value {
width: auto;
flex-grow: 1;
align-self: center;
color: rgb(180, 180, 180);
}
.labeling__label-listview {
flex-grow: 1;
min-height: 120px;
border-radius: 4px;
margin-right: 2px;
margin-top: 2px;
padding: 6px;
padding-top: 6px;
border-bottom-right-radius: 0;
background-color: #272727;
}
.labeling__configs-listview {
flex-grow: 1;
min-height: 150px;
background-color: rgb(32, 32, 32);
border-radius: 4px;
margin: 6px;
padding: 6px;
border-bottom-right-radius: 0;
}
.labeling__add-label-button {
align-self: flex-end;
border-top-right-radius: 0;
border-top-left-radius: 0;
background-color: #222222;
border-color: #707070;
border-width: 0;
margin-right: 2px;
margin-top: 2px;
border-radius: 2px;
color: #CACACA;
}
.labeling__add-label-button:hover{
background-color: #606060;
}
.labeling__add-label-button:active{
background-color: #303030;
}
.outer-container {
padding-top: 5px;
flex-grow: 1;
}
.outer-container-in-window{
padding: 7px;
}
.inner-container {
margin-top: 5px;
border-radius: 4px;
border-width: 1px;
padding: 5px;
background-color: #454545;
align-content: flex-start;
}
.inner-container .depth2{
background-color: rgb(60, 60, 60);
}
.labeling__add-to-list-button {
align-self: flex-end;
background-color: #505050;
border-width: 0;
color: #EEEEEE;
}
.labeling__add-to-config-button {
padding: 6px;
flex-shrink: 1;
-unity-text-align: middle-left;
background-color: #505050;
border-width: 0;
color: #EEEEEE;
}
.title-label {
-unity-font-style: bold;
margin-bottom: 5px;
color: #CACACA;
}
.subtitle-label {
color: #CACACA;
}
.collapse-toggle {
flex-shrink: 0;
margin-right: 3px;
margin-left: 3px;
width: 10px;
height: 10px;
align-self: center;
background-image: resource("Packages/com.unity.perception/Editor/Icons/FoldoutOpen.png");
}
.collapsed-toggle-state{
background-image: resource("Packages/com.unity.perception/Editor/Icons/FoldoutClosed.png");
}
.collapse-toggle:hover {
-unity-background-image-tint-color: cornflowerblue;
}
.label-config-header{
background-color: #272727;
flex-direction: row;
}
.suggested-label.label_add_from_config{
margin: 3px 3px 3px 3px;
}
.label-config-add-from{
border-radius: 4px;
margin: 2px 0 2px 0;
background-color: #272727;
padding: 3px 1px 3px 1px;
}
.labeling__config-name-label {
color: rgb(220, 220, 220);
width: auto;
flex-grow: 1;
align-self: center;
padding: 4px;
}
.collapsed {
display: none;
}
.config-label-present{
flex-direction: row;
align-content: center;
border-radius: 4px;
margin: 3px;
background-color: #272727;
}
.move-label-in-config-button{
background-color: #505050;
width: 30px;
height: 16px;
align-self: center;
-unity-background-scale-mode: scale-to-fit;
}
.move-up{
background-image: resource('Packages/com.unity.perception/Editor/Icons/ChevronUpPadded.png');
}
.move-down{
background-image: resource('Packages/com.unity.perception/Editor/Icons/ChevronDownPadded.png');
}
.move-label-in-config-button:hover{
background-color: #808080;
}
.move-label-in-config-button:active{
background-color: #303030;
}
.generic-hover:hover{
background-color: #555555;
}
.helpbox{
border-width: 1px;
border-color: #808080;
border-radius: 4px;
padding: 7px;
white-space: normal;
margin-top: 4px;
margin-bottom: 4px;
}

13
com.unity.perception/Editor/GroundTruth/Uxml/AddToConfigWindow.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="outer-container-in-window" name="outer-container">
<Style src="../Uss/Styles.uss"/>
<Label name="title" class="title-label"/>
<ListView name="selected-labels-list" class="labeling__label-listview" style="height:800px; margin: 0;"/>
<Label name="currently-present-label" text="Currently Present In:" style="margin: 10px 0 4px 0"/>
<ListView name="current-configs-listview" class="labeling__configs-listview" style="height:150px; margin: 0; max-height:150px"/>
<Label name="other-configs-label" style="margin: 10px 0 4px 0"/>
<ListView name="other-configs-listview" class="labeling__configs-listview" style="height:150px; margin: 0; max-height:150px"/>
<Label name="status" style="display:none; margin-top: 7px; -unity-font-style: bold; color:yellow"/>
</VisualElement>
</UXML>

10
com.unity.perception/Editor/GroundTruth/Uxml/AddToConfigWindow.uxml.meta


fileFormatVersion: 2
guid: 8b8acfa72087c43698d342a06e2b30e6
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

7
com.unity.perception/Editor/GroundTruth/Uxml/AddedLabelElement.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="added-label">
<Button name="remove-button" class="labeling__remove-item-button" />
<TextField name="label-value" class="labeling__added-label-value" />
<Button name="add-to-config-button" text="Add to Label Config..." class="labeling__add-to-config-button" />
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/AddedLabelElement.uxml.meta


fileFormatVersion: 2
guid: ab7eae2a6fad4a95ac73aa5d46704ef6
timeCreated: 1603311500

7
com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelNotPresent.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement>
<Style src="../Uss/Styles.uss"/>
<Label name="config-name" class="labeling__config-name-label"/>
<Button name="add-to-config-button" class="labeling__addremove-config-button"/>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelNotPresent.uxml.meta


fileFormatVersion: 2
guid: 5335f0007841a4aae96d6ec9e95d1b39
timeCreated: 1603311500

8
com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelPresent.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement class="config-label-present">
<Style src="../Uss/Styles.uss"/>
<Label name="config-name" class="labeling__config-name-label"/>
<Button name="remove-from-config-button" class="labeling__addremove-config-button"/>
<Button name="open-config-button" class="labeling__addremove-config-button" text="Open"/>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementLabelPresent.uxml.meta


fileFormatVersion: 2
guid: f65364e03181f4f24acd20305f6c34bb
timeCreated: 1603311500

38
com.unity.perception/Editor/GroundTruth/Uxml/Labeling_Main.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement class="outer-container" name="outer-container">
<Style src="../Uss/Styles.uss"/>
<Toggle name = "auto-or-manual-toggle" text="Use Automatic Labeling" style="margin-top: 5px"/>
<VisualElement name="automatic-labeling" class="inner-container">
<VisualElement name = "drop-down-parent"/>
<VisualElement style="flex-direction:row; flex-shrink:1; width:auto">
<Label name ="current-auto-label-title" text="Automatic Label:"/>
<Label name ="current-auto-label" style="-unity-font-style: bold; margin-left: 54px;"/>
</VisualElement>
<Button name ="add-auto-label-to-config" text="Add to Label Config..." style="margin-top: 5px; align-self: flex-end;"/>
</VisualElement>
<VisualElement name="manual-labeling" class="inner-container" style="margin-top:5px">
<Label text="Added Labels" name="added-labels-title" class="title-label"/>
<ListView name="current-labels-listview" class="labeling__label-listview" style="margin-top: 2px"/>
<Button name="add-label" text="Add New Label" class="labeling__add-label-button"/>
<Label name = "add-manual-labels-title" text="Add Labels" class="title-label" style = "margin-top:0px"/>
<VisualElement name="from-label-configs" class="inner-container depth2" style="margin-top:0">
<Label text="From Existing Label Configs" class="title-label"/>
<VisualElement name="add-from-label-configs" style="padding-top: 0px;">
<ScrollView name="label-configs-scrollview" class="labeling__label-configs-scrollview" style="min-height: 100px;"/>
</VisualElement>
</VisualElement>
<VisualElement name="suggested-labels" class="inner-container depth2">
<Label text="Other Suggested Labels" class="title-label"/>
<VisualElement name="suggested-labels-from-name" style="padding-top: 5px;">
<Label text="Based on asset name" class="subtitle-label"/>
<ListView name="suggested-labels-name-listview" class="labeling__label-listview" style="min-height: 100px;"/>
</VisualElement>
<VisualElement name="suggested-labels-from-path" style="padding-top: 5px;">
<Label text="Based on asset path" class="subtitle-label"/>
<ListView name="suggested-labels-path-listview" class="labeling__label-listview" style="min-height: 100px;"/>
</VisualElement>
</VisualElement>
</VisualElement>
</VisualElement>
</UXML>

10
com.unity.perception/Editor/GroundTruth/Uxml/Labeling_Main.uxml.meta


fileFormatVersion: 2
guid: 3cef46fa678f14155a2dfb5393f41af1
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

7
com.unity.perception/Editor/GroundTruth/Uxml/SuggestedLabelElement.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="suggested-label">
<Style src="../Uss/Styles.uss"/>
<Label name="label-value" class="labeling__suggested-label-value"/>
<Button name="add-button" class="labeling__add-to-list-button" text="Add to Labels"/>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/SuggestedLabelElement.uxml.meta


fileFormatVersion: 2
guid: 194f2c0998a924e0eb89baba7fa4141a
timeCreated: 1603311500

13
com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementForAddingLabelsFrom.uxml


<UXML xmlns="UnityEngine.UIElements">
<Toggle name="collapsed-state" style="display:none"/>
<VisualElement class="label-config-add-from">
<Style src="../Uss/Styles.uss"/>
<VisualElement class="label-config-header">
<VisualElement name="collapse-toggle" class="collapse-toggle"/>
<Label name="config-name" class="labeling__config-name-label"/>
<VisualElement style="flex-grow:1"/>
<Button name="open-config-button" text="Open"/>
</VisualElement>
<ListView name="label-config-contents-listview" class="labeling__label-listview" style="height: 300px"/>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/ConfigElementForAddingLabelsFrom.uxml.meta


fileFormatVersion: 2
guid: 0b6d5a00362234d9488a43570dfd15ae
timeCreated: 1603311500

10
com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml.meta


fileFormatVersion: 2
guid: 5a298ce54f2b64db49ada97cd180b99d
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

14
com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement class="added-label" style="padding-top: 3px;">
<Button name="remove-button" class="labeling__remove-item-button"/>
<VisualElement class="generic-hover"
style="flex-direction: row; padding: 3px 0 3px 6px; margin-left: 3px; margin-right: 3px; border-width: 1px; border-color: #555555; border-radius: 4px;">
<editor:ColorField name="label-color-value" style="min-width : 60px; max-width: 60px; align-self:center;"/>
</VisualElement>
<TextField name="label-value" class="labeling__added-label-value"/>
<VisualElement style="min-width:20px; flex-direction: row; display:none">
<Button name="move-up-button" class="move-label-in-config-button move-up" style="margin-right:-2px"/>
<Button name="move-down-button" class="move-label-in-config-button move-down"/>
</VisualElement>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml.meta


fileFormatVersion: 2
guid: 092a4ceabfda6410dbc745780dd88616
timeCreated: 1603311500

14
com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="added-label" style="padding-top: 3px;">
<Button name="remove-button" class="labeling__remove-item-button" />
<VisualElement class="generic-hover" style = "flex-direction: row; padding: 3px; margin-left: 3px; margin-right: 3px; border-width: 1px; border-color: #555555; border-radius: 4px;">
<Label name="label-id-title" text = "ID = " style="align-self:center; color: #888888;"/>
<TextField name="label-id-value" style="-unity-font-style: bold; color: #888888; min-width : 30px; align-self:center;"/>
</VisualElement>
<TextField name="label-value" class="labeling__added-label-value"/>
<VisualElement style="min-width:20px; flex-direction: row; display:none">
<Button name="move-up-button" class="move-label-in-config-button move-up" style="margin-right:-2px"/>
<Button name="move-down-button" class="move-label-in-config-button move-down"/>
</VisualElement>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml.meta


fileFormatVersion: 2
guid: 2961168094add41168cf3b38ba7009dd
timeCreated: 1603311500

53
com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement class="outer-container" name="outer-container">
<Style src="../Uss/Styles.uss"/>
<VisualElement class="inner-container" name="id-specific-ui">
<Toggle name="auto-id-toggle" text="Auto Assign IDs" style="margin:0" binding-path="autoAssignIds"/>
<VisualElement style="flex-direction: row; flex-grow: 1;">
<editor:EnumField label="Starting ID" name="starting-id-dropdown" binding-path="startingLabelId"
style="flex-grow:1; margin:0"/>
</VisualElement>
</VisualElement>
<VisualElement name="added-labels" class="inner-container" style="margin-top:5px">
<Label text="Added Labels" name="added-labels-title" class="title-label"/>
<ListView name="labels-listview" class="labeling__label-listview" style="margin-top: 5px;"/>
<Button name="save-button" text="Save" style="display:none"/>
<VisualElement name="button-bar" style="min-width:20px; flex-direction: row; margin-top: 5px">
<VisualElement name="move-buttons" style="flex-direction: row; min-width:100px">
<Label text="Move Selected Label:" style="align-self:center; margin-left: 2px; flex-shrink:1"/>
<Button name="move-up-button" class="move-label-in-config-button move-up"
style="margin-right:-2px"/>
<Button name="move-down-button" class="move-label-in-config-button move-down"/>
</VisualElement>
<VisualElement style="flex-grow:1"/>
<Button name="remove-all-labels" text="Remove All" class="labeling__add-label-button"
style="color:red"/>
<Button name="add-label" text="Add New Label" class="labeling__add-label-button"/>
</VisualElement>
</VisualElement>
<VisualElement name="other-labels" class="inner-container depth2">
<Label text="Other Labels in Project" class="title-label"/>
<ListView name="labels-in-project-listview" class="labeling__label-listview"
style="margin-top: 5px; min-height: 200px"/>
<Button name="add-all-labels-in-project" text="Add All Labels to Config" class="labeling__add-label-button"
style="margin-top: 5px"/>
</VisualElement>
<VisualElement name="other-labels" class="inner-container depth2">
<Label text="Import or Export Label Config" name="added-labels-title" class="title-label"/>
<VisualElement style="flex-direction: row; flex-grow: 1; padding: 0; margin: 8 0 3 0">
<Button name="import-file-button" text="Import from File" class="title-label"
style="align-self: center; padding: 10px; flex-grow:1; height: 40px; margin: 0 3 0 0"/>
<Button name="export-file-button" text="Export to File" class="title-label"
style="align-self: center; padding: 10px; flex-grow:1; height: 40px; margin: 0 0 0 3"/>
</VisualElement>
<VisualElement class="helpbox" style="margin: 7px 0 7px 0 ">
<TextElement
text="Importing a label config file will overwrite all existing contents of this label config."/>
</VisualElement>
</VisualElement>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/Icons/ChevronDown.png

之前 之后
宽度: 32  |  高度: 32  |  大小: 2.1 KiB

142
com.unity.perception/Editor/Icons/ChevronDown.png.meta


fileFormatVersion: 2
guid: fa47b84af1c764637bf825673d76949a
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 2
mipBias: -100
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

6
com.unity.perception/Editor/Icons/ChevronDownPadded.png

之前 之后
宽度: 32  |  高度: 32  |  大小: 2.1 KiB

142
com.unity.perception/Editor/Icons/ChevronDownPadded.png.meta


fileFormatVersion: 2
guid: 7d8b0ddb7696e48a6a9e037ba7d08e73
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 2
mipBias: -100
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

4
com.unity.perception/Editor/Icons/ChevronUp.png

之前 之后
宽度: 32  |  高度: 32  |  大小: 2.1 KiB

142
com.unity.perception/Editor/Icons/ChevronUp.png.meta


fileFormatVersion: 2
guid: 7c5db57ceb3164158b79c2eceb75cdbc
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 2
mipBias: -100
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

4
com.unity.perception/Editor/Icons/ChevronUpPadded.png

之前 之后
宽度: 32  |  高度: 32  |  大小: 2.1 KiB

142
com.unity.perception/Editor/Icons/ChevronUpPadded.png.meta


fileFormatVersion: 2
guid: 110dd95b59b974fd28227e7a929eec75
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 2
mipBias: -100
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

/com.unity.perception/Editor/Randomization/Icons.meta → /com.unity.perception/Editor/Icons.meta

/com.unity.perception/Editor/Randomization/Icons → /com.unity.perception/Editor/Icons

正在加载...
取消
保存