|
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Linq; |
|
|
|
using System.Reflection; |
|
|
|
using JetBrains.Annotations; |
|
|
|
using UnityEditor; |
|
|
|
using UnityEngine.Perception.Randomization.Parameters; |
|
|
|
using EngineAnalytics = UnityEngine.Analytics.Analytics; |
|
|
|
using UnityEngine.Perception.Randomization.Samplers; |
|
|
|
static class PerceptionEngineAnalytics |
|
|
|
public static class PerceptionEngineAnalytics |
|
|
|
// Events
|
|
|
|
const int k_MaxElementsInStruct = 100; |
|
|
|
const int k_MaxEventsPerHour = 100; |
|
|
|
static bool s_AllEventsRegistered; |
|
|
|
|
|
|
|
// Runtime Events
|
|
|
|
static string[] allEvents => new[] { k_EventScenarioCompleted }; |
|
|
|
const string k_EventScenarioCompleted = "perceptionScenarioCompleted"; |
|
|
|
|
|
|
|
#region Event: Scenario Completed
|
|
|
|
|
|
|
|
/// When a Scenario completes in editor, player, or USim
|
|
|
|
/// Which labelers will be identified and included in the analytics information.
|
|
|
|
const string k_ScenarioCompletedName = "perceptionscenariocompleted"; |
|
|
|
/* |
|
|
|
static int k_MaxItems = 100; |
|
|
|
static int k_MaxEventsPerHour = 100; |
|
|
|
*/ |
|
|
|
static bool s_IsRegistered; |
|
|
|
|
|
|
|
static readonly string[] LabelAllowlist = new[] |
|
|
|
static readonly Type[] k_LabelerAllowList = new[] |
|
|
|
"BoundingBox2DLabeler" |
|
|
|
typeof(BoundingBox3DLabeler), typeof(BoundingBox2DLabeler), typeof(InstanceSegmentationLabeler), |
|
|
|
typeof(KeypointLabeler), typeof(ObjectCountLabeler), typeof(SemanticSegmentationLabeler) |
|
|
|
static bool TryRegisterEvents() |
|
|
|
{ |
|
|
|
|
|
|
|
if (s_IsRegistered) |
|
|
|
return true; |
|
|
|
#region Data Structures
|
|
|
|
var success = true; |
|
|
|
/* |
|
|
|
success &= EngineAnalytics.RegisterEvent(k_ScenarioCompletedName, k_MaxEventsPerHour, k_MaxItems, |
|
|
|
k_VendorKey) == AnalyticsResult.Ok; |
|
|
|
*/ |
|
|
|
s_IsRegistered = success; |
|
|
|
return success; |
|
|
|
} |
|
|
|
|
|
|
|
#region perceptionscenariocompleted
|
|
|
|
struct PerceptionCameraData |
|
|
|
[Serializable] |
|
|
|
public class PerceptionCameraData |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
/*struct LabelerData |
|
|
|
|
|
|
|
[Serializable] |
|
|
|
public class LabelerData |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
}*/ |
|
|
|
struct MemberData |
|
|
|
public string objectFilter; |
|
|
|
public int animationPoseCount; |
|
|
|
|
|
|
|
public static LabelerData FromLabeler(CameraLabeler labeler) |
|
|
|
{ |
|
|
|
var labelerType = labeler.GetType(); |
|
|
|
if (!k_LabelerAllowList.Contains(labelerType)) |
|
|
|
return null; |
|
|
|
|
|
|
|
var labelerData = new LabelerData() |
|
|
|
{ |
|
|
|
name = labeler.GetType().Name |
|
|
|
}; |
|
|
|
|
|
|
|
switch (labeler) |
|
|
|
{ |
|
|
|
case BoundingBox3DLabeler bb3dl: |
|
|
|
labelerData.labelConfigCount = bb3dl.idLabelConfig.labelEntries.Count; |
|
|
|
break; |
|
|
|
case BoundingBox2DLabeler bb2dl: |
|
|
|
labelerData.labelConfigCount = bb2dl.idLabelConfig.labelEntries.Count; |
|
|
|
break; |
|
|
|
case InstanceSegmentationLabeler isl: |
|
|
|
labelerData.labelConfigCount = isl.idLabelConfig.labelEntries.Count; |
|
|
|
break; |
|
|
|
case KeypointLabeler kpl: |
|
|
|
labelerData.labelConfigCount = kpl.idLabelConfig.labelEntries.Count; |
|
|
|
labelerData.objectFilter = kpl.objectFilter.ToString(); |
|
|
|
labelerData.animationPoseCount = kpl.animationPoseConfigs.Count; |
|
|
|
break; |
|
|
|
case ObjectCountLabeler ocl: |
|
|
|
labelerData.labelConfigCount = ocl.labelConfig.labelEntries.Count; |
|
|
|
break; |
|
|
|
case SemanticSegmentationLabeler ssl: |
|
|
|
labelerData.labelConfigCount = ssl.labelConfig.labelEntries.Count; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
return labelerData; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
[Serializable] |
|
|
|
public class MemberData |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
public object value; |
|
|
|
public string value; |
|
|
|
public string type; |
|
|
|
struct ParameterData |
|
|
|
|
|
|
|
[Serializable] |
|
|
|
public class ParameterField |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
public (string name, string distribution)[] values; |
|
|
|
public float value = int.MinValue; |
|
|
|
public float rangeMinimum = int.MinValue; |
|
|
|
public float rangeMaximum = int.MinValue; |
|
|
|
public float mean = int.MinValue; |
|
|
|
public float stdDev = int.MinValue; |
|
|
|
public int categoricalParameterCount = int.MinValue; |
|
|
|
|
|
|
|
public static ParameterField ExtractSamplerInformation(ISampler sampler, string fieldName = "value") |
|
|
|
{ |
|
|
|
switch (sampler) |
|
|
|
{ |
|
|
|
case AnimationCurveSampler _: |
|
|
|
return new ParameterField() |
|
|
|
{ |
|
|
|
name = fieldName, |
|
|
|
distribution = "AnimationCurve" |
|
|
|
}; |
|
|
|
case ConstantSampler cs: |
|
|
|
return new ParameterField() |
|
|
|
{ |
|
|
|
name = fieldName, |
|
|
|
distribution = "Constant", |
|
|
|
value = cs.value |
|
|
|
}; |
|
|
|
case NormalSampler ns: |
|
|
|
return new ParameterField() |
|
|
|
{ |
|
|
|
name = fieldName, |
|
|
|
distribution = "Normal", |
|
|
|
mean = ns.mean, |
|
|
|
stdDev = ns.standardDeviation, |
|
|
|
rangeMinimum = ns.range.minimum, |
|
|
|
rangeMaximum = ns.range.maximum |
|
|
|
}; |
|
|
|
case UniformSampler us: |
|
|
|
return new ParameterField() |
|
|
|
{ |
|
|
|
name = fieldName, |
|
|
|
distribution = "Uniform", |
|
|
|
rangeMinimum = us.range.minimum, |
|
|
|
rangeMaximum = us.range.maximum |
|
|
|
}; |
|
|
|
default: |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public static List<ParameterField> ExtractSamplersInformation(IEnumerable<ISampler> samplers, IEnumerable<string> indexToNameMapping) |
|
|
|
{ |
|
|
|
var samplersList = samplers.ToList(); |
|
|
|
var indexToNameMappingList = indexToNameMapping.ToList(); |
|
|
|
if (samplersList.Count > indexToNameMappingList.Count) |
|
|
|
throw new Exception("Insufficient names provided for mapping ParameterFields"); |
|
|
|
return samplersList.Select((t, i) => ExtractSamplerInformation(t, indexToNameMappingList[i])).ToList(); |
|
|
|
} |
|
|
|
struct RandomizerData |
|
|
|
|
|
|
|
[Serializable] |
|
|
|
public class ParameterData |
|
|
|
[UsedImplicitly] |
|
|
|
[UsedImplicitly] |
|
|
|
public string type; |
|
|
|
public List<ParameterField> fields; |
|
|
|
} |
|
|
|
|
|
|
|
[Serializable] |
|
|
|
public class RandomizerData |
|
|
|
{ |
|
|
|
public string name; |
|
|
|
[UsedImplicitly] |
|
|
|
|
|
|
|
public static RandomizerData FromRandomizer(Randomizer randomizer) |
|
|
|
{ |
|
|
|
if (randomizer == null) |
|
|
|
return null; |
|
|
|
|
|
|
|
var data = new RandomizerData() |
|
|
|
{ |
|
|
|
name = randomizer.GetType().Name, |
|
|
|
}; |
|
|
|
|
|
|
|
var randomizerType = randomizer.GetType(); |
|
|
|
|
|
|
|
// Only looks for randomizers included by the Perception package.
|
|
|
|
if (randomizerType.Namespace == null || !randomizerType.Namespace.StartsWith("UnityEngine.Perception")) |
|
|
|
return null; |
|
|
|
|
|
|
|
// Naming configuration
|
|
|
|
var vectorFieldNames = new[] { "x", "y", "z", "w" }; |
|
|
|
var hsvaFieldNames = new[] { "hue", "saturation", "value", "alpha" }; |
|
|
|
var rgbFieldNames = new[] { "red", "green", "blue" }; |
|
|
|
|
|
|
|
// Add member fields and parameters separately
|
|
|
|
var members = new List<MemberData>(); |
|
|
|
var parameters = new List<ParameterData>(); |
|
|
|
foreach (var field in randomizerType.GetFields(BindingFlags.Public | BindingFlags.Instance)) |
|
|
|
{ |
|
|
|
var member = field.GetValue(randomizer); |
|
|
|
var memberType = member.GetType(); |
|
|
|
|
|
|
|
// If member is either a categorical or numeric parameter
|
|
|
|
if (memberType.IsSubclassOf(typeof(Parameter))) |
|
|
|
{ |
|
|
|
var pd = new ParameterData() |
|
|
|
{ |
|
|
|
name = field.Name, |
|
|
|
type = memberType.Name, |
|
|
|
fields = new List<ParameterField>() |
|
|
|
}; |
|
|
|
|
|
|
|
// All included parameter types
|
|
|
|
switch (member) |
|
|
|
{ |
|
|
|
case CategoricalParameterBase cp: |
|
|
|
pd.fields.Add(new ParameterField() |
|
|
|
{ |
|
|
|
name = "values", |
|
|
|
distribution = "Categorical", |
|
|
|
categoricalParameterCount = cp.probabilities.Count |
|
|
|
}); |
|
|
|
break; |
|
|
|
case BooleanParameter bP: |
|
|
|
pd.fields.Add(ParameterField.ExtractSamplerInformation(bP.value)); |
|
|
|
break; |
|
|
|
case FloatParameter fP: |
|
|
|
pd.fields.Add(ParameterField.ExtractSamplerInformation(fP.value)); |
|
|
|
break; |
|
|
|
case IntegerParameter iP: |
|
|
|
pd.fields.Add(ParameterField.ExtractSamplerInformation(iP.value)); |
|
|
|
break; |
|
|
|
case Vector2Parameter v2P: |
|
|
|
pd.fields.AddRange(ParameterField.ExtractSamplersInformation(v2P.samplers, vectorFieldNames)); |
|
|
|
break; |
|
|
|
case Vector3Parameter v3P: |
|
|
|
pd.fields.AddRange(ParameterField.ExtractSamplersInformation(v3P.samplers, vectorFieldNames)); |
|
|
|
break; |
|
|
|
case Vector4Parameter v4P: |
|
|
|
pd.fields.AddRange(ParameterField.ExtractSamplersInformation(v4P.samplers, vectorFieldNames)); |
|
|
|
break; |
|
|
|
case ColorHsvaParameter hsvaP: |
|
|
|
pd.fields.AddRange(ParameterField.ExtractSamplersInformation(hsvaP.samplers, hsvaFieldNames)); |
|
|
|
break; |
|
|
|
case ColorRgbParameter rgbP: |
|
|
|
pd.fields.AddRange(ParameterField.ExtractSamplersInformation(rgbP.samplers, rgbFieldNames)); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
parameters.Add(pd); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
members.Add(new MemberData() |
|
|
|
{ |
|
|
|
name = field.Name, |
|
|
|
type = memberType.FullName, |
|
|
|
value = member.ToString() |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
data.members = members.ToArray(); |
|
|
|
data.parameters = parameters.ToArray(); |
|
|
|
return data; |
|
|
|
} |
|
|
|
struct ScenarioCompletedData |
|
|
|
|
|
|
|
[Serializable] |
|
|
|
public class ScenarioCompletedData |
|
|
|
[UsedImplicitly] |
|
|
|
public string platform; |
|
|
|
[UsedImplicitly] |
|
|
|
public string[] labelers; |
|
|
|
[UsedImplicitly] |
|
|
|
public LabelerData[] labelers; |
|
|
|
public static void ReportScenarioCompleted(PerceptionCamera cam, IEnumerable<Randomizer> randomizers) |
|
|
|
#endregion
|
|
|
|
|
|
|
|
internal static void ReportScenarioCompleted(PerceptionCamera cam, IEnumerable<Randomizer> randomizers) |
|
|
|
var randomizer = randomizers.First(); |
|
|
|
var randomizerType = randomizer.GetType(); |
|
|
|
var randomizerName = randomizerType.Name; |
|
|
|
var randomizerFields = randomizerType.GetFields(BindingFlags.Public | BindingFlags.Instance); |
|
|
|
|
|
|
|
|
|
|
|
var data = new ScenarioCompletedData() |
|
|
|
var data = new ScenarioCompletedData(); |
|
|
|
if (cam != null) |
|
|
|
perceptionCamera = (cam == null) |
|
|
|
? new PerceptionCameraData() |
|
|
|
: new PerceptionCameraData() |
|
|
|
// Perception Camera Data
|
|
|
|
data.perceptionCamera = new PerceptionCameraData() |
|
|
|
}, |
|
|
|
labelers = (cam == null) |
|
|
|
? new string[]{} |
|
|
|
: cam.labelers |
|
|
|
.Select(labeler => labeler.GetType().Name) |
|
|
|
.Where(labeler => LabelAllowlist.Contains(labeler)).ToArray(), |
|
|
|
randomizers = null |
|
|
|
}; |
|
|
|
// EngineAnalytics.SendEvent(k_ScenarioCompletedName, data);
|
|
|
|
}; |
|
|
|
|
|
|
|
// Labeler Data
|
|
|
|
data.labelers = cam.labelers |
|
|
|
.Select(LabelerData.FromLabeler) |
|
|
|
.Where(labeler => labeler != null).ToArray(); |
|
|
|
} |
|
|
|
|
|
|
|
var randomizerList = randomizers.ToArray(); |
|
|
|
if (randomizerList.Any()) |
|
|
|
{ |
|
|
|
data.randomizers = randomizerList |
|
|
|
.Select(RandomizerData.FromRandomizer) |
|
|
|
.Where(x => x != null).ToArray(); |
|
|
|
} |
|
|
|
|
|
|
|
EditorAnalytics.SendEventWithLimit(k_EventScenarioCompleted, data); |
|
|
|
|
|
|
|
|
|
|
|
static bool TryRegisterEvents() |
|
|
|
{ |
|
|
|
if (s_AllEventsRegistered) |
|
|
|
return true; |
|
|
|
|
|
|
|
var success = true; |
|
|
|
foreach (var eventName in allEvents) |
|
|
|
{ |
|
|
|
success &= EditorAnalytics.RegisterEventWithLimit(eventName, k_MaxEventsPerHour, k_MaxElementsInStruct, k_VendorKey) == AnalyticsResult.Ok; |
|
|
|
} |
|
|
|
|
|
|
|
s_AllEventsRegistered = success; |
|
|
|
|
|
|
|
return success; |
|
|
|
} |
|
|
|
} |
|
|
|
} |