浏览代码
Aisv565 bb3d - Bounding box 3D perception changes (#82)
Aisv565 bb3d - Bounding box 3D perception changes (#82)
* Bounding box 3D Working version of bounding box 3d with the ability to capture data in either kitti or nuscenes format. I'm going to remove it in the next commit. Just wanted to make sure that we have a commit for this * BB3D cleaned up files * Code commented. * Comment fixes * Everything appears to be work prior to code clean up * Everything working before commit * Cleaned up code * Updated changelog * Updated documentation * Only report 3DBBs for visible, labeled objects * Moved execution of labeler to OnBeginRenderering * Now using rendered object info for occlusion * Json serialization of boundingbox3d data/main
GitHub
4 年前
当前提交
ea6e660a
共有 14 个文件被更改,包括 925 次插入 和 22 次删除
-
12TestProjects/PerceptionURP/Assets/Scenes/SampleScene.unity
-
3TestProjects/PerceptionURP/PerceptionURP.sln.DotSettings
-
2TestProjects/PerceptionURP/ProjectSettings/QualitySettings.asset
-
1com.unity.perception/CHANGELOG.md
-
7com.unity.perception/Documentation~/PerceptionCamera.md
-
34com.unity.perception/Runtime/GroundTruth/PerceptionCamera.cs
-
64com.unity.perception/Runtime/GroundTruth/SimulationState.cs
-
48com.unity.perception/Tests/Runtime/GroundTruthTests/DatasetJsonUtilityTests.cs
-
10com.unity.perception/Tests/Runtime/GroundTruthTests/TestHelper.cs
-
296com.unity.perception/Runtime/GroundTruth/Labelers/BoundingBox3DLabeler.cs
-
3com.unity.perception/Runtime/GroundTruth/Labelers/BoundingBox3DLabeler.cs.meta
-
456com.unity.perception/Tests/Runtime/GroundTruthTests/BoundingBox3dTests.cs
-
3com.unity.perception/Tests/Runtime/GroundTruthTests/BoundingBox3dTests.cs.meta
-
8TestProjects/PerceptionURP/Assets/Scenes/SampleScene.meta
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Property/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String></wpf:ResourceDictionary> |
|||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Property/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String> |
|||
<s:Boolean x:Key="/Default/UserDictionary/Words/=labeler_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using Unity.Collections; |
|||
using Unity.Entities; |
|||
using Unity.Profiling; |
|||
|
|||
namespace UnityEngine.Perception.GroundTruth |
|||
{ |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Produces 3d bounding box ground truth for all visible and <see cref="Labeling"/> objects each frame.
|
|||
/// </summary>
|
|||
public class BoundingBox3DLabeler : CameraLabeler |
|||
{ |
|||
EntityQuery m_EntityQuery; |
|||
|
|||
///<inheritdoc/>
|
|||
public override string description |
|||
{ |
|||
get => "Produces 3D bounding box ground truth data for all visible objects that bear a label defined in this labeler's associated label configuration."; |
|||
protected set {} |
|||
} |
|||
|
|||
// ReSharper disable MemberCanBePrivate.Global
|
|||
/// <summary>
|
|||
/// The GUID id to associate with the annotations produced by this labeler.
|
|||
/// </summary>
|
|||
public string annotationId = "0bfbe00d-00fa-4555-88d1-471b58449f5c"; |
|||
/// <summary>
|
|||
/// The <see cref="IdLabelConfig"/> which associates objects with labels.
|
|||
/// </summary>
|
|||
public IdLabelConfig idLabelConfig; |
|||
// ReSharper restore MemberCanBePrivate.Global
|
|||
|
|||
/// <summary>
|
|||
/// Each 3D bounding box data record maps a tuple of (instance, label) to translation, size and rotation that draws a 3D bounding box,
|
|||
/// as well as velocity and acceleration (optional) of the 3D bounding box. All location data is given with respect to the sensor coordinate system.
|
|||
///
|
|||
/// bounding_box_3d
|
|||
/// label_id (int): Integer identifier of the label
|
|||
/// label_name (str): String identifier of the label
|
|||
/// instance_id (str): UUID of the instance.
|
|||
/// translation (float, float, float): 3d bounding box's center location in meters as center_x, center_y, center_z with respect to global coordinate system.
|
|||
/// size (float, float, float): 3d bounding box size in meters as width, length, height.
|
|||
/// rotation (float, float, float, float): 3d bounding box orientation as quaternion: w, x, y, z.
|
|||
/// velocity (float, float, float) [optional]: 3d bounding box velocity in meters per second as v_x, v_y, v_z.
|
|||
/// acceleration (float, float, float) [optional]: 3d bounding box acceleration in meters per second^2 as a_x, a_y, a_z.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Currently not supporting exporting velocity and acceleration. Both values will be null.
|
|||
/// </remarks>
|
|||
[SuppressMessage("ReSharper", "InconsistentNaming")] |
|||
[Serializable] |
|||
public struct BoxData |
|||
{ |
|||
public int label_id; |
|||
public string label_name; |
|||
public uint instance_id; |
|||
public Vector3 translation; |
|||
public Vector3 size; |
|||
public Quaternion rotation; |
|||
public Vector3 velocity; |
|||
public Vector3 acceleration; |
|||
} |
|||
|
|||
static ProfilerMarker s_BoundingBoxCallback = new ProfilerMarker("OnBoundingBoxes3DReceived"); |
|||
AnnotationDefinition m_AnnotationDefinition; |
|||
|
|||
Dictionary<int, AsyncAnnotation> m_AsyncAnnotations; |
|||
Dictionary<int, Dictionary<uint, BoxData>> m_BoundingBoxValues; |
|||
List<BoxData> m_ToReport; |
|||
|
|||
int m_CurrentFrame; |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override bool supportsVisualization => false; |
|||
|
|||
/// <summary>
|
|||
/// Fired when the bounding boxes are computed for a frame.
|
|||
/// </summary>
|
|||
public event Action<int, List<BoxData>> BoundingBoxComputed; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new BoundingBox3DLabeler. Be sure to assign <see cref="idLabelConfig"/> before adding to a <see cref="PerceptionCamera"/>.
|
|||
/// </summary>
|
|||
public BoundingBox3DLabeler() {} |
|||
|
|||
/// <summary>
|
|||
/// Creates a new BoundingBox3DLabeler with the given <see cref="IdLabelConfig"/>.
|
|||
/// </summary>
|
|||
/// <param name="labelConfig">The label config for resolving the label for each object.</param>
|
|||
public BoundingBox3DLabeler(IdLabelConfig labelConfig) |
|||
{ |
|||
this.idLabelConfig = labelConfig; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Setup() |
|||
{ |
|||
if (idLabelConfig == null) |
|||
throw new InvalidOperationException("BoundingBox2DLabeler's idLabelConfig field must be assigned"); |
|||
|
|||
m_AnnotationDefinition = DatasetCapture.RegisterAnnotationDefinition("bounding box 3D", idLabelConfig.GetAnnotationSpecification(), |
|||
"Bounding box for each labeled object visible to the sensor", id: new Guid(annotationId)); |
|||
|
|||
perceptionCamera.RenderedObjectInfosCalculated += OnRenderObjectInfosCalculated; |
|||
|
|||
m_EntityQuery = World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntityQuery(typeof(Labeling), typeof(GroundTruthInfo)); |
|||
|
|||
m_AsyncAnnotations = new Dictionary<int, AsyncAnnotation>(); |
|||
m_BoundingBoxValues = new Dictionary<int, Dictionary<uint, BoxData>>(); |
|||
m_ToReport = new List<BoxData>(); |
|||
} |
|||
|
|||
static BoxData ConvertToBoxData(IdLabelEntry label, uint instanceId, Vector3 center, Vector3 extents, Quaternion rot) |
|||
{ |
|||
return new BoxData |
|||
{ |
|||
label_id = label.id, |
|||
label_name = label.label, |
|||
instance_id = instanceId, |
|||
translation = center, |
|||
size = extents * 2, |
|||
rotation = rot, |
|||
acceleration = Vector3.zero, |
|||
velocity = Vector3.zero |
|||
}; |
|||
} |
|||
|
|||
static Vector3[] GetBoxCorners(Bounds bounds, Quaternion rotation) |
|||
{ |
|||
var boundsCenter = bounds.center; |
|||
var right = Vector3.right * bounds.extents.x; |
|||
var up = Vector3.up * bounds.extents.y; |
|||
var forward = Vector3.forward * bounds.extents.z; |
|||
|
|||
right = rotation * right; |
|||
up = rotation * up; |
|||
forward = rotation * forward; |
|||
|
|||
var doubleRight = right * 2; |
|||
var doubleUp = up * 2; |
|||
var doubleForward = forward * 2; |
|||
|
|||
var corners = new Vector3[8]; |
|||
corners[0] = boundsCenter - right - up - forward; |
|||
corners[1] = corners[0] + doubleUp; |
|||
corners[2] = corners[1] + doubleRight; |
|||
corners[3] = corners[0] + doubleRight; |
|||
for (var i = 0; i < 4; i++) |
|||
{ |
|||
corners[i + 4] = corners[i] + doubleForward; |
|||
} |
|||
|
|||
return corners; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnBeginRendering() |
|||
{ |
|||
m_CurrentFrame = Time.frameCount; |
|||
|
|||
m_BoundingBoxValues[m_CurrentFrame] = new Dictionary<uint, BoxData>(); |
|||
|
|||
m_AsyncAnnotations[m_CurrentFrame] = perceptionCamera.SensorHandle.ReportAnnotationAsync(m_AnnotationDefinition); |
|||
|
|||
var entities = m_EntityQuery.ToEntityArray(Allocator.TempJob); |
|||
var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager; |
|||
|
|||
foreach (var entity in entities) |
|||
{ |
|||
ProcessEntity(entityManager.GetComponentObject<Labeling>(entity)); |
|||
} |
|||
|
|||
entities.Dispose(); |
|||
} |
|||
|
|||
void OnRenderObjectInfosCalculated(int frameCount, NativeArray<RenderedObjectInfo> renderedObjectInfos) |
|||
{ |
|||
if (!m_AsyncAnnotations.TryGetValue(frameCount, out var asyncAnnotation)) |
|||
return; |
|||
|
|||
if (!m_BoundingBoxValues.TryGetValue(frameCount, out var boxes)) |
|||
return; |
|||
|
|||
m_AsyncAnnotations.Remove(frameCount); |
|||
m_BoundingBoxValues.Remove(frameCount); |
|||
|
|||
using (s_BoundingBoxCallback.Auto()) |
|||
{ |
|||
m_ToReport.Clear(); |
|||
|
|||
for (var i = 0; i < renderedObjectInfos.Length; i++) |
|||
{ |
|||
var objectInfo = renderedObjectInfos[i]; |
|||
|
|||
if (boxes.TryGetValue(objectInfo.instanceId, out var box)) |
|||
{ |
|||
m_ToReport.Add(box); |
|||
} |
|||
} |
|||
|
|||
BoundingBoxComputed?.Invoke(frameCount, m_ToReport); |
|||
asyncAnnotation.ReportValues(m_ToReport); |
|||
} |
|||
} |
|||
|
|||
void ProcessEntity(Labeling labeledEntity) |
|||
{ |
|||
using (s_BoundingBoxCallback.Auto()) |
|||
{ |
|||
// Unfortunately to get the non-axis aligned bounding prism from a game object is not very
|
|||
// straightforward. A game object's default bounding prism is always axis aligned. To find a "tight"
|
|||
// fitting prism for a game object we must calculate the oriented bounds of all of the meshes in a
|
|||
// game object. These meshes (in the object tree) may go through a series of transformations. We need
|
|||
// to transform all of the children mesh bounds into the coordinate space of the "labeled" game object
|
|||
// and then intersect all of those bounds together. We then need to apply the "labeled" game object's
|
|||
// transform to the combined bounds to transform the bounds into world space. Finally, we then need
|
|||
// to take the bounds in world space and transform it to camera space to record it to json...
|
|||
//
|
|||
// Currently we are only reporting objects that are a) labeled and b) are visible based on the perception
|
|||
// camera's rendered object info. In the future we plan on reporting how much of the object can be seen, including
|
|||
// none if it is off camera
|
|||
if (idLabelConfig.TryGetLabelEntryFromInstanceId(labeledEntity.instanceId, out var labelEntry)) |
|||
{ |
|||
var entityGameObject = labeledEntity.gameObject; |
|||
|
|||
var meshFilters = entityGameObject.GetComponentsInChildren<MeshFilter>(); |
|||
if (meshFilters == null || meshFilters.Length == 0) return; |
|||
|
|||
var labelTransform = entityGameObject.transform; |
|||
var cameraTransform = perceptionCamera.transform; |
|||
var combinedBounds = new Bounds(Vector3.zero, Vector3.zero); |
|||
var areBoundsUnset = true; |
|||
|
|||
// Need to convert all bounds into labeling mesh space...
|
|||
foreach (var mesh in meshFilters) |
|||
{ |
|||
var currentTransform = mesh.gameObject.transform; |
|||
// Grab the bounds of the game object from the mesh, although these bounds are axis-aligned,
|
|||
// they are axis-aligned with respect to the current component's coordinate space. This, in theory
|
|||
// could still provide non-ideal fitting bounds (if the model is made strangely, but garbage in; garbage out)
|
|||
var meshBounds = mesh.mesh.bounds; |
|||
|
|||
var transformedBounds = new Bounds(meshBounds.center, meshBounds.size); |
|||
var transformedRotation = Quaternion.identity; |
|||
|
|||
// Apply the transformations on this object until we reach the labeled transform
|
|||
while (currentTransform != labelTransform) |
|||
{ |
|||
transformedBounds.center += currentTransform.localPosition; |
|||
transformedBounds.extents = Vector3.Scale(transformedBounds.extents, currentTransform.localScale); |
|||
transformedRotation *= currentTransform.localRotation; |
|||
currentTransform = currentTransform.parent; |
|||
} |
|||
|
|||
// Due to rotations that may be applied, we cannot simply use the extents of the bounds, but
|
|||
// need to calculate all 8 corners of the bounds and combine them with the current combined
|
|||
// bounds
|
|||
var corners = GetBoxCorners(transformedBounds, transformedRotation); |
|||
|
|||
// If this is the first time, create a new bounds struct
|
|||
if (areBoundsUnset) |
|||
{ |
|||
combinedBounds = new Bounds(corners[0], Vector3.zero); |
|||
areBoundsUnset = false; |
|||
} |
|||
|
|||
// Go through each corner add add it to the bounds
|
|||
foreach (var c2 in corners) |
|||
{ |
|||
combinedBounds.Encapsulate(c2); |
|||
} |
|||
} |
|||
|
|||
// Convert the combined bounds into world space
|
|||
combinedBounds.center = labelTransform.TransformPoint(combinedBounds.center); |
|||
combinedBounds.extents = Vector3.Scale(combinedBounds.extents, labelTransform.localScale); |
|||
|
|||
// Now convert all points into camera's space
|
|||
var cameraCenter = cameraTransform.InverseTransformPoint(combinedBounds.center); |
|||
cameraCenter = Vector3.Scale(cameraTransform.localScale, cameraCenter); |
|||
|
|||
// Rotation to go from label space to camera space
|
|||
var cameraRotation = Quaternion.Inverse(cameraTransform.rotation) * labelTransform.rotation; |
|||
|
|||
var converted = ConvertToBoxData(labelEntry, labeledEntity.instanceId, cameraCenter, combinedBounds.extents, cameraRotation); |
|||
|
|||
m_BoundingBoxValues[m_CurrentFrame][labeledEntity.instanceId] = converted; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: da97f6ffee604bdea17edaf29854ff3a |
|||
timeCreated: 1600266084 |
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using NUnit.Framework; |
|||
using Unity.Mathematics; |
|||
using UnityEngine; |
|||
using UnityEngine.Perception.GroundTruth; |
|||
using UnityEngine.TestTools; |
|||
|
|||
namespace GroundTruthTests |
|||
{ |
|||
[TestFixture] |
|||
public class BoundingBox3dTests : GroundTruthTestBase |
|||
{ |
|||
const float k_Delta = 0.0001f; |
|||
|
|||
static string PrintBox(BoundingBox3DLabeler.BoxData box) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
sb.Append("label id: " + box.label_id + " "); |
|||
sb.Append("label_name: " + box.label_name + " "); |
|||
sb.Append("instance_id: " + box.instance_id + " "); |
|||
sb.Append("translation: (" + box.translation[0] + ", " + box.translation[1] + ", " + box.translation[2] + ") "); |
|||
sb.Append("size: (" + box.size[0] + ", " + box.size[1] + ", " + box.size[2] + ") "); |
|||
sb.Append("rotation: " + box.rotation[0] + ", " + box.rotation[1] + ", " + box.rotation[2] + ", " + box.rotation[3] + ") "); |
|||
sb.Append("velocity: " + box.velocity[0] + ", " + box.velocity[1] + ", " + box.velocity[2]); |
|||
sb.Append("acceleration: (" + box.acceleration[0] + ", " + box.acceleration[1] + ", " + box.acceleration[2] + ")"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator CameraOffset_ProduceProperTranslationTest() |
|||
{ |
|||
var expected = new[] |
|||
{ |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 1, |
|||
labelId = 1, |
|||
labelName = "label", |
|||
position = new Vector3(0, 0, 10), |
|||
scale = new Vector3(5, 5, 5), |
|||
rotation = Quaternion.identity |
|||
} |
|||
}; |
|||
var target = TestHelper.CreateLabeledCube(); |
|||
var cameraPosition = new Vector3(0, 0, -10); |
|||
var cameraRotation = Quaternion.identity; |
|||
return ExecuteTest(target, cameraPosition, cameraRotation, expected); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator CameraOffsetAndRotated_ProduceProperTranslationTest() |
|||
{ |
|||
var expected = new[] |
|||
{ |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 1, |
|||
labelId = 1, |
|||
labelName = "label", |
|||
position = new Vector3(0, 0, Mathf.Sqrt(200)), |
|||
scale = new Vector3(5, 5, 5), |
|||
rotation = Quaternion.identity |
|||
} |
|||
}; |
|||
var target = TestHelper.CreateLabeledCube(x: 10, y: 0, z: 10, yaw: 45); |
|||
var cameraPosition = new Vector3(0, 0, 0); |
|||
var cameraRotation = Quaternion.Euler(0, 45, 0); |
|||
return ExecuteTest(target, cameraPosition, cameraRotation, expected); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator SimpleMultiMesh_ProduceProperTranslationTest() |
|||
{ |
|||
var expected = new[] |
|||
{ |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 1, |
|||
labelId = 1, |
|||
labelName = "label", |
|||
position = new Vector3(0, 0, 10), |
|||
scale = new Vector3(6.5f, 2.5f, 2.5f), |
|||
rotation = Quaternion.identity |
|||
} |
|||
}; |
|||
var target = CreateMultiMeshGameObject(); |
|||
target.transform.position = Vector3.zero; |
|||
var cameraPosition = new Vector3(0, 0, -10); |
|||
var cameraRotation = Quaternion.identity; |
|||
return ExecuteTest(target, cameraPosition, cameraRotation, expected); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator MultiInheritedMesh_ProduceProperTranslationTest() |
|||
{ |
|||
var expected = new[] |
|||
{ |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 1, |
|||
labelId = 2, |
|||
labelName = "car", |
|||
position = new Vector3(0, 0.525f, 20), |
|||
scale = new Vector3(2f, 0.875f, 2.4f), |
|||
rotation = Quaternion.identity |
|||
}, |
|||
}; |
|||
|
|||
var target = CreateTestReallyBadCar(new Vector3(0, 0.35f, 20), Quaternion.identity); |
|||
target.transform.localPosition = new Vector3(0, 0, 20); |
|||
var cameraPosition = new Vector3(0, 0, 0); |
|||
var cameraRotation = Quaternion.identity; |
|||
return ExecuteTest(target, cameraPosition, cameraRotation, expected); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator MultiInheritedMeshDifferentLabels_ProduceProperTranslationTest() |
|||
{ |
|||
var wheelScale = new Vector3(0.35f, 1.0f, 0.35f); |
|||
var wheelRot = Quaternion.Euler(0, 0, 90); |
|||
|
|||
var expected = new[] |
|||
{ |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 1, |
|||
labelId = 2, |
|||
labelName = "car", |
|||
position = new Vector3(0, 1.05f, 20), |
|||
scale = new Vector3(1f, 0.7f, 2.4f), |
|||
rotation = Quaternion.identity |
|||
}, |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 2, |
|||
labelId = 3, |
|||
labelName = "wheel", |
|||
position = new Vector3(1, 0.35f, 18.6f), |
|||
scale = wheelScale, |
|||
rotation = wheelRot |
|||
}, |
|||
new ExpectedResult |
|||
{ |
|||
instanceId = 4, |
|||
labelId = 3, |
|||
labelName = "wheel", |
|||
position = new Vector3(-1, 0.35f, 18.6f), |
|||
scale = wheelScale, |
|||
rotation = wheelRot |
|||
} |
|||
}; |
|||
|
|||
var target = CreateTestReallyBadCar(new Vector3(0, 0.35f, 20), Quaternion.identity, false); |
|||
var cameraPosition = new Vector3(0, 0, 0); |
|||
var cameraRotation = Quaternion.identity; |
|||
return ExecuteTest(target, cameraPosition, cameraRotation, expected); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator TestOcclusion_Seen() |
|||
{ |
|||
var target = TestHelper.CreateLabeledCube(scale: 15f, z: 50f); |
|||
return ExecuteSeenUnseenTest(target, Vector3.zero, quaternion.identity, 1); |
|||
} |
|||
|
|||
[UnityTest] |
|||
public IEnumerator TestOcclusion_Unseen() |
|||
{ |
|||
var target = TestHelper.CreateLabeledCube(scale: 15f, z: -50f); |
|||
return ExecuteSeenUnseenTest(target, Vector3.zero, quaternion.identity, 0); |
|||
} |
|||
|
|||
|
|||
struct ExpectedResult |
|||
{ |
|||
public int labelId; |
|||
public string labelName; |
|||
public int instanceId; |
|||
public Vector3 position; |
|||
public Vector3 scale; |
|||
public Quaternion rotation; |
|||
} |
|||
|
|||
IEnumerator ExecuteSeenUnseenTest(GameObject target, Vector3 cameraPos, Quaternion cameraRotation, int expectedSeen) |
|||
{ |
|||
var receivedResults = new List<(int, List<BoundingBox3DLabeler.BoxData>)>(); |
|||
var gameObject = SetupCamera(SetupLabelConfig(), (frame, data) => |
|||
{ |
|||
receivedResults.Add((frame, data)); |
|||
}); |
|||
|
|||
gameObject.transform.position = cameraPos; |
|||
gameObject.transform.rotation = cameraRotation; |
|||
|
|||
AddTestObjectForCleanup(gameObject); |
|||
|
|||
gameObject.SetActive(false); |
|||
receivedResults.Clear(); |
|||
gameObject.SetActive(true); |
|||
|
|||
yield return null; |
|||
yield return null; |
|||
|
|||
Assert.AreEqual(expectedSeen, receivedResults[0].Item2.Count); |
|||
|
|||
DestroyTestObject(gameObject); |
|||
UnityEngine.Object.DestroyImmediate(target); |
|||
} |
|||
|
|||
IEnumerator ExecuteTest(GameObject target, Vector3 cameraPos, Quaternion cameraRotation, IList<ExpectedResult> expectations) |
|||
{ |
|||
var receivedResults = new List<(int, List<BoundingBox3DLabeler.BoxData>)>(); |
|||
var gameObject = SetupCamera(SetupLabelConfig(), (frame, data) => |
|||
{ |
|||
receivedResults.Add((frame, data)); |
|||
}); |
|||
|
|||
gameObject.transform.position = cameraPos; |
|||
gameObject.transform.rotation = cameraRotation; |
|||
|
|||
AddTestObjectForCleanup(gameObject); |
|||
|
|||
gameObject.SetActive(false); |
|||
receivedResults.Clear(); |
|||
gameObject.SetActive(true); |
|||
|
|||
yield return null; |
|||
yield return null; |
|||
|
|||
Assert.AreEqual(expectations.Count, receivedResults[0].Item2.Count); |
|||
|
|||
for (var i = 0; i < receivedResults[0].Item2.Count; i++) |
|||
{ |
|||
var b = receivedResults[0].Item2[i]; |
|||
|
|||
Assert.AreEqual(expectations[i].labelId, b.label_id); |
|||
Assert.AreEqual(expectations[i].labelName, b.label_name); |
|||
Assert.AreEqual(expectations[i].instanceId, b.instance_id); |
|||
TestResults(b, expectations[i]); |
|||
} |
|||
|
|||
DestroyTestObject(gameObject); |
|||
UnityEngine.Object.DestroyImmediate(target); |
|||
} |
|||
|
|||
static IdLabelConfig SetupLabelConfig() |
|||
{ |
|||
var labelConfig = ScriptableObject.CreateInstance<IdLabelConfig>(); |
|||
labelConfig.Init(new List<IdLabelEntry>() |
|||
{ |
|||
new IdLabelEntry |
|||
{ |
|||
id = 1, |
|||
label = "label" |
|||
}, |
|||
new IdLabelEntry |
|||
{ |
|||
|
|||
id = 2, |
|||
label= "car" |
|||
}, |
|||
new IdLabelEntry |
|||
{ |
|||
id = 3, |
|||
label= "wheel" |
|||
}, |
|||
}); |
|||
|
|||
return labelConfig; |
|||
} |
|||
|
|||
static GameObject SetupCamera(IdLabelConfig config, Action<int, List<BoundingBox3DLabeler.BoxData>> computeListener) |
|||
{ |
|||
var cameraObject = new GameObject(); |
|||
cameraObject.SetActive(false); |
|||
var camera = cameraObject.AddComponent<Camera>(); |
|||
camera.orthographic = false; |
|||
camera.fieldOfView = 60; |
|||
camera.nearClipPlane = 0.3f; |
|||
camera.farClipPlane = 1000; |
|||
|
|||
var perceptionCamera = cameraObject.AddComponent<PerceptionCamera>(); |
|||
perceptionCamera.captureRgbImages = false; |
|||
var bboxLabeler = new BoundingBox3DLabeler(config); |
|||
if (computeListener != null) |
|||
bboxLabeler.BoundingBoxComputed += computeListener; |
|||
|
|||
perceptionCamera.AddLabeler(bboxLabeler); |
|||
|
|||
return cameraObject; |
|||
} |
|||
|
|||
static void TestResults(BoundingBox3DLabeler.BoxData data, ExpectedResult e) |
|||
{ |
|||
var scale = e.scale * 2; |
|||
|
|||
Assert.IsNotNull(data); |
|||
Assert.AreEqual(e.position[0], data.translation[0], k_Delta); |
|||
Assert.AreEqual(e.position[1], data.translation[1], k_Delta); |
|||
Assert.AreEqual(e.position[2], data.translation[2], k_Delta); |
|||
Assert.AreEqual(scale[0], data.size[0], k_Delta); |
|||
Assert.AreEqual(scale[1], data.size[1], k_Delta); |
|||
Assert.AreEqual(scale[2], data.size[2], k_Delta); |
|||
Assert.AreEqual(e.rotation[0], data.rotation[0], k_Delta); |
|||
Assert.AreEqual(e.rotation[1], data.rotation[1], k_Delta); |
|||
Assert.AreEqual(e.rotation[2], data.rotation[2], k_Delta); |
|||
Assert.AreEqual(e.rotation[3], data.rotation[3], k_Delta); |
|||
Assert.AreEqual(Vector3.zero, data.velocity); |
|||
Assert.AreEqual(Vector3.zero, data.acceleration); |
|||
} |
|||
|
|||
static GameObject CreateTestReallyBadCar(Vector3 position, Quaternion rotation, bool underOneLabel = true) |
|||
{ |
|||
var badCar = new GameObject("BadCar"); |
|||
badCar.transform.position = position; |
|||
badCar.transform.rotation = rotation; |
|||
if (underOneLabel) |
|||
{ |
|||
var labeling = badCar.AddComponent<Labeling>(); |
|||
labeling.labels.Add("car"); |
|||
} |
|||
|
|||
var body = GameObject.CreatePrimitive(PrimitiveType.Cube); |
|||
body.name = "body"; |
|||
body.transform.parent = badCar.transform; |
|||
body.transform.localPosition = new Vector3(0, 0.7f, 0); |
|||
body.transform.localScale = new Vector3(2f, 1.4f, 4.8f); |
|||
if (!underOneLabel) |
|||
{ |
|||
var labeling = body.AddComponent<Labeling>(); |
|||
labeling.labels.Add("car"); |
|||
} |
|||
|
|||
var wheel = GameObject.CreatePrimitive(PrimitiveType.Cylinder); |
|||
wheel.name = "wheel1"; |
|||
wheel.transform.parent = badCar.transform; |
|||
wheel.transform.localPosition = new Vector3(1f, 0, -1.4f); |
|||
wheel.transform.localRotation = Quaternion.Euler(0, 0, 90); |
|||
wheel.transform.localScale = new Vector3(0.7f, 1, 0.7f); |
|||
if (!underOneLabel) |
|||
{ |
|||
var labeling = wheel.AddComponent<Labeling>(); |
|||
labeling.labels.Add("wheel"); |
|||
} |
|||
|
|||
wheel = GameObject.CreatePrimitive(PrimitiveType.Cylinder); |
|||
wheel.name = "wheel2"; |
|||
wheel.transform.parent = badCar.transform; |
|||
wheel.transform.localPosition = new Vector3(1f, 0, 1.45f); |
|||
wheel.transform.localRotation = Quaternion.Euler(0, 0, 90); |
|||
wheel.transform.localScale = new Vector3(0.7f, 1, 0.7f); |
|||
if (!underOneLabel) |
|||
{ |
|||
var labeling = wheel.AddComponent<Labeling>(); |
|||
labeling.labels.Add("wheel"); |
|||
} |
|||
|
|||
wheel = GameObject.CreatePrimitive(PrimitiveType.Cylinder); |
|||
wheel.name = "wheel3"; |
|||
wheel.transform.parent = badCar.transform; |
|||
wheel.transform.localPosition = new Vector3(-1f, 0, -1.4f); |
|||
wheel.transform.localRotation = Quaternion.Euler(0, 0, 90); |
|||
wheel.transform.localScale = new Vector3(0.7f, 1, 0.7f); |
|||
if (!underOneLabel) |
|||
{ |
|||
var labeling = wheel.AddComponent<Labeling>(); |
|||
labeling.labels.Add("wheel"); |
|||
} |
|||
|
|||
wheel = GameObject.CreatePrimitive(PrimitiveType.Cylinder); |
|||
wheel.name = "wheel4"; |
|||
wheel.transform.parent = badCar.transform; |
|||
wheel.transform.localPosition = new Vector3(-1f, 0, 1.45f); |
|||
wheel.transform.localRotation = Quaternion.Euler(0, 0, 90); |
|||
wheel.transform.localScale = new Vector3(0.7f, 1, 0.7f); |
|||
if (!underOneLabel) |
|||
{ |
|||
var labeling = wheel.AddComponent<Labeling>(); |
|||
labeling.labels.Add("wheel"); |
|||
} |
|||
|
|||
return badCar; |
|||
} |
|||
|
|||
static GameObject CreateMultiMeshGameObject() |
|||
{ |
|||
var go = new GameObject(); |
|||
var labeling = go.AddComponent<Labeling>(); |
|||
labeling.labels.Add("label"); |
|||
|
|||
var left = GameObject.CreatePrimitive(PrimitiveType.Cube); |
|||
left.transform.parent = go.transform; |
|||
left.transform.localPosition = new Vector3(-4, 0, 0); |
|||
left.transform.localScale = new Vector3(5, 5, 5); |
|||
|
|||
var right = GameObject.CreatePrimitive(PrimitiveType.Cube); |
|||
right.transform.parent = go.transform; |
|||
right.transform.localPosition = new Vector3(4, 0, 0); |
|||
right.transform.localScale = new Vector3(5, 5, 5); |
|||
|
|||
var center = GameObject.CreatePrimitive(PrimitiveType.Cylinder); |
|||
center.transform.parent = go.transform; |
|||
center.transform.localPosition = Vector3.zero; |
|||
center.transform.localRotation = Quaternion.Euler(0, 0, 90); |
|||
center.transform.localScale = new Vector3(1, 3, 1); |
|||
|
|||
return go; |
|||
} |
|||
|
|||
class CubeSpinner : MonoBehaviour |
|||
{ |
|||
public Quaternion rotationPerFrame = Quaternion.Euler(0, 15, 0); |
|||
|
|||
void Update() |
|||
{ |
|||
transform.localRotation *= rotationPerFrame; |
|||
} |
|||
} |
|||
|
|||
class CubeMover : MonoBehaviour |
|||
{ |
|||
public Vector3 distancePerFrame = new Vector3(5, 0 , 0); |
|||
|
|||
void Update() |
|||
{ |
|||
transform.localPosition += distancePerFrame; |
|||
} |
|||
} |
|||
|
|||
static GameObject CreateDynamicBox(bool spinning = false, bool moving = false, Vector3? translation = null, Quaternion? rotation = null) |
|||
{ |
|||
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); |
|||
cube.name = "Cube"; |
|||
var labeling = cube.AddComponent<Labeling>(); |
|||
labeling.labels.Add("label"); |
|||
cube.transform.position = new Vector3(0f, 0f, 10f); |
|||
cube.transform.localScale = new Vector3(30f, 30f, 30f); |
|||
if (spinning) |
|||
{ |
|||
var spin = cube.AddComponent<CubeSpinner>(); |
|||
spin.rotationPerFrame = rotation ?? spin.rotationPerFrame; |
|||
} |
|||
if (moving) |
|||
{ |
|||
var move = cube.AddComponent<CubeMover>(); |
|||
move.distancePerFrame = translation ?? move.distancePerFrame; |
|||
} |
|||
return cube; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 783926dac50d43889ccc914436bdf544 |
|||
timeCreated: 1600266800 |
|
|||
fileFormatVersion: 2 |
|||
guid: c63d11a708fe3f047a545d7e457f6e81 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue