using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Unity.Collections;
using Unity.Profiling;
using UnityEngine.Serialization;
using Unity.Simulation;
using UnityEngine.UI;
namespace UnityEngine.Perception.GroundTruth
{
///
/// Produces 2d bounding box annotations for all visible objects each frame.
///
[Serializable]
public sealed class BoundingBox2DLabeler : CameraLabeler
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
struct BoundingBoxValue
{
public int label_id;
public string label_name;
public uint instance_id;
public float x;
public float y;
public float width;
public float height;
}
static ProfilerMarker s_BoundingBoxCallback = new ProfilerMarker("OnBoundingBoxesReceived");
///
/// The GUID id to associate with the annotations produced by this labeler.
///
public string annotationId = "f9f22e05-443f-4602-a422-ebe4ea9b55cb";
///
/// The which associates objects with labels.
///
[FormerlySerializedAs("labelingConfiguration")]
public IdLabelConfig idLabelConfig;
Dictionary m_AsyncAnnotations;
AnnotationDefinition m_BoundingBoxAnnotationDefinition;
BoundingBoxValue[] m_BoundingBoxValues;
private GameObject visualizationHolder = null;
private Vector2 originalScreenSize = Vector2.zero;
///
/// Creates a new BoundingBox2DLabeler. Be sure to assign before adding to a .
///
public BoundingBox2DLabeler()
{
}
///
/// Creates a new BoundingBox2DLabeler with the given .
///
/// The label config for resolving the label for each object.
public BoundingBox2DLabeler(IdLabelConfig labelConfig)
{
this.idLabelConfig = labelConfig;
}
///
protected override bool supportsVisualization => true;
///
protected override void Setup()
{
if (idLabelConfig == null)
throw new InvalidOperationException("BoundingBox2DLabeler's idLabelConfig field must be assigned");
m_AsyncAnnotations = new Dictionary();
m_BoundingBoxAnnotationDefinition = DatasetCapture.RegisterAnnotationDefinition("bounding box", idLabelConfig.GetAnnotationSpecification(),
"Bounding box for each labeled object visible to the sensor", id: new Guid(annotationId));
perceptionCamera.RenderedObjectInfosCalculated += OnRenderedObjectInfosCalculated;
visualizationEnabled = supportsVisualization;
// Record the original screen size. The screen size can change during play, and the visual bounding
// boxes need to be scaled appropriately
originalScreenSize = new Vector2(Screen.width, Screen.height);
}
///
protected override void OnBeginRendering()
{
m_AsyncAnnotations[Time.frameCount] = perceptionCamera.SensorHandle.ReportAnnotationAsync(m_BoundingBoxAnnotationDefinition);
}
void OnRenderedObjectInfosCalculated(int frameCount, NativeArray renderedObjectInfos)
{
if (!m_AsyncAnnotations.TryGetValue(frameCount, out var asyncAnnotation))
return;
m_AsyncAnnotations.Remove(frameCount);
using (s_BoundingBoxCallback.Auto())
{
if (m_BoundingBoxValues == null || m_BoundingBoxValues.Length != renderedObjectInfos.Length)
m_BoundingBoxValues = new BoundingBoxValue[renderedObjectInfos.Length];
for (var i = 0; i < renderedObjectInfos.Length; i++)
{
var objectInfo = renderedObjectInfos[i];
if (!idLabelConfig.TryGetLabelEntryFromInstanceId(objectInfo.instanceId, out var labelEntry))
continue;
m_BoundingBoxValues[i] = new BoundingBoxValue
{
label_id = labelEntry.id,
label_name = labelEntry.label,
instance_id = objectInfo.instanceId,
x = objectInfo.boundingBox.x,
y = objectInfo.boundingBox.y,
width = objectInfo.boundingBox.width,
height = objectInfo.boundingBox.height,
};
}
if (!CaptureOptions.useAsyncReadbackIfSupported && frameCount != Time.frameCount)
Debug.LogWarning("Not on current frame: " + frameCount + "(" + Time.frameCount + ")");
if (visualizationEnabled)
{
Visualize();
}
asyncAnnotation.ReportValues(m_BoundingBoxValues);
}
}
///
protected override void PopulateVisualizationPanel(ControlPanel panel)
{
panel.AddToggleControl("BoundingBoxes", enabled => {
visualizationEnabled = enabled;
});
objectPool = new List();
visualizationHolder = new GameObject("BoundsHolder" + Time.frameCount);
visualizationCanvas.AddComponent(visualizationHolder);
}
void ClearObjectPool(int count)
{
for (int i = count; i < objectPool.Count; i++)
{
objectPool[i].SetActive(false);
}
}
List objectPool = null;
void Visualize()
{
ClearObjectPool(m_BoundingBoxValues.Length);
// The player screen can be dynamically resized during play, need to
// scale the bounding boxes appropriately from the original screen size
float screenRatioWidth = Screen.width / originalScreenSize.x;
float screenRatioHeight = Screen.height / originalScreenSize.y;
for (int i = 0; i < m_BoundingBoxValues.Length; i++)
{
var boxVal = m_BoundingBoxValues[i];
if (i >= objectPool.Count)
{
var boundingBoxObject = GameObject.Instantiate(Resources.Load("BoundingBoxPrefab"));
objectPool.Add(boundingBoxObject);
var rectTransform = (RectTransform)boundingBoxObject.transform;
rectTransform.SetParent(visualizationHolder.transform, false);
}
if (!objectPool[i].activeSelf) objectPool[i].SetActive(true);
string label = boxVal.label_name + "_" + boxVal.instance_id;
objectPool[i].GetComponentInChildren().text = label;
var rectTrans = objectPool[i].transform as RectTransform;
rectTrans.anchoredPosition = new Vector2(boxVal.x * screenRatioWidth, -boxVal.y * screenRatioHeight);
rectTrans.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, boxVal.width * screenRatioWidth);
rectTrans.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, boxVal.height * screenRatioHeight);
}
}
///
override protected void OnVisualizerEnabledChanged(bool enabled)
{
if (visualizationHolder != null)
visualizationHolder.SetActive(enabled);
}
}
}