Mohsen Kamalzadeh
4 年前
当前提交
e5685e3b
共有 14 个文件被更改,包括 881 次插入 和 540 次删除
-
608com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs
-
61com.unity.perception/Editor/GroundTruth/LabelingEditor.cs
-
150com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs
-
4com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor.cs.meta
-
2com.unity.perception/Editor/GroundTruth/Uss/Styles.uss
-
15com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml
-
474com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs
-
3com.unity.perception/Editor/GroundTruth/LabelConfigEditor.cs.meta
-
84com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor_OLD.cs
-
3com.unity.perception/Editor/GroundTruth/SemanticSegmentationLabelConfigEditor_OLD.cs.meta
-
14com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml
-
3com.unity.perception/Editor/GroundTruth/Uxml/ColoredLabelElementInLabelConfig.uxml.meta
-
0/com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml
-
0/com.unity.perception/Editor/GroundTruth/Uxml/IdLabelElementInLabelConfig.uxml.meta
|
|||
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"); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e8cb4fead5b34d41884c1c9a77308c72 |
|||
timeCreated: 1593454492 |
|||
guid: c8e809a6323844c71aa3cb751a6ae9a1 |
|||
timeCreated: 1585940009 |
|
|||
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); }; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ffb8bb7ed73a343f3ae88c68aaf8a84f |
|||
timeCreated: 1585940009 |
|
|||
//#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
|
|
|||
fileFormatVersion: 2 |
|||
guid: e8cb4fead5b34d41884c1c9a77308c72 |
|||
timeCreated: 1593454492 |
|
|||
<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> |
|
|||
fileFormatVersion: 2 |
|||
guid: 092a4ceabfda6410dbc745780dd88616 |
|||
timeCreated: 1603311500 |
撰写
预览
正在加载...
取消
保存
Reference in new issue