浏览代码

Label config editor almost done

/main
Mohsen Kamalzadeh 4 年前
当前提交
e5685e3b
共有 14 个文件被更改,包括 881 次插入540 次删除
  1. 608
      com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs
  2. 61
      com.unity.perception/Editor/GroundTruth/LabelingEditor.cs
  3. 150
      com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs
  4. 4
      com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs.meta
  5. 2
      com.unity.perception/Editor/GroundTruth/Uss/Styles.uss
  6. 15
      com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml
  7. 474
      com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs
  8. 3
      com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs.meta
  9. 84
      com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor_OLD.cs
  10. 3
      com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor_OLD.cs.meta
  11. 14
      com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml
  12. 3
      com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml.meta
  13. 0
      /com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml
  14. 0
      /com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml.meta

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


namespace UnityEditor.Perception.GroundTruth
{
[CustomEditor(typeof(IdLabelConfig))]
class IdLabelConfigEditor : Editor
class IdLabelConfigEditor : LabelConfigEditor<IdLabelEntry>
private ListView m_LabelListView;
private ListView m_NonPresentLabelsListView;
private string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
private string m_UxmlPath;
private VisualElement m_Root;
private IdLabelConfig m_IdLabelConig;
private Button m_SaveButton;
private Button m_AddNewLabelButton;
private Button m_MoveUpButton;
private Button m_MoveDownButton;
private Button m_ImportFromFileButton;
private List<string> m_AddedLabels = new List<string>();
private SerializedProperty m_SerializedLabelsArray;
private static HashSet<string> allLabelsInProject = new HashSet<string>();
private List<string> m_LabelsNotPresentInConfig = new List<string>();
protected override LabelConfig<IdLabelEntry> TargetLabelConfig => (IdLabelConfig) serializedObject.targetObject;
public void OnEnable()
protected override void OnEnableExtended()
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_MoveUpButton = m_Root.Q<Button>("move-up-button");
m_MoveDownButton = m_Root.Q<Button>("move-down-button");
m_ImportFromFileButton = m_Root.Q<Button>("import-file-button");
m_SaveButton.SetEnabled(false);
m_IdLabelConig = (IdLabelConfig) serializedObject.targetObject;
m_SerializedLabelsArray = serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName);
UpdateMoveButtonState();
if (m_AutoAssign)
{
AutoAssignIds();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
}
RefreshAddedLabels();
SetupPresentLabelsListView();
RefreshLabelsMasterList();
RefreshNonPresentLabels();
SetupNonPresentLabelsListView();
m_AddNewLabelButton.clicked += () => { AddNewLabel(m_SerializedLabelsArray, m_AddedLabels); };
m_MoveDownButton.clicked += () =>
{
var selectedIndex = m_LabelListView.selectedIndex;
if (selectedIndex > -1 && selectedIndex < m_SerializedLabelsArray.arraySize - 1)
{
var currentProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var bottomProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex + 1)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var tmpString = bottomProperty.stringValue;
bottomProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
m_LabelListView.SetSelection(selectedIndex + 1);
serializedObject.ApplyModifiedProperties();
RefreshAddedLabels();
m_LabelListView.Refresh();
RefreshListViewHeight();
//AssetDatabase.SaveAssets();
}
};
m_MoveUpButton.clicked += () =>
{
var selectedIndex = m_LabelListView.selectedIndex;
if (selectedIndex > 0)
{
var currentProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var topProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex - 1)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var tmpString = topProperty.stringValue;
topProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
m_LabelListView.SetSelection(selectedIndex - 1);
serializedObject.ApplyModifiedProperties();
RefreshAddedLabels();
m_LabelListView.Refresh();
RefreshListViewHeight();
//AssetDatabase.SaveAssets();
}
};
m_LabelListView.RegisterCallback<ClickEvent>(evt => { UpdateMoveButtonState(); });
m_ImportFromFileButton.clicked += () =>
{
var path = EditorUtility.OpenFilePanel("Import label configuration", "", "");
if (path.Length != 0)
{
var fileContent = File.ReadAllText(path);
var jsonObj = JObject.Parse(fileContent);
ImportFromJson(jsonObj);
}
};
AutoAssignIdsIfNeeded();
m_MoveDownButton.clicked += MoveSelectedItemDown;
m_MoveUpButton.clicked += MoveSelectedItemUp;
public override VisualElement CreateInspectorGUI()
public override void PostRemoveOperations()
serializedObject.Update();
m_IdLabelConig = (IdLabelConfig) serializedObject.targetObject;
RefreshListDataAndPresenation();
return m_Root;
AutoAssignIdsIfNeeded();
static void RefreshLabelsMasterList()
private void MoveSelectedItemUp()
allLabelsInProject.Clear();
var allPrefabPaths = GetAllPrefabsInProject();
foreach (var path in allPrefabPaths)
var selectedIndex = m_LabelListView.selectedIndex;
if (selectedIndex > 0)
var asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
var labeling = asset.GetComponent<Labeling>();
if (labeling)
{
allLabelsInProject.UnionWith(labeling.labels);
}
}
}
var currentProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(ILabelEntry.label));
var topProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex - 1)
.FindPropertyRelative(nameof(ILabelEntry.label));
void RefreshNonPresentLabels()
{
m_LabelsNotPresentInConfig.Clear();
m_LabelsNotPresentInConfig.AddRange(allLabelsInProject);
m_LabelsNotPresentInConfig.RemoveAll(label => m_AddedLabels.Contains(label));
}
var tmpString = topProperty.stringValue;
topProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
static List<string> GetAllPrefabsInProject()
{
var allPaths = AssetDatabase.GetAllAssetPaths();
var prefabPaths = new List<string>();
foreach (var path in allPaths)
{
if (path.EndsWith(".prefab"))
prefabPaths.Add(path);
}
m_LabelListView.SetSelection(selectedIndex - 1);
return prefabPaths;
serializedObject.ApplyModifiedProperties();
RefreshAddedLabels();
m_LabelListView.Refresh();
RefreshListViewHeight();
//AssetDatabase.SaveAssets();
}
void UpdateMoveButtonState()
private void MoveSelectedItemDown()
m_MoveDownButton.SetEnabled(selectedIndex > -1);
m_MoveUpButton.SetEnabled(selectedIndex > -1);
}
public void RefreshListDataAndPresenation()
{
serializedObject.Update();
RefreshAddedLabels();
RefreshNonPresentLabels();
m_NonPresentLabelsListView.Refresh();
RefreshListViewHeight();
m_LabelListView.Refresh();
}
if (selectedIndex > -1 && selectedIndex < m_SerializedLabelsArray.arraySize - 1)
{
var currentProperty =
m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex)
.FindPropertyRelative(nameof(ILabelEntry.label));
var bottomProperty = m_SerializedLabelsArray.GetArrayElementAtIndex(selectedIndex + 1)
.FindPropertyRelative(nameof(ILabelEntry.label));
void ScrollToBottomAndSelectLastItem()
{
m_LabelListView.SetSelection(m_LabelListView.itemsSource.Count - 1);
UpdateMoveButtonState();
var tmpString = bottomProperty.stringValue;
bottomProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
m_Root.schedule.Execute(() => { m_LabelListView.ScrollToItem(-1); })
.StartingIn(
10); //to circumvent the delay in the listview's internal scrollview updating its geometry (when new items are added).
}
m_LabelListView.SetSelection(selectedIndex + 1);
void RefreshAddedLabels()
{
m_AddedLabels.Clear();
m_AddedLabels.AddRange(m_IdLabelConig.labelEntries.Select(entry => entry.label));
serializedObject.ApplyModifiedProperties();
RefreshAddedLabels();
m_LabelListView.Refresh();
RefreshListViewHeight();
//AssetDatabase.SaveAssets();
}
void SetupPresentLabelsListView()
protected override void SetupPresentLabelsListView()
m_LabelListView.itemsSource = m_AddedLabels;
base.SetupPresentLabelsListView();
new LabelElementInLabelConfig(this, m_SerializedLabelsArray, m_LabelListView);
new IdLabelElementInLabelConfig(this, m_SerializedLabelsArray);
if (e is LabelElementInLabelConfig addedLabel)
if (e is IdLabelElementInLabelConfig addedLabel)
addedLabel.m_LabelTextField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(IdLabelEntry.label)));
addedLabel.m_LabelId.text = m_IdLabelConig.labelEntries[i].id.ToString();
addedLabel.m_LabelTextField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(IdLabelEntry.label)));
addedLabel.m_LabelId.text = TargetLabelConfig.labelEntries[i].id.ToString();
const int itemHeight = 35;
m_LabelListView.itemHeight = itemHeight;
m_LabelListView.selectionType = SelectionType.Single;
m_LabelListView.RegisterCallback<AttachToPanelEvent>(evt => { RefreshListViewHeight(); });
void SetupNonPresentLabelsListView()
protected override IdLabelEntry CreateLabelEntryFromLabelString(SerializedProperty serializedArray,
string labelToAdd)
m_NonPresentLabelsListView.itemsSource = m_LabelsNotPresentInConfig;
int maxLabel = Int32.MinValue;
if (serializedArray.arraySize == 0)
maxLabel = -1;
VisualElement MakeItem()
for (int i = 0; i < serializedArray.arraySize; i++)
var element = new NonPresentLabelElement(this, m_SerializedLabelsArray);
return element;
var item = serializedArray.GetArrayElementAtIndex(i);
maxLabel = math.max(maxLabel, item.FindPropertyRelative(nameof(IdLabelEntry.id)).intValue);
void BindItem(VisualElement e, int i)
return new IdLabelEntry
if (e is NonPresentLabelElement nonPresentLabel)
id = maxLabel + 1,
label = labelToAdd
};
}
protected override IdLabelEntry ImportFromJsonExtended(string labelString, JObject labelEntryJObject,
List<IdLabelEntry> previousEntries, bool preventDuplicateIdentifiers = true)
{
bool invalid = false;
int parsedId = -1;
if (labelEntryJObject.TryGetValue("Id", out var idToken))
{
var idString = idToken.Value<string>();
if (Int32.TryParse(idString, out parsedId))
nonPresentLabel.m_Label.text = m_LabelsNotPresentInConfig[i];
if (preventDuplicateIdentifiers && previousEntries.FindAll(entry => entry.id == parsedId).Count > 0)
{
Debug.LogError("File contains a duplicate Label Id: " + parsedId);
invalid = true;
}
}
else
{
Debug.LogError("Error parsing Id for Label Entry" + labelEntryJObject +
" from file. Please make sure a string value is provided in the file and that it is convertible to an integer.");
invalid = true;
else
{
Debug.LogError("Error reading the Id field for Label Entry" + labelEntryJObject +
" from file. Please check the formatting.");
invalid = true;
}
const int itemHeight = 27;
return new IdLabelEntry
{
label = invalid? InvalidLabel : labelString,
id = parsedId
};
}
m_NonPresentLabelsListView.bindItem = BindItem;
m_NonPresentLabelsListView.makeItem = MakeItem;
m_NonPresentLabelsListView.itemHeight = itemHeight;
m_NonPresentLabelsListView.selectionType = SelectionType.None;
protected override void AddLabelIdentifierToJson(SerializedProperty labelEntry, JObject jObj)
{
jObj.Add("Id", labelEntry.FindPropertyRelative(nameof(IdLabelEntry.id)).intValue.ToString());
public void SetSaveButtonEnabled(bool enabled)
protected override void AppendLabelEntryToSerializedArray(SerializedProperty serializedArray,
IdLabelEntry labelEntry)
m_SaveButton.SetEnabled(enabled);
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 void RefreshListViewHeight()
protected override void ImportLabelEntryListIntoSerializedArray(SerializedProperty serializedArray,
List<IdLabelEntry> labelEntriesToAdd)
m_LabelListView.style.minHeight =
Mathf.Clamp(m_LabelListView.itemsSource.Count * m_LabelListView.itemHeight, 300, 600);
labelEntriesToAdd = labelEntriesToAdd.OrderBy(entry => entry.id).ToList();
base.ImportLabelEntryListIntoSerializedArray(serializedArray, labelEntriesToAdd);
bool m_AutoAssign => serializedObject.FindProperty(nameof(IdLabelConfig.autoAssignIds)).boolValue;
private bool AutoAssign => serializedObject.FindProperty(nameof(IdLabelConfig.autoAssignIds)).boolValue;
void AutoAssignIds()
private void AutoAssignIds()
{
var serializedProperty = serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName);
var size = serializedProperty.arraySize;

}
}
void AddNewLabel(SerializedProperty serializedArray, List<string> presentLabels)
{
AddNewLabel(serializedArray, FindNewLabelValue(presentLabels));
}
public void AddNewLabel(SerializedProperty serializedArray, string labelToAdd)
{
if (m_AddedLabels.Contains(labelToAdd)) //label has already been added, cannot add again
return;
int maxLabel = Int32.MinValue;
if (serializedArray.arraySize == 0)
maxLabel = -1;
for (int i = 0; i < serializedArray.arraySize; i++)
{
var item = serializedArray.GetArrayElementAtIndex(i);
maxLabel = math.max(maxLabel, item.FindPropertyRelative(nameof(IdLabelEntry.id)).intValue);
}
AppendLabelEntryWithIdToSerializedArray(serializedArray, labelToAdd, maxLabel + 1);
serializedObject.ApplyModifiedProperties();
RefreshListDataAndPresenation();
ScrollToBottomAndSelectLastItem();
}
// Add a list of IdLabelEntry objects to the target object's serialized entries array. This function assumes the labelsToAdd list does not contain any duplicate labels.
void ImportLabelEntryListIntoSerializedArray(SerializedProperty serializedArray,
List<IdLabelEntry> labelEntriesToAdd)
{
serializedArray.ClearArray();
labelEntriesToAdd = labelEntriesToAdd.OrderBy(entry => entry.id).ToList();
foreach (var entry in labelEntriesToAdd)
{
AppendLabelEntryWithIdToSerializedArray(serializedArray, entry.label, entry.id);
}
serializedObject.ApplyModifiedProperties();
RefreshListDataAndPresenation();
}
void AppendLabelEntryWithIdToSerializedArray(SerializedProperty serializedArray, string label, int id)
{
var index = serializedArray.arraySize;
serializedArray.InsertArrayElementAtIndex(index);
var element = serializedArray.GetArrayElementAtIndex(index);
var idProperty = element.FindPropertyRelative(nameof(IdLabelEntry.id));
idProperty.intValue = id;
var labelProperty = element.FindPropertyRelative(nameof(IdLabelEntry.label));
labelProperty.stringValue = label;
}
string FindNewLabelValue(List<string> labels)
{
string baseLabel = "New Label";
string label = baseLabel;
int count = 1;
while (labels.Contains(label))
{
label = baseLabel + "_" + count++;
}
return label;
}
void ImportFromJson(JObject jsonObj)
private void AutoAssignIdsIfNeeded()
List<IdLabelEntry> importedLabelEntries = new List<IdLabelEntry>();
JToken type;
if (jsonObj.TryGetValue("LabelEntryType", out type))
{
if (type.Value<string>() == nameof(IdLabelEntry))
{
JToken labelsArrayToken;
if (jsonObj.TryGetValue("LabelEntries", out labelsArrayToken))
{
JArray labelsArray = JArray.Parse(labelsArrayToken.ToString());
if (labelsArray != null)
{
foreach (var labelEntryToken in labelsArray)
{
if (labelEntryToken is JObject entryObject)
{
JToken labelToken;
JToken idToken;
if (entryObject.TryGetValue("Label", out labelToken))
{
if (entryObject.TryGetValue("Id", out idToken))
{
int parsedId;
string idString = idToken.Value<string>();
if (Int32.TryParse(idString, out parsedId))
{
string labelString = labelToken.Value<string>();
if (importedLabelEntries.FindAll(entry => entry.label == labelString)
.Count > 0)
{
Debug.LogError("File contains a duplicate Label: " + labelString);
}
else if (importedLabelEntries.FindAll(entry => entry.id == parsedId)
.Count > 0)
{
Debug.LogError("File contains a duplicate Label Id: " + parsedId);
}
else
{
var labelEntry = new IdLabelEntry
{
label = labelString,
id = parsedId
};
importedLabelEntries.Add(labelEntry);
}
}
else
{
Debug.LogError("Error reading Id for Label Entry" + labelEntryToken +
" from file. Please make sure a string value is provided.");
return;
}
}
else
{
Debug.LogError("Error reading Id for Label Entry" + labelEntryToken +
" from file. Please check the formatting.");
return;
}
}
else
{
Debug.LogError("Error reading Label for Label Entry" + labelEntryToken +
" from file. Please check the formatting.");
return;
}
}
else
{
Debug.LogError("Error reading Label Entry " + labelEntryToken +
" from file. Please check the formatting.");
return;
}
}
ImportLabelEntryListIntoSerializedArray(m_SerializedLabelsArray, importedLabelEntries);
}
else
{
Debug.LogError(
"Could not read list of Label Entries from file. Please check the formatting.");
}
}
}
else
{
Debug.LogError("Specified LabelEntryType does not match " + nameof(IdLabelEntry));
}
}
else
if (AutoAssign)
Debug.LogError("LabelEntryType not found.");
AutoAssignIds();
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
class LabelElementInLabelConfig : VisualElement
internal class IdLabelElementInLabelConfig : LabelElementInLabelConfig<IdLabelEntry>
private string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
private string m_UxmlPath;
private Button m_RemoveButton;
private Button m_MoveUpButton;
private Button m_MoveDownButton;
protected override string UxmlPath => UxmlDir + "IdLabelElementInLabelConfig.uxml";
public TextField m_LabelTextField;
public int m_IndexInList;
public LabelElementInLabelConfig(IdLabelConfigEditor editor, SerializedProperty labelsArray,
ListView labelsListView)
{
m_UxmlPath = m_UxmlDir + "LabelElementInLabelConfig.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree(this);
m_LabelTextField = this.Q<TextField>("label-value");
m_RemoveButton = this.Q<Button>("remove-button");
m_MoveUpButton = this.Q<Button>("move-up-button");
m_MoveDownButton = this.Q<Button>("move-down-button");
m_LabelId = this.Q<Label>("label-id-value");
m_LabelTextField.isDelayed = true;
m_MoveDownButton.clicked += () =>
{
if (m_IndexInList < labelsArray.arraySize - 1)
{
var currentProperty =
labelsArray.GetArrayElementAtIndex(m_IndexInList)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var bottomProperty = labelsArray.GetArrayElementAtIndex(m_IndexInList + 1)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var tmpString = bottomProperty.stringValue;
bottomProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
m_IndexInList++;
labelsListView.SetSelection(m_IndexInList);
UpdateMoveButtonVisibility(labelsArray);
editor.serializedObject.ApplyModifiedProperties();
labelsListView.Refresh();
editor.RefreshListViewHeight();
//AssetDatabase.SaveAssets();
}
};
m_MoveUpButton.clicked += () =>
{
if (m_IndexInList > 0)
{
var currentProperty =
labelsArray.GetArrayElementAtIndex(m_IndexInList)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var topProperty = labelsArray.GetArrayElementAtIndex(m_IndexInList - 1)
.FindPropertyRelative(nameof(IdLabelEntry.label));
var tmpString = topProperty.stringValue;
topProperty.stringValue = currentProperty.stringValue;
currentProperty.stringValue = tmpString;
m_IndexInList--;
labelsListView.SetSelection(m_IndexInList);
UpdateMoveButtonVisibility(labelsArray);
editor.serializedObject.ApplyModifiedProperties();
editor.RefreshListDataAndPresenation();
//AssetDatabase.SaveAssets();
}
};
m_LabelTextField.RegisterValueChangedCallback<string>((cEvent) =>
{
labelsArray.GetArrayElementAtIndex(m_IndexInList).FindPropertyRelative(nameof(IdLabelEntry.label))
.stringValue = cEvent.newValue;
if (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)
labelsArray.serializedObject.ApplyModifiedProperties();
editor.RefreshListDataAndPresenation();
}
});
m_RemoveButton.clicked += () =>
{
labelsArray.DeleteArrayElementAtIndex(m_IndexInList);
editor.serializedObject.ApplyModifiedProperties();
editor.RefreshListDataAndPresenation();
};
}
public void UpdateMoveButtonVisibility(SerializedProperty labelsArray)
public IdLabelElementInLabelConfig(LabelConfigEditor<IdLabelEntry> editor, SerializedProperty labelsArray) :
base(editor, labelsArray)
m_MoveDownButton.visible = m_IndexInList != labelsArray.arraySize - 1;
m_MoveUpButton.visible = m_IndexInList != 0;
}
class NonPresentLabelElement : VisualElement
{
private string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
private string m_UxmlPath;
private Button m_AddButton;
public Label m_Label;
public NonPresentLabelElement(IdLabelConfigEditor editor, SerializedProperty labelsArray)
protected override void InitExtended()
m_UxmlPath = m_UxmlDir + "SuggestedLabelElement.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree(this);
m_Label = this.Q<Label>("label-value");
m_AddButton = this.Q<Button>("add-button");
m_AddButton.clicked += () => { editor.AddNewLabel(labelsArray, m_Label.text); };
m_LabelId = this.Q<Label>("label-id-value");
}
}
}

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


