浏览代码

IdLabelConfig editor almost done

/main
Mohsen Kamalzadeh 4 年前
当前提交
bd94366d
共有 6 个文件被更改,包括 443 次插入48 次删除
  1. 402
      com.unity.perception/Editor/GroundTruth/IdLabelConfigEditor.cs
  2. 7
      com.unity.perception/Editor/GroundTruth/LabelingEditor.cs
  3. 29
      com.unity.perception/Editor/GroundTruth/Uss/Styles.uss
  4. 37
      com.unity.perception/Editor/GroundTruth/Uxml/LabelConfig_Main.uxml
  5. 10
      com.unity.perception/Editor/GroundTruth/Uxml/LabelElementInLabelConfig.uxml
  6. 6
      com.unity.perception/Editor/Randomization/Uss/Styles.uss

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


using System;
using System.Collections;
using System.IO;
using System.Linq;
using Unity.Mathematics;
using UnityEditor.UIElements;

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
{

private ListView m_LabelListView;
private ListView m_NonPresentLabelsListView;
private string m_UxmlDir = "Packages/com.unity.perception/Editor/GroundTruth/Uxml/";
private string m_UxmlPath;

private Button m_SaveButton;
private Button m_AddNewLabelButton
private Button m_AddNewLabelButton;
private Button m_MoveUpButton;
private Button m_MoveDownButton;
private Button m_ImportFromFileButton;
private static HashSet<string> allLabelsInProject = new HashSet<string>();
private List<string> m_LabelsNotPresentInConfig = new List<string>();
m_NonPresentLabelsListView = m_Root.Q<ListView>("labels-in-project-listview");
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_IdLabelConig = (IdLabelConfig)target;
m_IdLabelConig = (IdLabelConfig) serializedObject.targetObject;
UpdateMoveButtonState();
if (m_AutoAssign)
{

}
RefreshAddedLabels();
SetupLabelsListView();
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);
}
};
m_IdLabelConig = (IdLabelConfig)target;
m_IdLabelConig = (IdLabelConfig) serializedObject.targetObject;
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);
}
}
}
void RefreshNonPresentLabels()
{
m_LabelsNotPresentInConfig.Clear();
m_LabelsNotPresentInConfig.AddRange(allLabelsInProject);
m_LabelsNotPresentInConfig.RemoveAll(label => m_AddedLabels.Contains(label));
}
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);
}
return prefabPaths;
}
void UpdateMoveButtonState()
{
var selectedIndex = m_LabelListView.selectedIndex;
m_MoveDownButton.SetEnabled(selectedIndex > -1);
m_MoveUpButton.SetEnabled(selectedIndex > -1);
}
public void RefreshListDataAndPresenation()
{
serializedObject.Update();
RefreshNonPresentLabels();
m_NonPresentLabelsListView.Refresh();
RefreshListViewHeight();
return m_Root;
}
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).
m_AddedLabels.AddRange( m_IdLabelConig.labelEntries.Select(entry => entry.label));
m_AddedLabels.AddRange(m_IdLabelConig.labelEntries.Select(entry => entry.label));
void SetupLabelsListView()
void SetupPresentLabelsListView()
{
m_LabelListView.itemsSource = m_AddedLabels;

}
}
const int itemHeight = 30;
const int itemHeight = 35;
m_LabelListView.RegisterCallback<AttachToPanelEvent>(evt =>
m_LabelListView.RegisterCallback<AttachToPanelEvent>(evt => { RefreshListViewHeight(); });
}
void SetupNonPresentLabelsListView()
{
m_NonPresentLabelsListView.itemsSource = m_LabelsNotPresentInConfig;
VisualElement MakeItem()
RefreshListViewHeight();
});
var element = new NonPresentLabelElement(this, m_SerializedLabelsArray);
return element;
}
void BindItem(VisualElement e, int i)
{
if (e is NonPresentLabelElement nonPresentLabel)
{
nonPresentLabel.m_Label.text = m_LabelsNotPresentInConfig[i];
}
}
const int itemHeight = 27;
m_NonPresentLabelsListView.bindItem = BindItem;
m_NonPresentLabelsListView.makeItem = MakeItem;
m_NonPresentLabelsListView.itemHeight = itemHeight;
m_NonPresentLabelsListView.selectionType = SelectionType.None;
}
public void SetSaveButtonEnabled(bool enabled)