using UnityEngine.Perception.GroundTruth;
using UnityEngine.PlayerLoop;
using UnityEngine.Rendering.UI;
using UnityEngine.UI;
using Button = UnityEngine.UIElements.Button;
namespace UnityEditor.Perception.GroundTruth
{

m_LabelTextField.isDelayed = true;
ScrollView tmp = listView.Q<ScrollView>();
tmp.verticalScroller.slider.RegisterCallback<MouseDownEvent>(evt =>
{
Debug.Log("mouse down");
});
listView.RegisterCallback<FocusInEvent>(evt =>
{
Debug.Log("list focused");
});
m_LabelTextField.RegisterCallback<FocusOutEvent>(evt =>
{
Debug.Log("focus out");
});
m_LabelTextField.RegisterCallback<FocusInEvent>(evt =>
{
Debug.Log("focus in");
});
//Do not let the user define a duplicate label
if (editor.CommonLabels.Contains(cEvent.newValue) && editor.CommonLabels.IndexOf(cEvent.newValue) != m_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.RefreshData();
return;
}
bool shouldRefresh = false;
foreach (var targetObject in editor.targets)

m_CommonLabels.Clear();
var firstTarget = editor.targets[0] as Labeling;
m_CommonLabels.AddRange(firstTarget.labels);
if (firstTarget)
{
m_CommonLabels.AddRange(firstTarget.labels);
foreach (var obj in editor.targets)
{
m_CommonLabels = m_CommonLabels.Intersect(((Labeling) obj).labels).ToList();
}
foreach (var obj in editor.targets)
{
m_CommonLabels = m_CommonLabels.Intersect(((Labeling) obj).labels).ToList();
}
foreach (var targetObject in editor.targets)
{
if (targetObject is Labeling labeling)
foreach (var targetObject in editor.targets)
RemoveLabelFromLabelingSerObj(labeling, m_CommonLabels);
if (targetObject is Labeling labeling)
{
RemoveLabelFromLabelingSerObj(labeling, m_CommonLabels);
}
editor.serializedObject.SetIsDifferentCacheDirty();
editor.RefreshData();
editor.serializedObject.SetIsDifferentCacheDirty();
editor.RefreshData();
};
}

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


using System;
using System.Collections;
using System.IO;
using Unity.Mathematics;
using UnityEditor.UIElements;
using UnityEditor.VersionControl;
using UnityEngine.UIElements;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Task = System.Threading.Tasks.Task;
class SemanticSegmentationLabelConfigEditor : Editor
class SemanticSegmentationLabelConfigEditor : LabelConfigEditor<SemanticSegmentationLabelEntry>
ReorderableList m_LabelsList;
const float k_Margin = 5f;
protected override LabelConfig<SemanticSegmentationLabelEntry> TargetLabelConfig => (SemanticSegmentationLabelConfig) serializedObject.targetObject;
public void OnEnable()
protected override void OnEnableExtended()
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_MoveButtons.style.display = DisplayStyle.None;
}
public override void PostRemoveOperations()
{ }
protected override void SetupPresentLabelsListView()
{
base.SetupPresentLabelsListView();
VisualElement MakeItem() =>
new ColoredLabelElementInLabelConfig(this, m_SerializedLabelsArray);
void BindItem(VisualElement e, int i)
{
if (e is ColoredLabelElementInLabelConfig addedLabel)
{
addedLabel.m_IndexInList = i;
addedLabel.m_LabelTextField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.label)));
addedLabel.m_ColorField.BindProperty(m_SerializedLabelsArray.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color)));
addedLabel.UpdateMoveButtonVisibility(m_SerializedLabelsArray);
}
}
m_LabelListView.bindItem = BindItem;
m_LabelListView.makeItem = MakeItem;
void OnAdd(ReorderableList list)
protected override SemanticSegmentationLabelEntry CreateLabelEntryFromLabelString(SerializedProperty serializedArray, string labelToAdd)
for (int i = 0; i < list.serializedProperty.arraySize; i++)
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));
Color foundColor;
colorProperty.colorValue = standardColorList.First();
foundColor = standardColorList.First();
colorProperty.colorValue = Random.ColorHSV(0, 1, .5f, 1, 1, 1);
foundColor = 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 SemanticSegmentationLabelEntry ImportFromJsonExtended(string labelString, JObject labelEntryJObject,
List<SemanticSegmentationLabelEntry> previousEntries, bool preventDuplicateIdentifiers = true)
var element = m_LabelsList.serializedProperty.GetArrayElementAtIndex(index);
var colorProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color));
var labelProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.label));
using (var change = new EditorGUI.ChangeCheckScope())
bool invalid = false;
Color parsedColor = Color.black;
if (labelEntryJObject.TryGetValue("Color", out var colorToken))
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)
var colorString = colorToken.Value<string>();
if (ColorUtility.TryParseHtmlString(colorString, out parsedColor))
labelProperty.stringValue = newLabel;
if (preventDuplicateIdentifiers && previousEntries.FindAll(entry => entry.color == parsedColor).Count > 0)
{
Debug.LogError("File contains a duplicate Label Color: " + colorString);
invalid = true;
}
}
else
{
Debug.LogError("Error parsing Color for Label Entry" + labelEntryJObject +
" from file. Please make sure a string value is provided in the file and that it is properly formatted as an HTML color.");
invalid = true;
using (var change = new EditorGUI.ChangeCheckScope())
else
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)
{
colorProperty.colorValue = newLabel;
}
Debug.LogError("Error reading the Color field for Label Entry" + labelEntryJObject +
" from file. Please check the formatting.");
invalid = true;
return new SemanticSegmentationLabelEntry
{
label = invalid? InvalidLabel : labelString,
color = parsedColor
};
public override void OnInspectorGUI()
protected override void AddLabelIdentifierToJson(SerializedProperty labelEntry, JObject jObj)
{
jObj.Add("Color", "#"+ColorUtility.ToHtmlStringRGBA(
labelEntry.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color)).colorValue));
}
protected override void AppendLabelEntryToSerializedArray(SerializedProperty serializedArray, SemanticSegmentationLabelEntry semanticSegmentationLabelEntry)
serializedObject.Update();
var index = serializedArray.arraySize;
serializedArray.InsertArrayElementAtIndex(index);
var element = serializedArray.GetArrayElementAtIndex(index);
var colorProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color));
colorProperty.colorValue = semanticSegmentationLabelEntry.color;
var labelProperty = element.FindPropertyRelative(nameof(ILabelEntry.label));
labelProperty.stringValue = semanticSegmentationLabelEntry.label;
}
}
m_LabelsList.DoLayoutList();
this.serializedObject.ApplyModifiedProperties();
internal class ColoredLabelElementInLabelConfig : LabelElementInLabelConfig<SemanticSegmentationLabelEntry>
{
protected override string UxmlPath => UxmlDir + "ColoredLabelElementInLabelConfig.uxml";
public ColorField m_ColorField;
public ColoredLabelElementInLabelConfig(LabelConfigEditor<SemanticSegmentationLabelEntry> editor, SerializedProperty labelsArray) : base(editor, labelsArray)
{ }
protected override void InitExtended()
{
m_ColorField = this.Q<ColorField>("label-color-value");
}
}
}

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


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

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


border-radius: 4px;
border-width: 1px;
padding: 10px;
background-color: rgb(75, 75, 75);
background-color: #454545;
align-content: flex-start;
}

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


<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 style="flex-direction: row; min-width:100px">
<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="add-label" text="Add New Label" class="labeling__add-label-button"/>
</VisualElement>
<!--<Toggle name = "auto-assign-toggle" text="Auto Assign Ids" style="margin-top: 5px"/>-->
</VisualElement>
<VisualElement name="other-labels" class="inner-container depth2">

</VisualElement>
<VisualElement name="other-labels" class="inner-container depth2">
<Label text="Import Labels" name="added-labels-title" class="title-label"/>
<VisualElement class="helpbox" style = "margin: 7px 0 7px 0 ">
<Label text="Import or Export List" 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 ">
<Button name = "import-file-button" text="Import from File" class="title-label"
style="align-self: center; padding: 10px; width: 150px; height: 40px; margin-top: 5px;"/>
</VisualElement>
</VisualElement>

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


using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.Mathematics;
using UnityEditor.UIElements;
using UnityEditor.VersionControl;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.UIElements;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Task = System.Threading.Tasks.Task;
namespace UnityEditor.Perception.GroundTruth
{
abstract class LabelConfigEditor<T> : Editor where T : ILabelEntry
{
private string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
private string m_UxmlPath;
private int m_AddedLabelsItemHeight = 37;
private int m_OtherLabelsItemHeight = 27;
protected abstract LabelConfig<T> TargetLabelConfig { get; }
private List<string> m_AddedLabels = new List<string>();
public List<string> AddedLabels => m_AddedLabels;
protected SerializedProperty m_SerializedLabelsArray;
private static HashSet<string> allLabelsInProject = new HashSet<string>();
private List<string> m_LabelsNotPresentInConfig = new List<string>();
private VisualElement m_Root;
protected ListView m_LabelListView;
private ListView m_NonPresentLabelsListView;
private Button m_SaveButton;
private Button m_AddNewLabelButton;
protected Button m_MoveUpButton;
protected Button m_MoveDownButton;
protected VisualElement m_MoveButtons;
private Button m_ImportFromFileButton;
private Button m_ExportToFileButton;
public void OnEnable()
{
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_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_SaveButton.SetEnabled(false);
m_SerializedLabelsArray = serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName);
UpdateMoveButtonState();
RefreshAddedLabels();
SetupPresentLabelsListView();
RefreshLabelsMasterList();
RefreshNonPresentLabels();
SetupNonPresentLabelsListView();
OnEnableExtended();
m_AddNewLabelButton.clicked += () => { AddNewLabel(m_SerializedLabelsArray, m_AddedLabels); };
m_LabelListView.RegisterCallback<ClickEvent>(evt => { UpdateMoveButtonState(); });
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(this);
var writer = File.CreateText(path);
writer.Write(fileContents);
writer.Flush();
writer.Close();
}
};
}
protected abstract void OnEnableExtended();
public abstract void PostRemoveOperations();
public override VisualElement CreateInspectorGUI()
{
serializedObject.Update();
RefreshListDataAndPresenation();
return m_Root;
}
static void RefreshLabelsMasterList()
{
allLabelsInProject.Clear();
var allPrefabPaths = GetAllPrefabsInProject();
foreach (var path in allPrefabPaths)
{
var asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
var labeling = asset.GetComponent<Labeling>();
if (labeling)
{
allLabelsInProject.UnionWith(labeling.labels);
}
}
}
private void RefreshNonPresentLabels()
{
m_LabelsNotPresentInConfig.Clear();
m_LabelsNotPresentInConfig.AddRange(allLabelsInProject);
m_LabelsNotPresentInConfig.RemoveAll(label => m_AddedLabels.Contains(label));
}
private static IEnumerable<string> GetAllPrefabsInProject()
{
var allPaths = AssetDatabase.GetAllAssetPaths();
return allPaths.Where(path => path.EndsWith(".prefab")).ToList();
}
private void UpdateMoveButtonState()
{
var selectedIndex = m_LabelListView.selectedIndex;
m_MoveDownButton.SetEnabled(selectedIndex > -1);
m_MoveUpButton.SetEnabled(selectedIndex > -1);
}
public void RefreshListDataAndPresenation()
{
serializedObject.Update();
RefreshAddedLabels();
RefreshNonPresentLabels();
m_NonPresentLabelsListView.Refresh();
RefreshListViewHeight();
m_LabelListView.Refresh();
}
private void ScrollToBottomAndSelectLastItem()
{
m_LabelListView.SetSelection(m_LabelListView.itemsSource.Count - 1);
UpdateMoveButtonState();
m_Root.schedule.Execute(() => { m_LabelListView.ScrollToItem(-1); })
.StartingIn(
10); //to circumvent the delay in the listview's internal scrollview updating its geometry (when new items are added).
}
protected void RefreshAddedLabels()
{
m_AddedLabels.Clear();
m_AddedLabels.AddRange(TargetLabelConfig.labelEntries.Select(entry => entry.label));
}
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(); });
}
private void SetupNonPresentLabelsListView()
{
m_NonPresentLabelsListView.itemsSource = m_LabelsNotPresentInConfig;
VisualElement MakeItem()
{
var element = new NonPresentLabelElement<T>(this, m_SerializedLabelsArray);
return element;
}
void BindItem(VisualElement e, int i)
{
if (e is NonPresentLabelElement<T> nonPresentLabel)
{
nonPresentLabel.m_Label.text = m_LabelsNotPresentInConfig[i];
}
}
m_NonPresentLabelsListView.bindItem = BindItem;
m_NonPresentLabelsListView.makeItem = MakeItem;
m_NonPresentLabelsListView.itemHeight = m_OtherLabelsItemHeight;
m_NonPresentLabelsListView.selectionType = SelectionType.None;
}
// public void SetSaveButtonEnabled(bool enabled)
// {
// m_SaveButton.SetEnabled(enabled);
// }
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;
}
private void AddNewLabel(SerializedProperty serializedArray, List<string> presentLabels)
{
AddLabel(serializedArray, FindNewLabelString(presentLabels));
}
public void AddLabel(SerializedProperty serializedArray, string labelToAdd)
{
if (m_AddedLabels.Contains(labelToAdd)) //label has already been added, cannot add again
return;
AppendLabelEntryToSerializedArray(serializedArray, CreateLabelEntryFromLabelString(serializedArray, labelToAdd));
serializedObject.ApplyModifiedProperties();
RefreshListDataAndPresenation();
ScrollToBottomAndSelectLastItem();
}
protected abstract T CreateLabelEntryFromLabelString(SerializedProperty serializedArray, string labelToAdd);
// Import a list of IdLabelEntry objects to the target object's serialized entries array. This function assumes the labelsToAdd list does not contain any duplicate labels.
protected virtual void ImportLabelEntryListIntoSerializedArray(SerializedProperty serializedArray,
List<T> labelEntriesToAdd)
{
serializedArray.ClearArray();
foreach (var entry in labelEntriesToAdd)
{
AppendLabelEntryToSerializedArray(serializedArray, entry);
}
serializedObject.ApplyModifiedProperties();
RefreshListDataAndPresenation();
}
protected abstract void AppendLabelEntryToSerializedArray(SerializedProperty serializedArray, T labelEntry);
protected abstract T ImportFromJsonExtended(string labelString, JObject jsonObject, List<T> previousEntries, bool preventDuplicateIdentifiers = true);
protected const string InvalidLabel = "INVALID_LABEL";
void ImportFromJson(JObject jsonObj)
{
var importedLabelEntries = new List<T>();
if (jsonObj.TryGetValue("LabelEntryType", out var type))
{
if (type.Value<string>() == typeof(T).Name)
{
if (jsonObj.TryGetValue("LabelEntries", out var labelsArrayToken))
{
JArray labelsArray = JArray.Parse(labelsArrayToken.ToString());
if (labelsArray != null)
{
foreach (var labelEntryToken in labelsArray)
{
if (labelEntryToken is JObject entryObject)
{
if (entryObject.TryGetValue("Label", out var labelToken))
{
string labelString = labelToken.Value<string>();
if (importedLabelEntries.FindAll(entry => entry.label == labelString)
.Count > 0)
{
Debug.LogError("File contains a duplicate Label: " + labelString);
return;
}
T labelEntry = ImportFromJsonExtended(labelString, entryObject, importedLabelEntries);
if (labelEntry.label == InvalidLabel)
return;
importedLabelEntries.Add(labelEntry);
}
else
{
Debug.LogError("Error reading Label for Label Entry" + labelEntryToken +
" from file. Please check the formatting.");
return;
}
}
else
{
Debug.LogError("Error reading Label Entry " + labelEntryToken +
" from file. Please check the formatting.");
return;
}
}
ImportLabelEntryListIntoSerializedArray(m_SerializedLabelsArray, importedLabelEntries);
}
else
{
Debug.LogError(
"Could not read list of Label Entries from file. Please check the formatting.");
}
}
}
else
{
Debug.LogError("Specified LabelEntryType does not match " + typeof(T).Name);
}
}
else
{
Debug.LogError("LabelEntryType not found.");
}
}
protected abstract void AddLabelIdentifierToJson(SerializedProperty labelEntry, JObject jObj);
private string ExportToJson(LabelConfigEditor<T> editor)
{
JObject result = new JObject();
result.Add("LabelEntryType", typeof(T).Name);
JArray labelEntries = new JArray();
for (int i = 0; i < m_SerializedLabelsArray.arraySize; i++)
{
var entry = m_SerializedLabelsArray.GetArrayElementAtIndex(i);
JObject entryJobj = new JObject();
entryJobj.Add("Label", entry.FindPropertyRelative(nameof(ILabelEntry.label)).stringValue);
AddLabelIdentifierToJson(entry, entryJobj);
labelEntries.Add(entryJobj);
}
result.Add("LabelEntries", labelEntries);
return result.ToString();
}
}
internal abstract class LabelElementInLabelConfig<T> : VisualElement where T : ILabelEntry
{
protected const string UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
protected abstract string UxmlPath { get; }
private Button m_RemoveButton;
private Button m_MoveUpButton;
private Button m_MoveDownButton;
public TextField m_LabelTextField;
public int m_IndexInList;
protected SerializedProperty m_LabelsArray;
//protected ListView m_LabelsListView;
protected LabelConfigEditor<T> m_LabelConfigEditor;
protected LabelElementInLabelConfig(LabelConfigEditor<T> editor, SerializedProperty labelsArray)
{
m_LabelConfigEditor = editor;
m_LabelsArray = labelsArray;
//m_LabelsListView = labelsListView;
Init();
}
private void Init()
{
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UxmlPath).CloneTree(this);
m_LabelTextField = this.Q<TextField>("label-value");
m_RemoveButton = this.Q<Button>("remove-button");
m_MoveUpButton = this.Q<Button>("move-up-button");
m_MoveDownButton = this.Q<Button>("move-down-button");
m_LabelTextField.isDelayed = true;
InitExtended();
m_LabelTextField.RegisterValueChangedCallback<string>((cEvent) =>
{
if (m_LabelConfigEditor.AddedLabels.Contains(cEvent.newValue) && m_LabelConfigEditor.AddedLabels.IndexOf(cEvent.newValue) != m_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 m_LabelConfigEditor.AddedLabels.IndexOf(cEvent.newValue) != m_IndexInList 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(m_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 proprty.
m_LabelsArray.serializedObject.ApplyModifiedProperties();
m_LabelConfigEditor.RefreshListDataAndPresenation();
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(m_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.RefreshListDataAndPresenation();
}
});
m_RemoveButton.clicked += () =>
{
m_LabelsArray.DeleteArrayElementAtIndex(m_IndexInList);
m_LabelConfigEditor.PostRemoveOperations();
m_LabelConfigEditor.serializedObject.ApplyModifiedProperties();
m_LabelConfigEditor.RefreshListDataAndPresenation();
};
}
protected abstract void InitExtended();
public void UpdateMoveButtonVisibility(SerializedProperty labelsArray)
{
m_MoveDownButton.visible = m_IndexInList != labelsArray.arraySize - 1;
m_MoveUpButton.visible = m_IndexInList != 0;
}
}
class NonPresentLabelElement<T> : VisualElement where T : ILabelEntry
{
private string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
private string m_UxmlPath;
private Button m_AddButton;
public Label m_Label;
public NonPresentLabelElement(LabelConfigEditor<T> editor, SerializedProperty labelsArray)
{
m_UxmlPath = m_UxmlDir + "SuggestedLabelElement.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree(this);
m_Label = this.Q<Label>("label-value");
m_AddButton = this.Q<Button>("add-button");
m_AddButton.clicked += () => { editor.AddLabel(labelsArray, m_Label.text); };
}
}
}

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