public void RefreshListViewHeight()
{
m_LabelListView.style.minHeight = Mathf.Clamp(m_LabelListView.itemsSource.Count * m_LabelListView.itemHeight, 300, 600);
m_LabelListView.style.minHeight =
Mathf.Clamp(m_LabelListView.itemsSource.Count * m_LabelListView.itemHeight, 300, 600);
void AutoAssignIds()
{
var serializedProperty = serializedObject.FindProperty(IdLabelConfig.labelEntriesFieldName);

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;
void AddNewLabel(SerializedProperty serializedArray, HashSet<string> presentLabels)
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;

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)
{
idProperty.intValue = maxLabel + 1;
idProperty.intValue = id;
labelProperty.stringValue = FindNewLabelValue(presentLabels);
// if (m_AutoAssign)
// AutoAssignIds();
serializedObject.ApplyModifiedProperties();
//EditorUtility.SetDirty(target);
labelProperty.stringValue = label;
string FindNewLabelValue(HashSet<string> labels)
string FindNewLabelValue(List<string> labels)
{
string baseLabel = "New Label";
string label = baseLabel;

label = baseLabel + "_" + count++;
}
void ImportFromJson(JObject jsonObj)
{
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
{
Debug.LogError("LabelEntryType not found.");
}
}
}
class LabelElementInLabelConfig : VisualElement

public int m_IndexInList;
public LabelElementInLabelConfig(IdLabelConfigEditor editor, SerializedProperty labelsArray, ListView labelsListView)
public LabelElementInLabelConfig(IdLabelConfigEditor editor, SerializedProperty labelsArray,
ListView labelsListView)
{
m_UxmlPath = m_UxmlDir + "LabelElementInLabelConfig.uxml";
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(m_UxmlPath).CloneTree(this);

m_MoveDownButton = this.Q<Button>("move-down-button");
m_LabelId = this.Q<Label>("label-id-value");
m_LabelTextField.isDelayed = true;
m_MoveDownButton.clicked += () =>
{

labelsArray.GetArrayElementAtIndex(m_IndexInList).FindPropertyRelative(nameof(IdLabelEntry.label));
labelsArray.GetArrayElementAtIndex(m_IndexInList)
.FindPropertyRelative(nameof(IdLabelEntry.label));
.FindPropertyRelative("label");
.FindPropertyRelative(nameof(IdLabelEntry.label));
var tmpString = bottomProperty.stringValue;
bottomProperty.stringValue = currentProperty.stringValue;

if (m_IndexInList > 0)
{
var currentProperty =
labelsArray.GetArrayElementAtIndex(m_IndexInList).FindPropertyRelative(nameof(IdLabelEntry.label));
labelsArray.GetArrayElementAtIndex(m_IndexInList)
.FindPropertyRelative(nameof(IdLabelEntry.label));
.FindPropertyRelative("label");
.FindPropertyRelative(nameof(IdLabelEntry.label));
var tmpString = topProperty.stringValue;
topProperty.stringValue = currentProperty.stringValue;

editor.serializedObject.ApplyModifiedProperties();
labelsListView.Refresh();
editor.RefreshListViewHeight();
editor.RefreshListDataAndPresenation();
//AssetDatabase.SaveAssets();
}
};

editor.SetSaveButtonEnabled(EditorUtility.IsDirty(editor.target));
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();
}
labelsArray.DeleteArrayElementAtIndex(m_IndexInList);
editor.serializedObject.ApplyModifiedProperties();
editor.RefreshListDataAndPresenation();
};
}

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)
{
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); };
}
}
}

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


m_LabelTextField.RegisterValueChangedCallback<string>((cEvent) =>
{
bool shouldRefresh = false;
foreach (var targetObject in editor.targets)
{
if (targetObject is Labeling labeling)

var serializedLabelingObject2 = new SerializedObject(labeling);
var serializedLabelArray2 = serializedLabelingObject2.FindProperty("labels");
serializedLabelArray2.GetArrayElementAtIndex(indexToModifyInTargetLabelList).stringValue = cEvent.newValue;
shouldRefresh = shouldRefresh || serializedLabelArray2.serializedObject.hasModifiedProperties;
editor.RefreshData();
//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.RefreshData();
});
m_AddToConfigButton.clicked += () =>

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


align-self: center;
}
.labeling__remove-item-button:hover {
background-color: #808080;
}
.added-label {
margin: 3px 4px 3px 4px;
flex-direction: row;

.labeling__label-listview {
flex-grow: 1;
min-height: 150;
min-height: 150px;
border-radius: 4px;
margin-right: 2px;
margin-top: 2px;

border-width: 0;
margin-right: 2px;
margin-top: 2px;
border-radius: 2px;
}
.labeling__add-label-button:hover{
background-color: #606060;
}
.labeling__add-label-button:active{
background-color: #303030;
}
.outer-container {

.move-label-in-config-button{
background-color: #505050;
width: 30px;
height: 16px;
-unity-background-scale-mode: scale-to-fit;
}
.move-up{

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;
}

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


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="outer-container" name="outer-container">
<Style src="../Uss/Styles.uss"/>
<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"/>
<Button name="add-label" text="Add New Label" class="labeling__add-label-button"/>
<VisualElement name="added-labels" class="inner-container" style="margin-top:0px">
<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 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="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"/>
</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="Warning:" style="color:yellow; margin-bottom: 5px"/>
<TextElement
text="Importing labels from file will overwrite all existing contents of this label configuration."/>
</VisualElement>
<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>
</UXML>

10
com.unity.perception/Editor/GroundTruth/Uxml/LabelElementInLabelConfig.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="added-label">
<VisualElement class="added-label" style="padding-top: 3px;">
<VisualElement style = "flex-direction: row; padding: 3px; margin-left: 3px; margin-right: 3px; border-width: 1px; border-color: dimgray; border-radius: 4px;">
<Label name="label-id-title" text = "id = " style="align-self:center; color: #CACACA;"/>
<Label name="label-id-value" style="-unity-font-style: bold; color: #CACACA; min-width : 25px; align-self:center;"/>
<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;"/>
<Label name="label-id-value" style="-unity-font-style: bold; color: #888888; min-width : 25px; align-self:center;"/>
<VisualElement style="min-width:20px; flex-direction: row;">
<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>

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


/* Scenario classes */
.scenario__info-box {
border-width: 1px;
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;

background-color: #191919;
padding: 2px;
}
/* Randomizer classes */
.randomizer__drag-bar {

正在加载...
取消
保存