fileFormatVersion: 2
guid: ffb8bb7ed73a343f3ae88c68aaf8a84f
timeCreated: 1585940009

84
com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor_OLD.cs


//#define ENABLED
#if ENABLED
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Perception.GroundTruth;
using Random = UnityEngine.Random;
namespace UnityEditor.Perception.GroundTruth
{
[CustomEditor(typeof(SemanticSegmentationLabelConfig))]
class SemanticSegmentationLabelConfigEditor : Editor
{
ReorderableList m_LabelsList;
const float k_Margin = 5f;
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;
}
void OnAdd(ReorderableList list)
{
var standardColorList = new List<Color>(SemanticSegmentationLabelConfig.s_StandardColors);
for (int i = 0; i < list.serializedProperty.arraySize; i++)
{
var item = list.serializedProperty.GetArrayElementAtIndex(i);
standardColorList.Remove(item.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color)).colorValue);
}
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);
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(target);
}
void DrawElement(Rect rect, int index, bool isactive, bool isfocused)
{
var element = m_LabelsList.serializedProperty.GetArrayElementAtIndex(index);
var colorProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.color));
var labelProperty = element.FindPropertyRelative(nameof(SemanticSegmentationLabelEntry.label));
using (var change = new EditorGUI.ChangeCheckScope())
{
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)
{
colorProperty.colorValue = newLabel;
}
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
m_LabelsList.DoLayoutList();
this.serializedObject.ApplyModifiedProperties();
}
}
}
#endif

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


fileFormatVersion: 2
guid: e8cb4fead5b34d41884c1c9a77308c72
timeCreated: 1593454492

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

/com.unity.perception/Editor/GroundTruth/Uxml/LabelElementInLabelConfig.uxml → /com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml

/com.unity.perception/Editor/GroundTruth/Uxml/LabelElementInLabelConfig.uxml.meta → /com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml.meta

正在加载...
取消
保存