Wes xx mediapipe integration

This commit is contained in:
Jelle De Geest
2023-03-12 20:34:16 +00:00
parent 8349b5f149
commit b11eeb465c
975 changed files with 192230 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
namespace Mediapipe.Unity
{
/// <summary>
/// This class draws annotations on the screen which is the parent of the attached <see cref="GameObject" />.<br />
/// That is, it's used like this.<br />
/// 1. Select a GameObject where you'd like to draw annotations.<br />
/// 2. Create an empty child GameObject (let's say AnnotationLayer) directly under it.<br />
/// 3. Attach <see cref="AnnotationController{T}" /> to AnnotationLayer.<br />
/// 4. Create an empty child GameObject (let's say RootAnnotation) directly under AnnotationLayer.<br />
/// 5. Attach <see cref="HierarchicalAnnotation" /> to RootAnnotation.<br />
/// </summary>
/// <remarks>
/// Note that this class can be accessed from a thread other than main thread.
/// Extended classes must be implemented to work in such a situation, since Unity APIs won't work in other threads.
/// </remarks>
public abstract class AnnotationController<T> : MonoBehaviour where T : HierarchicalAnnotation
{
[SerializeField] protected T annotation;
protected bool isStale = false;
public bool isMirrored
{
get => annotation.isMirrored;
set
{
if (annotation.isMirrored != value)
{
annotation.isMirrored = value;
}
}
}
public RotationAngle rotationAngle
{
get => annotation.rotationAngle;
set
{
if (annotation.rotationAngle != value)
{
annotation.rotationAngle = value;
}
}
}
protected virtual void Start()
{
if (!TryGetComponent<RectTransform>(out var _))
{
Logger.LogVerbose(GetType().Name, $"Adding RectTransform to {gameObject.name}");
var rectTransform = gameObject.AddComponent<RectTransform>();
// stretch width and height by default
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.anchoredPosition3D = Vector3.zero;
rectTransform.sizeDelta = Vector2.zero;
}
}
protected virtual void LateUpdate()
{
if (isStale)
{
SyncNow();
}
}
protected virtual void OnDestroy()
{
if (annotation != null)
{
Destroy(annotation);
annotation = null;
}
isStale = false;
}
/// <summary>
/// Draw annotations in current thread.
/// This method must set <see cref="isStale" /> to false.
/// </summary>
/// <remarks>
/// This method can only be called from main thread.
/// </remarks>
protected abstract void SyncNow();
protected void UpdateCurrentTarget<TValue>(TValue newTarget, ref TValue currentTarget)
{
if (IsTargetChanged(newTarget, currentTarget))
{
currentTarget = newTarget;
isStale = true;
}
}
protected bool IsTargetChanged<TValue>(TValue newTarget, TValue currentTarget)
{
// It's assumed that target has not changed iff previous target and new target are both null.
return currentTarget != null || newTarget != null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f1980da4d69e05869d4a1cdac8f4ced
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,153 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class Arrow : MonoBehaviour
{
[SerializeField] private Color _color = Color.white;
[SerializeField] private Vector3 _direction = Vector3.right;
[SerializeField] private float _magnitude = 0.0f;
[SerializeField] private float _capScale = 1.0f;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
private void Start()
{
ApplyColor(color);
ApplyDirection(_direction);
ApplyCapScale(_capScale);
ApplyLineWidth(_lineWidth);
ApplyMagnitude(_magnitude); // magnitude must be set after _capScale
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyDirection(_direction);
ApplyCapScale(_capScale);
ApplyLineWidth(_lineWidth);
ApplyMagnitude(_magnitude); // magnitude must be set after _capScale
}
}
#endif
private Transform _cone;
private Transform cone
{
get
{
if (_cone == null)
{
_cone = transform.Find("Cone");
}
return _cone;
}
}
private LineRenderer lineRenderer => gameObject.GetComponent<LineRenderer>();
public Vector3 direction
{
get => _direction;
set
{
_direction = value.normalized;
ApplyDirection(_direction);
}
}
public float magnitude
{
get => _magnitude;
set
{
if (value < 0)
{
throw new ArgumentException("Magnitude must be positive");
}
_magnitude = value;
ApplyMagnitude(value);
}
}
public Color color
{
get => _color;
set
{
_color = value;
ApplyColor(value);
}
}
public void SetVector(Vector3 v)
{
direction = v;
magnitude = v.magnitude;
}
public void SetCapScale(float capScale)
{
_capScale = capScale;
ApplyCapScale(_capScale);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(_lineWidth);
}
private void ApplyColor(Color color)
{
lineRenderer.startColor = color;
lineRenderer.endColor = color;
cone.GetComponent<Renderer>().material.color = color;
}
private void ApplyDirection(Vector3 direction)
{
lineRenderer.SetPosition(1, _magnitude * direction);
cone.localRotation = Quaternion.FromToRotation(Vector3.up, direction);
}
private void ApplyMagnitude(float magnitude)
{
lineRenderer.SetPosition(1, magnitude * direction);
if (magnitude == 0)
{
cone.localScale = Vector3.zero;
cone.localPosition = Vector3.zero;
}
else
{
ApplyCapScale(_capScale);
cone.localPosition = (cone.localScale.y + magnitude) * direction; // pivot is at the center of cone
}
}
private void ApplyCapScale(float capScale)
{
cone.localScale = capScale * Vector3.one;
}
private void ApplyLineWidth(float lineWidth)
{
lineRenderer.startWidth = lineWidth;
lineRenderer.endWidth = lineWidth;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a793788ee98ea8ea3b28457e7dd7197d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class CircleAnnotation : HierarchicalAnnotation
{
[SerializeField] private LineRenderer _lineRenderer;
[SerializeField] private Color _color = Color.green;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
private void OnEnable()
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
private void OnDisable()
{
ApplyLineWidth(0.0f);
_lineRenderer.positionCount = 0;
_lineRenderer.SetPositions(new Vector3[] { });
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
}
#endif
public void SetColor(Color color)
{
_color = color;
ApplyColor(color);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(lineWidth);
}
public void Draw(Vector3 center, float radius, int vertices = 128)
{
var start = new Vector3(radius, 0, 0);
var positions = new Vector3[vertices];
for (var i = 0; i < positions.Length; i++)
{
var q = Quaternion.Euler(0, 0, i * 360 / positions.Length);
positions[i] = (q * start) + center;
}
_lineRenderer.positionCount = positions.Length;
_lineRenderer.SetPositions(positions);
}
private void ApplyColor(Color color)
{
if (_lineRenderer != null)
{
_lineRenderer.startColor = color;
_lineRenderer.endColor = color;
}
}
private void ApplyLineWidth(float lineWidth)
{
if (_lineRenderer != null)
{
_lineRenderer.startWidth = lineWidth;
_lineRenderer.endWidth = lineWidth;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98e622123dd1b9ac1a87d8609ce21d37
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
namespace Mediapipe.Unity
{
public class Connection
{
public readonly HierarchicalAnnotation start;
public readonly HierarchicalAnnotation end;
public Connection(HierarchicalAnnotation start, HierarchicalAnnotation end)
{
this.start = start;
this.end = end;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e71b609a71e6232a8043e02da04b2bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
namespace Mediapipe.Unity
{
public class ConnectionAnnotation : LineAnnotation
{
private Connection _currentTarget;
public bool isEmpty => _currentTarget == null;
public void Draw(Connection target)
{
_currentTarget = target;
if (ActivateFor(_currentTarget))
{
Draw(_currentTarget.start.gameObject, _currentTarget.end.gameObject);
}
}
public void Redraw()
{
Draw(_currentTarget);
}
protected bool ActivateFor(Connection target)
{
if (target == null || !target.start.isActiveInHierarchy || !target.end.isActiveInHierarchy)
{
SetActive(false);
return false;
}
SetActive(true);
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90aa7330426be071f9141dd20224e6cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class ConnectionListAnnotation : ListAnnotation<ConnectionAnnotation>
{
[SerializeField] private Color _color = Color.red;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
}
#endif
public void Fill(IList<(int, int)> connections, PointListAnnotation points)
{
Draw(connections.Select(pair => new Connection(points[pair.Item1], points[pair.Item2])).ToList());
}
public void SetColor(Color color)
{
_color = color;
ApplyColor(color);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(lineWidth);
}
public void Draw(IList<Connection> targets)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) => { if (annotation != null) { annotation.Draw(target); } });
}
}
public void Redraw()
{
foreach (var connection in children)
{
if (connection != null) { connection.Redraw(); }
}
}
protected override ConnectionAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetColor(_color);
annotation.SetLineWidth(_lineWidth);
return annotation;
}
private void ApplyColor(Color color)
{
foreach (var line in children)
{
if (line != null) { line.SetColor(color); }
}
}
private void ApplyLineWidth(float lineWidth)
{
foreach (var line in children)
{
if (line != null) { line.SetLineWidth(lineWidth); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 03e2158a34405b74280f0e793ae6d083
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 4824063073641375834, guid: ce6db8ad8200781fc9201c21237ce23b,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,114 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class CuboidAnnotation : HierarchicalAnnotation
{
[SerializeField] private PointListAnnotation _pointListAnnotation;
[SerializeField] private ConnectionListAnnotation _lineListAnnotation;
[SerializeField] private TransformAnnotation _transformAnnotation;
[SerializeField] private float _arrowLengthScale = 1.0f;
/// 3 ----------- 7
/// /| /|
/// ../ | 0 / |
/// .4 ----------- 8 |
/// | 1 ---------|- 5
/// | / | /
/// |/ |/
/// 2 ----------- 6
private readonly List<(int, int)> _connections = new List<(int, int)> {
(1, 2),
(3, 4),
(5, 6),
(7, 8),
(1, 3),
(2, 4),
(5, 7),
(6, 8),
(1, 5),
(2, 6),
(3, 7),
(4, 8),
};
public override bool isMirrored
{
set
{
_pointListAnnotation.isMirrored = value;
_lineListAnnotation.isMirrored = value;
_transformAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_pointListAnnotation.rotationAngle = value;
_lineListAnnotation.rotationAngle = value;
_transformAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
private void Start()
{
_pointListAnnotation.Fill(9);
_lineListAnnotation.Fill(_connections, _pointListAnnotation);
}
public void SetPointColor(Color color)
{
_pointListAnnotation.SetColor(color);
}
public void SetLineColor(Color color)
{
_lineListAnnotation.SetColor(color);
}
public void SetLineWidth(float lineWidth)
{
_lineListAnnotation.SetLineWidth(lineWidth);
}
public void SetArrowCapScale(float arrowCapScale)
{
_transformAnnotation.SetArrowCapScale(arrowCapScale);
}
public void SetArrowLengthScale(float arrowLengthScale)
{
_arrowLengthScale = arrowLengthScale;
}
public void SetArrowWidth(float arrowWidth)
{
_transformAnnotation.SetArrowWidth(arrowWidth);
}
public void Draw(ObjectAnnotation target, Vector2 focalLength, Vector2 principalPoint, float zScale, bool visualizeZ = true)
{
if (ActivateFor(target))
{
_pointListAnnotation.Draw(target.Keypoints, focalLength, principalPoint, zScale, visualizeZ);
_lineListAnnotation.Redraw();
_transformAnnotation.Draw(target, _pointListAnnotation[0].transform.localPosition, _arrowLengthScale, visualizeZ);
}
}
}
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 6e88663936c139fb4971d8ab54a85a11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _pointListAnnotation: {instanceID: 0}
- _lineListAnnotation: {instanceID: 0}
- _transformAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,152 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class CuboidListAnnotation : ListAnnotation<CuboidAnnotation>
{
[SerializeField] private Color _pointColor = Color.green;
[SerializeField] private Color _lineColor = Color.red;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
[SerializeField] private float _arrowCapScale = 2.0f;
[SerializeField] private float _arrowLengthScale = 1.0f;
[SerializeField, Range(0, 1)] private float _arrowWidth = 1.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyPointColor(_pointColor);
ApplyLineColor(_lineColor);
ApplyLineWidth(_lineWidth);
ApplyArrowCapScale(_arrowCapScale);
ApplyArrowLengthScale(_arrowLengthScale);
ApplyArrowWidth(_arrowWidth);
}
}
#endif
public void SetPointColor(Color pointColor)
{
_pointColor = pointColor;
ApplyPointColor(pointColor);
}
public void SetLineColor(Color lineColor)
{
_lineColor = lineColor;
ApplyLineColor(lineColor);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(lineWidth);
}
public void SetArrowCapScale(float arrowCapScale)
{
_arrowCapScale = arrowCapScale;
ApplyArrowCapScale(arrowCapScale);
}
public void SetArrowLengthScale(float arrowLengthScale)
{
_arrowLengthScale = arrowLengthScale;
ApplyArrowLengthScale(arrowLengthScale);
}
public void SetArrowWidth(float arrowWidth)
{
_arrowWidth = arrowWidth;
ApplyArrowWidth(arrowWidth);
}
public void Draw(IList<ObjectAnnotation> targets, Vector2 focalLength, Vector2 principalPoint, float scale, bool visualizeZ = true)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, focalLength, principalPoint, scale, visualizeZ); }
});
}
}
public void Draw(FrameAnnotation target, Vector2 focalLength, Vector2 principalPoint, float scale, bool visualizeZ = true)
{
Draw(target?.Annotations, focalLength, principalPoint, scale, visualizeZ);
}
protected override CuboidAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetPointColor(_pointColor);
annotation.SetLineColor(_lineColor);
annotation.SetLineWidth(_lineWidth);
annotation.SetArrowCapScale(_arrowCapScale);
annotation.SetArrowLengthScale(_arrowLengthScale);
annotation.SetArrowWidth(_arrowWidth);
return annotation;
}
private void ApplyPointColor(Color pointColor)
{
foreach (var cuboid in children)
{
if (cuboid != null) { cuboid.SetPointColor(pointColor); }
}
}
private void ApplyLineColor(Color lineColor)
{
foreach (var cuboid in children)
{
if (cuboid != null) { cuboid.SetLineColor(lineColor); }
}
}
private void ApplyLineWidth(float lineWidth)
{
foreach (var cuboid in children)
{
if (cuboid != null) { cuboid.SetLineWidth(lineWidth); }
}
}
private void ApplyArrowCapScale(float arrowCapScale)
{
foreach (var cuboid in children)
{
if (cuboid != null) { cuboid.SetArrowCapScale(arrowCapScale); }
}
}
private void ApplyArrowLengthScale(float arrowLengthScale)
{
foreach (var cuboid in children)
{
if (cuboid != null) { cuboid.SetArrowLengthScale(arrowLengthScale); }
}
}
private void ApplyArrowWidth(float arrowWidth)
{
foreach (var cuboid in children)
{
if (cuboid != null) { cuboid.SetArrowWidth(arrowWidth); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 69c9a763206a6fa938324ba456924e67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 2519151911608524794, guid: 915f3c383a25ff9bd985b1394fc052de,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using Mediapipe.Unity.CoordinateSystem;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class DetectionAnnotation : HierarchicalAnnotation
{
[SerializeField] private RectangleAnnotation _locationDataAnnotation;
[SerializeField] private PointListAnnotation _relativeKeypointsAnnotation;
[SerializeField] private LabelAnnotation _labelAnnotation;
public override bool isMirrored
{
set
{
_locationDataAnnotation.isMirrored = value;
_relativeKeypointsAnnotation.isMirrored = value;
_labelAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_locationDataAnnotation.rotationAngle = value;
_relativeKeypointsAnnotation.rotationAngle = value;
_labelAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
public void SetLineWidth(float lineWidth)
{
_locationDataAnnotation.SetLineWidth(lineWidth);
}
public void SetKeypointRadius(float radius)
{
_relativeKeypointsAnnotation.SetRadius(radius);
}
/// <param name="threshold">
/// Score threshold. This value must be between 0 and 1.
/// This will affect the rectangle's color. For example, if the score is below the threshold, the rectangle will be transparent.
/// The default value is 0.
/// </param>
public void Draw(Detection target, float threshold = 0.0f)
{
if (ActivateFor(target))
{
var score = target.Score.Count > 0 ? target.Score[0] : 1.0f;
var color = GetColor(score, Mathf.Clamp(threshold, 0.0f, 1.0f));
// Assume that location data's format is always RelativeBoundingBox
// TODO: fix if there are cases where this assumption is not correct.
var rectVertices = GetScreenRect().GetRectVertices(target.LocationData.RelativeBoundingBox, rotationAngle, isMirrored);
_locationDataAnnotation.SetColor(GetColor(score, Mathf.Clamp(threshold, 0.0f, 1.0f)));
_locationDataAnnotation.Draw(rectVertices);
var width = rectVertices[2].x - rectVertices[0].x;
var height = rectVertices[2].y - rectVertices[0].y;
var labelText = target.Label.Count > 0 ? target.Label[0] : null;
var vertexId = (((int)rotationAngle / 90) + 1) % 4;
var isInverted = ImageCoordinate.IsInverted(rotationAngle);
var (maxWidth, maxHeight) = isInverted ? (height, width) : (width, height);
_labelAnnotation.Draw(labelText, rectVertices[vertexId], color, maxWidth, maxHeight);
_relativeKeypointsAnnotation.Draw(target.LocationData.RelativeKeypoints);
}
}
private Color GetColor(float score, float threshold)
{
var t = (score - threshold) / (1 - threshold);
var h = Mathf.Lerp(90, 0, t) / 360; // from yellow-green to red
var color = Color.HSVToRGB(h, 1, 1);
if (t < 0)
{
// below the threshold
color.a = 0.5f;
}
return color;
}
}
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: 6a82b9904ff34cc4fb66157217fe48a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _locationDataAnnotation: {instanceID: 0}
- _relativeKeypointsAnnotation: {instanceID: 0}
- _labelAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
namespace Mediapipe.Unity
{
public class DetectionAnnotationController : AnnotationController<DetectionAnnotation>
{
[SerializeField, Range(0, 1)] private float _threshold = 0.0f;
private Detection _currentTarget;
public void DrawNow(Detection target)
{
_currentTarget = target;
SyncNow();
}
public void DrawLater(Detection target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _threshold);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 275060fa7ac08c4128c0ea18c71b73dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotationPrefab: {fileID: 5404176935574894484, guid: 9e4308c3e97d26a388364cbe0ea8bfb4,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public sealed class DetectionListAnnotation : ListAnnotation<DetectionAnnotation>
{
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
[SerializeField] private float _keypointRadius = 15.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyLineWidth(_lineWidth);
ApplyKeypointRadius(_keypointRadius);
}
}
#endif
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(lineWidth);
}
public void SetKeypointRadius(float keypointRadius)
{
_keypointRadius = keypointRadius;
ApplyKeypointRadius(keypointRadius);
}
/// <param name="threshold">
/// Score threshold. This value must be between 0 and 1.
/// This will affect the rectangle's color. For example, if the score is below the threshold, the rectangle will be transparent.
/// The default value is 0.
/// </param>
public void Draw(IList<Detection> targets, float threshold = 0.0f)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, threshold); }
});
}
}
/// <param name="threshold">
/// Score threshold. This value must be between 0 and 1.
/// This will affect the rectangle's color. For example, if the score is below the threshold, the rectangle will be transparent.
/// The default value is 0.
/// </param>
public void Draw(DetectionList target, float threshold = 0.0f)
{
Draw(target?.Detection, threshold);
}
protected override DetectionAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetLineWidth(_lineWidth);
annotation.SetKeypointRadius(_keypointRadius);
return annotation;
}
private void ApplyLineWidth(float lineWidth)
{
foreach (var detection in children)
{
if (detection != null) { detection.SetLineWidth(lineWidth); }
}
}
private void ApplyKeypointRadius(float keypointRadius)
{
foreach (var detection in children)
{
if (detection != null) { detection.SetKeypointRadius(keypointRadius); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: d433cdb024dfd584696eeb11efb71102
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 5404176935574894484, guid: 9e4308c3e97d26a388364cbe0ea8bfb4,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class DetectionListAnnotationController : AnnotationController<DetectionListAnnotation>
{
[SerializeField, Range(0, 1)] private float _threshold = 0.0f;
private IList<Detection> _currentTarget;
public void DrawNow(IList<Detection> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawNow(DetectionList target)
{
DrawNow(target?.Detection);
}
public void DrawLater(IList<Detection> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
public void DrawLater(DetectionList target)
{
DrawLater(target?.Detection);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _threshold);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 8741257e98d0a1560b37e577decc0e2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotationPrefab: {fileID: 6320745076577806712, guid: 26114bc9cccb92454a468ea4d41f400a,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,217 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class FaceLandmarkListAnnotation : HierarchicalAnnotation
{
[SerializeField] private PointListAnnotation _landmarkListAnnotation;
[SerializeField] private ConnectionListAnnotation _connectionListAnnotation;
private const int _LandmarkCount = 468;
private readonly List<(int, int)> _connections = new List<(int, int)> {
// Face Oval
(10, 338),
(338, 297),
(297, 332),
(332, 284),
(284, 251),
(251, 389),
(389, 356),
(356, 454),
(454, 323),
(323, 361),
(361, 288),
(288, 397),
(397, 365),
(365, 379),
(379, 378),
(378, 400),
(400, 377),
(377, 152),
(152, 148),
(148, 176),
(176, 149),
(149, 150),
(150, 136),
(136, 172),
(172, 58),
(58, 132),
(132, 93),
(93, 234),
(234, 127),
(127, 162),
(162, 21),
(21, 54),
(54, 103),
(103, 67),
(67, 109),
(109, 10),
// Left Eye
(33, 7),
(7, 163),
(163, 144),
(144, 145),
(145, 153),
(153, 154),
(154, 155),
(155, 133),
(33, 246),
(246, 161),
(161, 160),
(160, 159),
(159, 158),
(158, 157),
(157, 173),
(173, 133),
// Left Eyebrow
(46, 53),
(53, 52),
(52, 65),
(65, 55),
(70, 63),
(63, 105),
(105, 66),
(66, 107),
// Right Eye
(263, 249),
(249, 390),
(390, 373),
(373, 374),
(374, 380),
(380, 381),
(381, 382),
(382, 362),
(263, 466),
(466, 388),
(388, 387),
(387, 386),
(386, 385),
(385, 384),
(384, 398),
(398, 362),
// Right Eyebrow
(276, 283),
(283, 282),
(282, 295),
(295, 285),
(300, 293),
(293, 334),
(334, 296),
(296, 336),
// Lips (Inner)
(78, 95),
(95, 88),
(88, 178),
(178, 87),
(87, 14),
(14, 317),
(317, 402),
(402, 318),
(318, 324),
(324, 308),
(78, 191),
(191, 80),
(80, 81),
(81, 82),
(82, 13),
(13, 312),
(312, 311),
(311, 310),
(310, 415),
(415, 308),
// Lips (Outer)
(61, 146),
(146, 91),
(91, 181),
(181, 84),
(84, 17),
(17, 314),
(314, 405),
(405, 321),
(321, 375),
(375, 291),
(61, 185),
(185, 40),
(40, 39),
(39, 37),
(37, 0),
(0, 267),
(267, 269),
(269, 270),
(270, 409),
(409, 291),
};
public override bool isMirrored
{
set
{
_landmarkListAnnotation.isMirrored = value;
_connectionListAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_landmarkListAnnotation.rotationAngle = value;
_connectionListAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
private void Start()
{
_landmarkListAnnotation.Fill(_LandmarkCount);
_connectionListAnnotation.Fill(_connections, _landmarkListAnnotation);
}
public void SetLandmarkColor(Color landmarkColor)
{
_landmarkListAnnotation.SetColor(landmarkColor);
}
public void SetLandmarkRadius(float landmarkRadius)
{
_landmarkListAnnotation.SetRadius(landmarkRadius);
}
public void SetConnectionColor(Color connectionColor)
{
_connectionListAnnotation.SetColor(connectionColor);
}
public void SetConnectionWidth(float connectionWidth)
{
_connectionListAnnotation.SetLineWidth(connectionWidth);
}
public void Draw(IList<NormalizedLandmark> target, bool visualizeZ = false)
{
if (ActivateFor(target))
{
_landmarkListAnnotation.Draw(target, visualizeZ);
// Draw explicitly because connection annotation's targets remain the same.
_connectionListAnnotation.Redraw();
}
}
public void Draw(NormalizedLandmarkList target, bool visualizeZ = false)
{
Draw(target?.Landmark, visualizeZ);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 1275dad009e98d9f490bd65e83f7eba5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _landmarkListAnnotation: {instanceID: 0}
- _connectionListAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class FaceLandmarkListAnnotationController : AnnotationController<FaceLandmarkListWithIrisAnnotation>
{
[SerializeField] private bool _visualizeZ = false;
[SerializeField] private int _circleVertices = 128;
private IList<NormalizedLandmark> _currentTarget;
public void DrawNow(IList<NormalizedLandmark> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawNow(NormalizedLandmarkList target)
{
DrawNow(target?.Landmark);
}
public void DrawLater(IList<NormalizedLandmark> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
public void DrawLater(NormalizedLandmarkList target)
{
DrawLater(target?.Landmark);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _visualizeZ, _circleVertices);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b1fe9c61c434938249e2c9d067a337a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,166 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class FaceLandmarkListWithIrisAnnotation : HierarchicalAnnotation
{
[SerializeField] private FaceLandmarkListAnnotation _faceLandmarkListAnnotation;
[SerializeField] private IrisLandmarkListAnnotation _leftIrisLandmarkListAnnotation;
[SerializeField] private IrisLandmarkListAnnotation _rightIrisLandmarkListAnnotation;
private const int _FaceLandmarkCount = 468;
private const int _IrisLandmarkCount = 5;
public override bool isMirrored
{
set
{
_faceLandmarkListAnnotation.isMirrored = value;
_leftIrisLandmarkListAnnotation.isMirrored = value;
_rightIrisLandmarkListAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_faceLandmarkListAnnotation.rotationAngle = value;
_leftIrisLandmarkListAnnotation.rotationAngle = value;
_rightIrisLandmarkListAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
public void SetFaceLandmarkColor(Color color)
{
_faceLandmarkListAnnotation.SetLandmarkColor(color);
}
public void SetIrisLandmarkColor(Color color)
{
_leftIrisLandmarkListAnnotation.SetLandmarkColor(color);
_rightIrisLandmarkListAnnotation.SetLandmarkColor(color);
}
public void SetFaceLandmarkRadius(float radius)
{
_faceLandmarkListAnnotation.SetLandmarkRadius(radius);
}
public void SetIrisLandmarkRadius(float radius)
{
_leftIrisLandmarkListAnnotation.SetLandmarkRadius(radius);
_rightIrisLandmarkListAnnotation.SetLandmarkRadius(radius);
}
public void SetFaceConnectionColor(Color color)
{
_faceLandmarkListAnnotation.SetConnectionColor(color);
}
public void SetFaceConnectionWidth(float width)
{
_faceLandmarkListAnnotation.SetConnectionWidth(width);
}
public void SetIrisCircleColor(Color color)
{
_leftIrisLandmarkListAnnotation.SetCircleColor(color);
_rightIrisLandmarkListAnnotation.SetCircleColor(color);
}
public void SetIrisCircleWidth(float width)
{
_leftIrisLandmarkListAnnotation.SetCircleWidth(width);
_rightIrisLandmarkListAnnotation.SetCircleWidth(width);
}
public void Draw(IList<NormalizedLandmark> target, bool visualizeZ = false, int circleVertices = 128)
{
var (faceLandmarks, leftLandmarks, rightLandmarks) = PartitionLandmarkList(target);
DrawFaceLandmarkList(faceLandmarks, visualizeZ);
DrawLeftIrisLandmarkList(leftLandmarks, visualizeZ, circleVertices);
DrawRightIrisLandmarkList(rightLandmarks, visualizeZ, circleVertices);
}
public void Draw(NormalizedLandmarkList target, bool visualizeZ = false, int circleVertices = 128)
{
Draw(target.Landmark, visualizeZ, circleVertices);
}
public void DrawFaceLandmarkList(IList<NormalizedLandmark> target, bool visualizeZ = false)
{
_faceLandmarkListAnnotation.Draw(target, visualizeZ);
}
public void DrawLeftIrisLandmarkList(IList<NormalizedLandmark> target, bool visualizeZ = false, int circleVertices = 128)
{
// does not deactivate if the target is null as long as face landmarks are present.
_leftIrisLandmarkListAnnotation.Draw(target, visualizeZ, circleVertices);
}
public void DrawRightIrisLandmarkList(IList<NormalizedLandmark> target, bool visualizeZ = false, int circleVertices = 128)
{
// does not deactivate if the target is null as long as face landmarks are present.
_rightIrisLandmarkListAnnotation.Draw(target, visualizeZ, circleVertices);
}
private static (IList<NormalizedLandmark>, IList<NormalizedLandmark>, IList<NormalizedLandmark>) PartitionLandmarkList(IList<NormalizedLandmark> landmarks)
{
if (landmarks == null)
{
return (null, null, null);
}
var enumerator = landmarks.GetEnumerator();
var faceLandmarks = new List<NormalizedLandmark>(_FaceLandmarkCount);
for (var i = 0; i < _FaceLandmarkCount; i++)
{
if (enumerator.MoveNext())
{
faceLandmarks.Add(enumerator.Current);
}
}
if (faceLandmarks.Count < _FaceLandmarkCount)
{
return (null, null, null);
}
var leftIrisLandmarks = new List<NormalizedLandmark>(_IrisLandmarkCount);
for (var i = 0; i < _IrisLandmarkCount; i++)
{
if (enumerator.MoveNext())
{
leftIrisLandmarks.Add(enumerator.Current);
}
}
if (leftIrisLandmarks.Count < _IrisLandmarkCount)
{
return (faceLandmarks, null, null);
}
var rightIrisLandmarks = new List<NormalizedLandmark>(_IrisLandmarkCount);
for (var i = 0; i < _IrisLandmarkCount; i++)
{
if (enumerator.MoveNext())
{
rightIrisLandmarks.Add(enumerator.Current);
}
}
return rightIrisLandmarks.Count < _IrisLandmarkCount ? (faceLandmarks, leftIrisLandmarks, null) : (faceLandmarks, leftIrisLandmarks, rightIrisLandmarks);
}
}
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: e3266a44159f9c0f495c095447ca1e5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _faceLandmarkListAnnotation: {instanceID: 0}
- _leftIrisLandmarkListAnnotation: {instanceID: 0}
- _rightIrisLandmarkListAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
namespace Mediapipe.Unity
{
public class FrameAnnotationController : AnnotationController<CuboidListAnnotation>
{
[SerializeField] private bool _visualizeZ = true;
[SerializeField] private float _translateZ = -10.0f;
[SerializeField] private float _scaleZ = 1.0f;
[HideInInspector] public Vector2 focalLength = Vector2.zero;
[HideInInspector] public Vector2 principalPoint = Vector2.zero;
private FrameAnnotation _currentTarget;
protected override void Start()
{
base.Start();
ApplyTranslateZ(_translateZ);
}
private void OnValidate()
{
ApplyTranslateZ(_translateZ);
}
public void DrawNow(FrameAnnotation target)
{
_currentTarget = target;
SyncNow();
}
public void DrawLater(FrameAnnotation target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, focalLength, principalPoint, _scaleZ, _visualizeZ);
}
private void ApplyTranslateZ(float translateZ)
{
annotation.transform.localPosition = _visualizeZ ? new Vector3(0, 0, translateZ) : Vector3.zero;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ec6e7b1749fd598a93275c583e1ceb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,152 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class HandLandmarkListAnnotation : HierarchicalAnnotation
{
[SerializeField] private PointListAnnotation _landmarkListAnnotation;
[SerializeField] private ConnectionListAnnotation _connectionListAnnotation;
[SerializeField] private Color _leftLandmarkColor = Color.green;
[SerializeField] private Color _rightLandmarkColor = Color.green;
public enum Hand
{
Left,
Right,
}
private const int _LandmarkCount = 21;
private readonly List<(int, int)> _connections = new List<(int, int)> {
(0, 1),
(1, 2),
(2, 3),
(3, 4),
(0, 5),
(5, 9),
(9, 13),
(13, 17),
(0, 17),
(5, 6),
(6, 7),
(7, 8),
(9, 10),
(10, 11),
(11, 12),
(13, 14),
(14, 15),
(15, 16),
(17, 18),
(18, 19),
(19, 20),
};
public override bool isMirrored
{
set
{
_landmarkListAnnotation.isMirrored = value;
_connectionListAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_landmarkListAnnotation.rotationAngle = value;
_connectionListAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
public PointAnnotation this[int index] => _landmarkListAnnotation[index];
private void Start()
{
_landmarkListAnnotation.Fill(_LandmarkCount);
_connectionListAnnotation.Fill(_connections, _landmarkListAnnotation);
}
public void SetLeftLandmarkColor(Color leftLandmarkColor)
{
_leftLandmarkColor = leftLandmarkColor;
}
public void SetRightLandmarkColor(Color rightLandmarkColor)
{
_rightLandmarkColor = rightLandmarkColor;
}
public void SetLandmarkRadius(float landmarkRadius)
{
_landmarkListAnnotation.SetRadius(landmarkRadius);
}
public void SetConnectionColor(Color connectionColor)
{
_connectionListAnnotation.SetColor(connectionColor);
}
public void SetConnectionWidth(float connectionWidth)
{
_connectionListAnnotation.SetLineWidth(connectionWidth);
}
public void SetHandedness(Hand handedness)
{
if (handedness == Hand.Left)
{
_landmarkListAnnotation.SetColor(_leftLandmarkColor);
}
else if (handedness == Hand.Right)
{
_landmarkListAnnotation.SetColor(_rightLandmarkColor);
}
}
public void SetHandedness(IList<Classification> handedness)
{
if (handedness == null || handedness.Count == 0 || handedness[0].Label == "Left")
{
SetHandedness(Hand.Left);
}
else if (handedness[0].Label == "Right")
{
SetHandedness(Hand.Right);
}
// ignore unknown label
}
public void SetHandedness(ClassificationList handedness)
{
SetHandedness(handedness.Classification);
}
public void Draw(IList<NormalizedLandmark> target, bool visualizeZ = false)
{
if (ActivateFor(target))
{
_landmarkListAnnotation.Draw(target, visualizeZ);
// Draw explicitly because connection annotation's targets remain the same.
_connectionListAnnotation.Redraw();
}
}
public void Draw(NormalizedLandmarkList target, bool visualizeZ = false)
{
Draw(target?.Landmark, visualizeZ);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: febd88b13c7a8e91b855c9dd35dd65d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _landmarkListAnnotation: {instanceID: 0}
- _connectionListAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
namespace Mediapipe.Unity
{
public interface IHierachicalAnnotation
{
IHierachicalAnnotation root { get; }
Transform transform { get; }
RectTransform GetAnnotationLayer();
UnityEngine.Rect GetScreenRect();
}
public abstract class HierarchicalAnnotation : MonoBehaviour, IHierachicalAnnotation
{
private IHierachicalAnnotation _root;
public IHierachicalAnnotation root
{
get
{
if (_root == null)
{
var parentObj = transform.parent == null ? null : transform.parent.gameObject;
_root = (parentObj != null && parentObj.TryGetComponent<IHierachicalAnnotation>(out var parent)) ? parent.root : this;
}
return _root;
}
protected set => _root = value;
}
public RectTransform GetAnnotationLayer()
{
return root.transform.parent.gameObject.GetComponent<RectTransform>();
}
public UnityEngine.Rect GetScreenRect()
{
return GetAnnotationLayer().rect;
}
public bool isActive => gameObject.activeSelf;
public bool isActiveInHierarchy => gameObject.activeInHierarchy;
public void SetActive(bool isActive)
{
if (this.isActive != isActive)
{
gameObject.SetActive(isActive);
}
}
/// <summary>
/// Prepare to annotate <paramref name="target" />.
/// If <paramref name="target" /> is not null, it activates itself.
/// </summary>
/// <return>
/// If it is activated and <paramref name="target" /> can be drawn.
/// In effect, it returns if <paramref name="target" /> is null or not.
/// </return>
/// <param name="target">Data to be annotated</param>
protected bool ActivateFor<T>(T target)
{
if (target == null)
{
SetActive(false);
return false;
}
SetActive(true);
return true;
}
public virtual bool isMirrored { get; set; }
public virtual RotationAngle rotationAngle { get; set; } = RotationAngle.Rotation0;
protected TAnnotation InstantiateChild<TAnnotation>(GameObject prefab) where TAnnotation : HierarchicalAnnotation
{
var annotation = Instantiate(prefab, transform).GetComponent<TAnnotation>();
annotation.isMirrored = isMirrored;
annotation.rotationAngle = rotationAngle;
return annotation;
}
protected TAnnotation InstantiateChild<TAnnotation>(string name = "Game Object") where TAnnotation : HierarchicalAnnotation
{
var gameObject = new GameObject(name);
gameObject.transform.SetParent(transform);
return gameObject.AddComponent<TAnnotation>();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 167652b13248a643192ac447671499d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public sealed class HolisticLandmarkListAnnotation : HierarchicalAnnotation
{
[SerializeField] private FaceLandmarkListWithIrisAnnotation _faceLandmarkListAnnotation;
[SerializeField] private PoseLandmarkListAnnotation _poseLandmarkListAnnotation;
[SerializeField] private HandLandmarkListAnnotation _leftHandLandmarkListAnnotation;
[SerializeField] private HandLandmarkListAnnotation _rightHandLandmarkListAnnotation;
[SerializeField] private ConnectionListAnnotation _connectionListAnnotation;
public override bool isMirrored
{
set
{
_faceLandmarkListAnnotation.isMirrored = value;
_poseLandmarkListAnnotation.isMirrored = value;
_leftHandLandmarkListAnnotation.isMirrored = value;
_rightHandLandmarkListAnnotation.isMirrored = value;
_connectionListAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_faceLandmarkListAnnotation.rotationAngle = value;
_poseLandmarkListAnnotation.rotationAngle = value;
_leftHandLandmarkListAnnotation.rotationAngle = value;
_rightHandLandmarkListAnnotation.rotationAngle = value;
_connectionListAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
private void Start()
{
_leftHandLandmarkListAnnotation.SetHandedness(HandLandmarkListAnnotation.Hand.Left);
_rightHandLandmarkListAnnotation.SetHandedness(HandLandmarkListAnnotation.Hand.Right);
_connectionListAnnotation.Fill(2); // left/right wrist joint
}
public void Draw(IList<NormalizedLandmark> faceLandmarks, IList<NormalizedLandmark> poseLandmarks,
IList<NormalizedLandmark> leftHandLandmarks, IList<NormalizedLandmark> rightHandLandmarks, bool visualizeZ = false, int circleVertices = 128)
{
var mask = PoseLandmarkListAnnotation.BodyParts.All;
if (faceLandmarks != null)
{
mask ^= PoseLandmarkListAnnotation.BodyParts.Face;
}
if (leftHandLandmarks != null)
{
mask ^= PoseLandmarkListAnnotation.BodyParts.LeftHand;
}
if (rightHandLandmarks != null)
{
mask ^= PoseLandmarkListAnnotation.BodyParts.RightHand;
}
_faceLandmarkListAnnotation.Draw(faceLandmarks, visualizeZ, circleVertices);
_poseLandmarkListAnnotation.Draw(poseLandmarks, mask, visualizeZ);
_leftHandLandmarkListAnnotation.Draw(leftHandLandmarks, visualizeZ);
_rightHandLandmarkListAnnotation.Draw(rightHandLandmarks, visualizeZ);
RedrawWristJoints();
}
public void Draw(NormalizedLandmarkList faceLandmarks, NormalizedLandmarkList poseLandmarks,
NormalizedLandmarkList leftHandLandmarks, NormalizedLandmarkList rightHandLandmarks, bool visualizeZ = false, int circleVertices = 128)
{
Draw(
faceLandmarks?.Landmark,
poseLandmarks?.Landmark,
leftHandLandmarks?.Landmark,
rightHandLandmarks?.Landmark,
visualizeZ,
circleVertices
);
}
private void RedrawWristJoints()
{
if (_connectionListAnnotation[0].isEmpty)
{
// connect left elbow and wrist
_connectionListAnnotation[0].Draw(new Connection(_poseLandmarkListAnnotation[13], _leftHandLandmarkListAnnotation[0]));
}
if (_connectionListAnnotation[1].isEmpty)
{
// connect right elbow and wrist
_connectionListAnnotation[1].Draw(new Connection(_poseLandmarkListAnnotation[14], _rightHandLandmarkListAnnotation[0]));
}
_connectionListAnnotation.Redraw();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8778d2c5de1025526bb9fccf445db529
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _faceLandmarkListAnnotation: {instanceID: 0}
- _poseLandmarkListAnnotation: {instanceID: 0}
- _leftHandLandmarkListAnnotation: {instanceID: 0}
- _rightHandLandmarkListAnnotation: {instanceID: 0}
- _leftIrisLandmarkListAnnotation: {instanceID: 0}
- _rightIrisLandmarkListAnnotation: {instanceID: 0}
- _connectionListAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,96 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class HolisticLandmarkListAnnotationController : AnnotationController<HolisticLandmarkListAnnotation>
{
[SerializeField] private bool _visualizeZ = false;
[SerializeField] private int _circleVertices = 128;
private IList<NormalizedLandmark> _currentFaceLandmarkList;
private IList<NormalizedLandmark> _currentPoseLandmarkList;
private IList<NormalizedLandmark> _currentLeftHandLandmarkList;
private IList<NormalizedLandmark> _currentRightHandLandmarkList;
public void DrawNow(IList<NormalizedLandmark> faceLandmarkList, IList<NormalizedLandmark> poseLandmarkList,
IList<NormalizedLandmark> leftHandLandmarkList, IList<NormalizedLandmark> rightHandLandmarkList)
{
_currentFaceLandmarkList = faceLandmarkList;
_currentPoseLandmarkList = poseLandmarkList;
_currentLeftHandLandmarkList = leftHandLandmarkList;
_currentRightHandLandmarkList = rightHandLandmarkList;
SyncNow();
}
public void DrawNow(NormalizedLandmarkList faceLandmarkList, NormalizedLandmarkList poseLandmarkList,
NormalizedLandmarkList leftHandLandmarkList, NormalizedLandmarkList rightHandLandmarkList)
{
DrawNow(
faceLandmarkList?.Landmark,
poseLandmarkList?.Landmark,
leftHandLandmarkList?.Landmark,
rightHandLandmarkList?.Landmark
);
}
public void DrawFaceLandmarkListLater(IList<NormalizedLandmark> faceLandmarkList)
{
UpdateCurrentTarget(faceLandmarkList, ref _currentFaceLandmarkList);
}
public void DrawFaceLandmarkListLater(NormalizedLandmarkList faceLandmarkList)
{
DrawFaceLandmarkListLater(faceLandmarkList?.Landmark);
}
public void DrawPoseLandmarkListLater(IList<NormalizedLandmark> poseLandmarkList)
{
UpdateCurrentTarget(poseLandmarkList, ref _currentPoseLandmarkList);
}
public void DrawPoseLandmarkListLater(NormalizedLandmarkList poseLandmarkList)
{
DrawPoseLandmarkListLater(poseLandmarkList?.Landmark);
}
public void DrawLeftHandLandmarkListLater(IList<NormalizedLandmark> leftHandLandmarkList)
{
UpdateCurrentTarget(leftHandLandmarkList, ref _currentLeftHandLandmarkList);
}
public void DrawLeftHandLandmarkListLater(NormalizedLandmarkList leftHandLandmarkList)
{
DrawLeftHandLandmarkListLater(leftHandLandmarkList?.Landmark);
}
public void DrawRightHandLandmarkListLater(IList<NormalizedLandmark> rightHandLandmarkList)
{
UpdateCurrentTarget(rightHandLandmarkList, ref _currentRightHandLandmarkList);
}
public void DrawRightHandLandmarkListLater(NormalizedLandmarkList rightHandLandmarkList)
{
DrawRightHandLandmarkListLater(rightHandLandmarkList?.Landmark);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(
_currentFaceLandmarkList,
_currentPoseLandmarkList,
_currentLeftHandLandmarkList,
_currentRightHandLandmarkList,
_visualizeZ,
_circleVertices
);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d476bc13b66983ac4b7ac1f48fb6aff9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,100 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using Mediapipe.Unity.CoordinateSystem;
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class IrisLandmarkListAnnotation : HierarchicalAnnotation
{
[SerializeField] private PointListAnnotation _landmarkListAnnotation;
[SerializeField] private CircleAnnotation _circleAnnotation;
public override bool isMirrored
{
set
{
_landmarkListAnnotation.isMirrored = value;
_circleAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_landmarkListAnnotation.rotationAngle = value;
_circleAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
public void SetLandmarkColor(Color landmarkColor)
{
_landmarkListAnnotation.SetColor(landmarkColor);
}
public void SetLandmarkRadius(float landmarkRadius)
{
_landmarkListAnnotation.SetRadius(landmarkRadius);
}
public void SetCircleColor(Color circleColor)
{
_circleAnnotation.SetColor(circleColor);
}
public void SetCircleWidth(float circleWidth)
{
_circleAnnotation.SetLineWidth(circleWidth);
}
public void Draw(IList<NormalizedLandmark> target, bool visualizeZ = false, int vertices = 128)
{
if (ActivateFor(target))
{
_landmarkListAnnotation.Draw(target, visualizeZ);
var rect = GetScreenRect();
var center = rect.GetPoint(target[0], rotationAngle, isMirrored);
if (!visualizeZ)
{
center.z = 0.0f;
}
var radius = CalculateRadius(rect, target);
_circleAnnotation.Draw(center, radius, vertices);
}
}
public void Draw(NormalizedLandmarkList target, bool visualizeZ = false, int vertices = 128)
{
Draw(target?.Landmark, visualizeZ, vertices);
}
private float CalculateRadius(UnityEngine.Rect rect, IList<NormalizedLandmark> target)
{
var r1 = CalculateDistance(rect, target[1], target[3]);
var r2 = CalculateDistance(rect, target[2], target[4]);
return (r1 + r2) / 4;
}
private float CalculateDistance(UnityEngine.Rect rect, NormalizedLandmark a, NormalizedLandmark b)
{
var aPos = rect.GetPoint(a, rotationAngle, isMirrored);
var bPos = rect.GetPoint(b, rotationAngle, isMirrored);
aPos.z = 0.0f;
bPos.z = 0.0f;
return Vector3.Distance(aPos, bPos);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 1663d4d6a7ce17fdbad3dcb3e667ee85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _landmarkListAnnotation: {instanceID: 0}
- _circleAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
using UnityEngine.UI;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class LabelAnnotation : HierarchicalAnnotation
{
[SerializeField] private Text _labelText;
[SerializeField] private Transform _backgroundTransform;
public void Draw(string text, Vector3 position, Color color, float maxWidth, float maxHeight)
{
if (ActivateFor(text))
{
// move to the front to show background plane.
_labelText.transform.localPosition = new Vector3(position.x, position.y, -1);
_labelText.transform.localRotation = Quaternion.Euler(0, 0, -(int)rotationAngle);
_labelText.text = text;
_labelText.color = DecideTextColor(color);
_labelText.fontSize = GetFontSize(text, maxWidth, Mathf.Min(maxHeight, 48.0f));
var width = Mathf.Min(_labelText.preferredWidth + 24, maxWidth); // add margin
var height = _labelText.preferredHeight;
var rectTransform = _labelText.GetComponent<RectTransform>();
rectTransform.sizeDelta = new Vector2(width, height);
_backgroundTransform.localScale = new Vector3(width / 10, 1, height / 10);
_backgroundTransform.gameObject.GetComponent<Renderer>().material.color = color;
}
}
private int GetFontSize(string text, float maxWidth, float maxHeight)
{
var ch = Mathf.Min(maxWidth / text.Length, maxHeight);
return (int)Mathf.Clamp(ch, 24.0f, 72.0f);
}
private Color DecideTextColor(Color backgroundColor)
{
var lw = CalcContrastRatio(Color.white, backgroundColor);
var lb = CalcContrastRatio(backgroundColor, Color.black);
return lw < lb ? Color.black : Color.white;
}
private float CalcRelativeLuminance(Color color)
{
var r = color.r <= 0.03928f ? color.r / 12.92f : Mathf.Pow((color.r + 0.055f) / 1.055f, 2.4f);
var g = color.g <= 0.03928f ? color.g / 12.92f : Mathf.Pow((color.g + 0.055f) / 1.055f, 2.4f);
var b = color.b <= 0.03928f ? color.b / 12.92f : Mathf.Pow((color.b + 0.055f) / 1.055f, 2.4f);
return (0.2126f * r) + (0.7152f * g) + (0.0722f * b);
}
private float CalcContrastRatio(Color lighter, Color darker)
{
var l1 = CalcRelativeLuminance(lighter);
var l2 = CalcRelativeLuminance(darker);
return (l1 + 0.05f) / (l2 + 0.05f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96652e94527a9f3e1b4079bc3d139d06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class LineAnnotation : HierarchicalAnnotation
{
[SerializeField] private LineRenderer _lineRenderer;
[SerializeField] private Color _color = Color.green;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
private void OnEnable()
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
private void OnDisable()
{
ApplyLineWidth(0.0f);
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
}
#endif
public void SetColor(Color color)
{
_color = color;
ApplyColor(_color);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(_lineWidth);
}
public void Draw(Vector3 a, Vector3 b)
{
_lineRenderer.SetPositions(new Vector3[] { a, b });
}
public void Draw(GameObject a, GameObject b)
{
_lineRenderer.SetPositions(new Vector3[] { a.transform.localPosition, b.transform.localPosition });
}
public void ApplyColor(Color color)
{
if (_lineRenderer != null)
{
_lineRenderer.startColor = color;
_lineRenderer.endColor = color;
}
}
private void ApplyLineWidth(float lineWidth)
{
if (_lineRenderer != null)
{
_lineRenderer.startWidth = lineWidth;
_lineRenderer.endWidth = lineWidth;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6253cad9b27173aaaf2b82ac82f421c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,122 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public abstract class ListAnnotation<T> : HierarchicalAnnotation where T : HierarchicalAnnotation
{
[SerializeField] private GameObject _annotationPrefab;
private List<T> _children;
protected List<T> children
{
get
{
if (_children == null)
{
_children = new List<T>();
}
return _children;
}
}
public T this[int index] => children[index];
public int count => children.Count;
public void Fill(int count)
{
while (children.Count < count)
{
children.Add(InstantiateChild(false));
}
}
public void Add(T element)
{
children.Add(element);
}
public override bool isMirrored
{
set
{
foreach (var child in children)
{
child.isMirrored = value;
}
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
foreach (var child in children)
{
child.rotationAngle = value;
}
base.rotationAngle = value;
}
}
protected virtual void Destroy()
{
foreach (var child in children)
{
Destroy(child);
}
_children = null;
}
protected virtual T InstantiateChild(bool isActive = true)
{
var annotation = InstantiateChild<T>(_annotationPrefab);
annotation.SetActive(isActive);
return annotation;
}
/// <summary>
/// Zip <see cref="children" /> and <paramref name="argumentList" />, and call <paramref name="action" /> with each pair.
/// If <paramref name="argumentList" /> has more elements than <see cref="children" />, <see cref="children" /> elements will be initialized with <see cref="InstantiateChild" />.
/// </summary>
/// <param name="action">
/// This will receive 2 arguments and return void.
/// The 1st argument is <typeparamref name="T" />, that is an ith element in <see cref="children" />.
/// The 2nd argument is <typeparamref name="TArg" />, that is also an ith element in <paramref name="argumentList" />.
/// </param>
protected void CallActionForAll<TArg>(IList<TArg> argumentList, Action<T, TArg> action)
{
for (var i = 0; i < Mathf.Max(children.Count, argumentList.Count); i++)
{
if (i >= argumentList.Count)
{
// children.Count > argumentList.Count
action(children[i], default);
continue;
}
// reset annotations
if (i >= children.Count)
{
// children.Count < argumentList.Count
children.Add(InstantiateChild());
}
else if (children[i] == null)
{
// child is not initialized yet
children[i] = InstantiateChild();
}
action(children[i], argumentList[i]);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab5e576d5ced309e9a79c434be1a9741
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,146 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class MaskAnnotation : HierarchicalAnnotation
{
[SerializeField] private RawImage _screen;
[SerializeField] private Shader _maskShader;
[SerializeField] private Texture2D _maskTexture;
[SerializeField] private Color _color = Color.blue;
[SerializeField, Range(0, 1)] private float _threshold = 0.9f;
private Material _prevMaterial;
private Material _material;
private GraphicsBuffer _maskBuffer;
private void OnEnable()
{
ApplyMaskTexture(_maskTexture, _color);
ApplyThreshold(_threshold);
}
private void OnDisable()
{
if (_prevMaterial != null)
{
ApplyMaterial(_prevMaterial);
}
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyMaskTexture(_maskTexture, _color);
ApplyThreshold(_threshold);
}
}
#endif
private void OnDestroy()
{
if (_maskBuffer != null)
{
_maskBuffer.Release();
}
}
public void Init(int width, int height)
{
_material = new Material(_maskShader)
{
renderQueue = (int)RenderQueue.Transparent
};
_material.SetTexture("_MainTex", _screen.texture);
ApplyMaskTexture(_maskTexture, _color);
_material.SetInt("_Width", width);
_material.SetInt("_Height", height);
ApplyThreshold(_threshold);
InitMaskBuffer(width, height);
}
public void Draw(float[] mask, int width, int height)
{
if (mask == null)
{
ApplyMaterial(_prevMaterial);
return;
}
if (mask.Length != width * height)
{
throw new ArgumentException("mask size must equal width * height");
}
ApplyMaterial(_material);
_maskBuffer.SetData(mask);
}
private Texture2D CreateMonoColorTexture(Color color)
{
var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
var textureColor = new Color32((byte)(255 * color.r), (byte)(255 * color.g), (byte)(255 * color.b), (byte)(255 * color.a));
texture.SetPixels32(new Color32[] { textureColor });
texture.Apply();
return texture;
}
private void InitMaskBuffer(int width, int height)
{
if (_maskBuffer != null)
{
_maskBuffer.Release();
}
var stride = Marshal.SizeOf(typeof(float));
_maskBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, width * height, stride);
_material.SetBuffer("_MaskBuffer", _maskBuffer);
}
private void ApplyMaterial(Material material)
{
if (_prevMaterial == null)
{
// backup
_prevMaterial = _screen.material;
}
if (_screen.material != material)
{
_screen.material = material;
}
}
private void ApplyMaskTexture(Texture maskTexture, Color maskColor)
{
if (_material != null)
{
_material.SetTexture("_MaskTex", maskTexture == null ? CreateMonoColorTexture(maskColor) : maskTexture);
}
}
private void ApplyThreshold(float threshold)
{
if (_material != null)
{
_material.SetFloat("_Threshold", threshold);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f993d16dcccfcecb6892ad3cc2ea76c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
namespace Mediapipe.Unity
{
public class MaskAnnotationController : AnnotationController<MaskAnnotation>
{
private int _maskWidth;
private int _maskHeight;
private ImageFrame _currentTarget;
private float[] _maskArray;
public void InitScreen(int maskWidth, int maskHeight)
{
_maskWidth = maskWidth;
_maskHeight = maskHeight;
_maskArray = new float[_maskWidth * _maskHeight];
annotation.Init(_maskWidth, _maskHeight);
}
public void DrawNow(ImageFrame target)
{
_currentTarget = target;
UpdateMaskArray(_currentTarget);
SyncNow();
}
public void DrawLater(ImageFrame target)
{
UpdateCurrentTarget(target, ref _currentTarget);
UpdateMaskArray(_currentTarget);
}
private void UpdateMaskArray(ImageFrame imageFrame)
{
if (imageFrame != null)
{
// NOTE: assume that the image is transformed properly by calculators.
var _ = imageFrame.TryReadChannelNormalized(0, _maskArray);
}
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget == null ? null : _maskArray, _maskWidth, _maskHeight);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ece564ad16755f1ef94c4fe04bd7ce2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,181 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class MultiFaceLandmarkListAnnotation : ListAnnotation<FaceLandmarkListWithIrisAnnotation>
{
[SerializeField] private Color _faceLandmarkColor = Color.green;
[SerializeField] private Color _irisLandmarkColor = Color.yellow;
[SerializeField] private float _faceLandmarkRadius = 10.0f;
[SerializeField] private float _irisLandmarkRadius = 10.0f;
[SerializeField] private Color _faceConnectionColor = Color.red;
[SerializeField] private Color _irisCircleColor = Color.blue;
[SerializeField, Range(0, 1)] private float _faceConnectionWidth = 1.0f;
[SerializeField, Range(0, 1)] private float _irisCircleWidth = 1.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyFaceLandmarkColor(_faceLandmarkColor);
ApplyIrisLandmarkColor(_irisLandmarkColor);
ApplyFaceLandmarkRadius(_faceLandmarkRadius);
ApplyIrisLandmarkRadius(_irisLandmarkRadius);
ApplyFaceConnectionColor(_faceConnectionColor);
ApplyIrisCircleColor(_irisCircleColor);
ApplyFaceConnectionWidth(_faceConnectionWidth);
ApplyIrisCircleWidth(_irisCircleWidth);
}
}
#endif
public void SetFaceLandmarkRadius(float radius)
{
_faceLandmarkRadius = radius;
ApplyFaceLandmarkRadius(_faceLandmarkRadius);
}
public void SetIrisLandmarkRadius(float radius)
{
_irisLandmarkRadius = radius;
ApplyIrisLandmarkRadius(_irisLandmarkRadius);
}
public void SetFaceLandmarkColor(Color color)
{
_faceLandmarkColor = color;
ApplyFaceLandmarkColor(_faceLandmarkColor);
}
public void SetIrisLandmarkColor(Color color)
{
_irisLandmarkColor = color;
ApplyIrisLandmarkColor(_irisLandmarkColor);
}
public void SetFaceConnectionWidth(float width)
{
_faceConnectionWidth = width;
ApplyFaceConnectionWidth(_faceConnectionWidth);
}
public void SetFaceConnectionColor(Color color)
{
_faceConnectionColor = color;
ApplyFaceConnectionColor(_faceConnectionColor);
}
public void SetIrisCircleWidth(float width)
{
_irisCircleWidth = width;
ApplyIrisCircleWidth(_irisCircleWidth);
}
public void SetIrisCircleColor(Color color)
{
_irisCircleColor = color;
ApplyIrisCircleColor(_irisCircleColor);
}
public void Draw(IList<NormalizedLandmarkList> targets, bool visualizeZ = false)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, visualizeZ); }
});
}
}
protected override FaceLandmarkListWithIrisAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetFaceLandmarkRadius(_faceLandmarkRadius);
annotation.SetIrisLandmarkRadius(_irisLandmarkRadius);
annotation.SetFaceLandmarkColor(_faceLandmarkColor);
annotation.SetIrisLandmarkColor(_irisLandmarkColor);
annotation.SetFaceConnectionWidth(_faceConnectionWidth);
annotation.SetFaceConnectionColor(_faceConnectionColor);
annotation.SetIrisCircleWidth(_irisCircleWidth);
annotation.SetIrisCircleColor(_irisCircleColor);
return annotation;
}
private void ApplyFaceLandmarkRadius(float radius)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetFaceLandmarkRadius(radius); }
}
}
private void ApplyIrisLandmarkRadius(float radius)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetIrisLandmarkRadius(radius); }
}
}
private void ApplyFaceLandmarkColor(Color color)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetFaceLandmarkColor(color); }
}
}
private void ApplyIrisLandmarkColor(Color color)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetIrisLandmarkColor(color); }
}
}
private void ApplyFaceConnectionWidth(float width)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetFaceConnectionWidth(width); }
}
}
private void ApplyFaceConnectionColor(Color color)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetFaceConnectionColor(color); }
}
}
private void ApplyIrisCircleWidth(float width)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetIrisCircleWidth(width); }
}
}
private void ApplyIrisCircleColor(Color color)
{
foreach (var faceLandmarkList in children)
{
if (faceLandmarkList != null) { faceLandmarkList.SetIrisCircleColor(color); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: d1ec0e202f29d7ee28cccba68415d95b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 7273100862690186831, guid: 16074ce928b0558829296ab0d9ccadf3,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class MultiFaceLandmarkListAnnotationController : AnnotationController<MultiFaceLandmarkListAnnotation>
{
[SerializeField] private bool _visualizeZ = false;
private IList<NormalizedLandmarkList> _currentTarget;
public void DrawNow(IList<NormalizedLandmarkList> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawLater(IList<NormalizedLandmarkList> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _visualizeZ);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 417bb930807ba51e9bfcc5d0e24ef3ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,143 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class MultiHandLandmarkListAnnotation : ListAnnotation<HandLandmarkListAnnotation>
{
[SerializeField] private Color _leftLandmarkColor = Color.green;
[SerializeField] private Color _rightLandmarkColor = Color.green;
[SerializeField] private float _landmarkRadius = 15.0f;
[SerializeField] private Color _connectionColor = Color.white;
[SerializeField, Range(0, 1)] private float _connectionWidth = 1.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyLeftLandmarkColor(_leftLandmarkColor);
ApplyRightLandmarkColor(_rightLandmarkColor);
ApplyLandmarkRadius(_landmarkRadius);
ApplyConnectionColor(_connectionColor);
ApplyConnectionWidth(_connectionWidth);
}
}
#endif
public void SetLeftLandmarkColor(Color leftLandmarkColor)
{
_leftLandmarkColor = leftLandmarkColor;
ApplyLeftLandmarkColor(_leftLandmarkColor);
}
public void SetRightLandmarkColor(Color rightLandmarkColor)
{
_rightLandmarkColor = rightLandmarkColor;
ApplyRightLandmarkColor(_rightLandmarkColor);
}
public void SetLandmarkRadius(float landmarkRadius)
{
_landmarkRadius = landmarkRadius;
ApplyLandmarkRadius(_landmarkRadius);
}
public void SetConnectionColor(Color connectionColor)
{
_connectionColor = connectionColor;
ApplyConnectionColor(_connectionColor);
}
public void SetConnectionWidth(float connectionWidth)
{
_connectionWidth = connectionWidth;
ApplyConnectionWidth(_connectionWidth);
}
public void SetHandedness(IList<ClassificationList> handedness)
{
var count = handedness == null ? 0 : handedness.Count;
for (var i = 0; i < Mathf.Min(count, children.Count); i++)
{
children[i].SetHandedness(handedness[i]);
}
for (var i = count; i < children.Count; i++)
{
children[i].SetHandedness((IList<Classification>)null);
}
}
public void Draw(IList<NormalizedLandmarkList> targets, bool visualizeZ = false)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, visualizeZ); }
});
}
}
protected override HandLandmarkListAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetLeftLandmarkColor(_leftLandmarkColor);
annotation.SetRightLandmarkColor(_rightLandmarkColor);
annotation.SetLandmarkRadius(_landmarkRadius);
annotation.SetConnectionColor(_connectionColor);
annotation.SetConnectionWidth(_connectionWidth);
return annotation;
}
private void ApplyLeftLandmarkColor(Color leftLandmarkColor)
{
foreach (var handLandmarkList in children)
{
if (handLandmarkList != null) { handLandmarkList.SetLeftLandmarkColor(leftLandmarkColor); }
}
}
private void ApplyRightLandmarkColor(Color rightLandmarkColor)
{
foreach (var handLandmarkList in children)
{
if (handLandmarkList != null) { handLandmarkList.SetRightLandmarkColor(rightLandmarkColor); }
}
}
private void ApplyLandmarkRadius(float landmarkRadius)
{
foreach (var handLandmarkList in children)
{
if (handLandmarkList != null) { handLandmarkList.SetLandmarkRadius(landmarkRadius); }
}
}
private void ApplyConnectionColor(Color connectionColor)
{
foreach (var handLandmarkList in children)
{
if (handLandmarkList != null) { handLandmarkList.SetConnectionColor(connectionColor); }
}
}
private void ApplyConnectionWidth(float connectionWidth)
{
foreach (var handLandmarkList in children)
{
if (handLandmarkList != null) { handLandmarkList.SetConnectionWidth(connectionWidth); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 5ba3c50c2cb46dc9e916dd3a33c8a488
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 4724242833946851653, guid: b420c3106bd9cc5b6871a7df5459dba0,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class MultiHandLandmarkListAnnotationController : AnnotationController<MultiHandLandmarkListAnnotation>
{
[SerializeField] private bool _visualizeZ = false;
private IList<NormalizedLandmarkList> _currentHandLandmarkLists;
private IList<ClassificationList> _currentHandedness;
public void DrawNow(IList<NormalizedLandmarkList> handLandmarkLists, IList<ClassificationList> handedness = null)
{
_currentHandLandmarkLists = handLandmarkLists;
_currentHandedness = handedness;
SyncNow();
}
public void DrawLater(IList<NormalizedLandmarkList> handLandmarkLists)
{
UpdateCurrentTarget(handLandmarkLists, ref _currentHandLandmarkLists);
}
public void DrawLater(IList<ClassificationList> handedness)
{
UpdateCurrentTarget(handedness, ref _currentHandedness);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentHandLandmarkLists, _visualizeZ);
if (_currentHandedness != null)
{
annotation.SetHandedness(_currentHandedness);
}
_currentHandedness = null;
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: c3f533a3b3fececba86462b46ff711ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotationPrefab: {fileID: 7434967355688441420, guid: 87a9be2a7620991a78342ef9a56fa62c,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Mediapipe.Unity
{
public class NormalizedLandmarkListAnnotationController : AnnotationController<PointListAnnotation>
{
[SerializeField] private bool _visualizeZ = false;
private IList<NormalizedLandmark> _currentTarget;
public void DrawNow(IList<NormalizedLandmark> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawNow(NormalizedLandmarkList target)
{
DrawNow(target?.Landmark);
}
public void DrawNow(IList<NormalizedLandmarkList> landmarkLists)
{
DrawNow(FlattenNormalizedLandmarkLists(landmarkLists));
}
public void DrawLater(IList<NormalizedLandmark> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
public void DrawLater(NormalizedLandmarkList target)
{
UpdateCurrentTarget(target?.Landmark, ref _currentTarget);
}
public void DrawLater(IList<NormalizedLandmarkList> landmarkLists)
{
UpdateCurrentTarget(FlattenNormalizedLandmarkLists(landmarkLists), ref _currentTarget);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _visualizeZ);
}
private IList<NormalizedLandmark> FlattenNormalizedLandmarkLists(IList<NormalizedLandmarkList> landmarkLists)
{
return landmarkLists?.Select((x) => x.Landmark).SelectMany(x => x).ToList();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: acbf25ab09c7f4115948d7981b6024c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
namespace Mediapipe.Unity
{
public class NormalizedRectAnnotationController : AnnotationController<RectangleAnnotation>
{
private NormalizedRect _currentTarget;
public void DrawNow(NormalizedRect target)
{
_currentTarget = target;
SyncNow();
}
public void DrawLater(NormalizedRect target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 145060647209d6d1c86b8ccce9fcaf5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
namespace Mediapipe.Unity
{
public class NormalizedRectListAnnotationController : AnnotationController<RectangleListAnnotation>
{
private IList<NormalizedRect> _currentTarget;
public void DrawNow(IList<NormalizedRect> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawLater(IList<NormalizedRect> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 4f4190c4421f92d5187d2ebdc88a9594
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotationPrefab: {fileID: 9058686056784979840, guid: 94ee148c30d93d3c8aaa3aaf5ab54853,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,138 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using Mediapipe.Unity.CoordinateSystem;
using UnityEngine;
using mplt = Mediapipe.LocationData.Types;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class PointAnnotation : HierarchicalAnnotation
{
[SerializeField] private Color _color = Color.green;
[SerializeField] private float _radius = 15.0f;
private void OnEnable()
{
ApplyColor(_color);
ApplyRadius(_radius);
}
private void OnDisable()
{
ApplyRadius(0.0f);
}
public void SetColor(Color color)
{
_color = color;
ApplyColor(_color);
}
public void SetRadius(float radius)
{
_radius = radius;
ApplyRadius(_radius);
}
public void Draw(Vector3 position)
{
SetActive(true); // Vector3 is not nullable
transform.localPosition = position;
}
public void Draw(Landmark target, Vector3 scale, bool visualizeZ = true)
{
if (ActivateFor(target))
{
var position = GetScreenRect().GetPoint(target, scale, rotationAngle, isMirrored);
if (!visualizeZ)
{
position.z = 0.0f;
}
transform.localPosition = position;
}
}
public void Draw(NormalizedLandmark target, bool visualizeZ = true)
{
if (ActivateFor(target))
{
var position = GetScreenRect().GetPoint(target, rotationAngle, isMirrored);
if (!visualizeZ)
{
position.z = 0.0f;
}
transform.localPosition = position;
}
}
public void Draw(NormalizedPoint2D target)
{
if (ActivateFor(target))
{
var position = GetScreenRect().GetPoint(target, rotationAngle, isMirrored);
transform.localPosition = position;
}
}
public void Draw(Point3D target, Vector2 focalLength, Vector2 principalPoint, float zScale, bool visualizeZ = true)
{
if (ActivateFor(target))
{
var position = GetScreenRect().GetPoint(target, focalLength, principalPoint, zScale, rotationAngle, isMirrored);
if (!visualizeZ)
{
position.z = 0.0f;
}
transform.localPosition = position;
}
}
public void Draw(AnnotatedKeyPoint target, Vector2 focalLength, Vector2 principalPoint, float zScale, bool visualizeZ = true)
{
if (visualizeZ)
{
Draw(target?.Point3D, focalLength, principalPoint, zScale, true);
}
else
{
Draw(target?.Point2D);
}
}
public void Draw(mplt.RelativeKeypoint target, float threshold = 0.0f)
{
if (ActivateFor(target))
{
Draw(GetScreenRect().GetPoint(target, rotationAngle, isMirrored));
SetColor(GetColor(target.Score, threshold));
}
}
private void ApplyColor(Color color)
{
GetComponent<Renderer>().material.color = color;
}
private void ApplyRadius(float radius)
{
transform.localScale = radius * Vector3.one;
}
private Color GetColor(float score, float threshold)
{
var t = (score - threshold) / (1 - threshold);
var h = Mathf.Lerp(90, 0, t) / 360; // from yellow-green to red
return Color.HSVToRGB(h, 1, 1);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0476f607871e33f2783b582f7a75703c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,135 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
using mplt = Mediapipe.LocationData.Types;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class PointListAnnotation : ListAnnotation<PointAnnotation>
{
[SerializeField] private Color _color = Color.green;
[SerializeField] private float _radius = 15.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyColor(_color);
ApplyRadius(_radius);
}
}
#endif
public void SetColor(Color color)
{
_color = color;
ApplyColor(_color);
}
public void SetRadius(float radius)
{
_radius = radius;
ApplyRadius(_radius);
}
public void Draw(IList<Vector3> targets)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target); }
});
}
}
public void Draw(IList<Landmark> targets, Vector3 scale, bool visualizeZ = true)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, scale, visualizeZ); }
});
}
}
public void Draw(LandmarkList targets, Vector3 scale, bool visualizeZ = true)
{
Draw(targets.Landmark, scale, visualizeZ);
}
public void Draw(IList<NormalizedLandmark> targets, bool visualizeZ = true)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, visualizeZ); }
});
}
}
public void Draw(NormalizedLandmarkList targets, bool visualizeZ = true)
{
Draw(targets.Landmark, visualizeZ);
}
public void Draw(IList<AnnotatedKeyPoint> targets, Vector2 focalLength, Vector2 principalPoint, float zScale, bool visualizeZ = true)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, focalLength, principalPoint, zScale, visualizeZ); }
});
}
}
public void Draw(IList<mplt.RelativeKeypoint> targets, float threshold = 0.0f)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, threshold); }
});
}
}
protected override PointAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetColor(_color);
annotation.SetRadius(_radius);
return annotation;
}
private void ApplyColor(Color color)
{
foreach (var point in children)
{
if (point != null) { point.SetColor(color); }
}
}
private void ApplyRadius(float radius)
{
foreach (var point in children)
{
if (point != null) { point.SetRadius(radius); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: ae7563ab8645734c6b682cbbca130f6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 5105739270100195971, guid: cd50999c161e69345953a2cb517dd339,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,278 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public sealed class PoseLandmarkListAnnotation : HierarchicalAnnotation
{
[SerializeField] private PointListAnnotation _landmarkListAnnotation;
[SerializeField] private ConnectionListAnnotation _connectionListAnnotation;
[SerializeField] private Color _leftLandmarkColor = Color.green;
[SerializeField] private Color _rightLandmarkColor = Color.green;
[Flags]
public enum BodyParts : short
{
None = 0,
Face = 1,
// Torso = 2,
LeftArm = 4,
LeftHand = 8,
RightArm = 16,
RightHand = 32,
LowerBody = 64,
All = 127,
}
private const int _LandmarkCount = 33;
private static readonly int[] _LeftLandmarks = new int[] {
1, 2, 3, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31
};
private static readonly int[] _RightLandmarks = new int[] {
4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32
};
private static readonly List<(int, int)> _Connections = new List<(int, int)> {
// Left Eye
(0, 1),
(1, 2),
(2, 3),
(3, 7),
// Right Eye
(0, 4),
(4, 5),
(5, 6),
(6, 8),
// Lips
(9, 10),
// Left Arm
(11, 13),
(13, 15),
// Left Hand
(15, 17),
(15, 19),
(15, 21),
(17, 19),
// Right Arm
(12, 14),
(14, 16),
// Right Hand
(16, 18),
(16, 20),
(16, 22),
(18, 20),
// Torso
(11, 12),
(12, 24),
(24, 23),
(23, 11),
// Left Leg
(23, 25),
(25, 27),
(27, 29),
(27, 31),
(29, 31),
// Right Leg
(24, 26),
(26, 28),
(28, 30),
(28, 32),
(30, 32),
};
public override bool isMirrored
{
set
{
_landmarkListAnnotation.isMirrored = value;
_connectionListAnnotation.isMirrored = value;
base.isMirrored = value;
}
}
public override RotationAngle rotationAngle
{
set
{
_landmarkListAnnotation.rotationAngle = value;
_connectionListAnnotation.rotationAngle = value;
base.rotationAngle = value;
}
}
public PointAnnotation this[int index] => _landmarkListAnnotation[index];
private void Start()
{
_landmarkListAnnotation.Fill(_LandmarkCount);
ApplyLeftLandmarkColor(_leftLandmarkColor);
ApplyRightLandmarkColor(_rightLandmarkColor);
_connectionListAnnotation.Fill(_Connections, _landmarkListAnnotation);
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyLeftLandmarkColor(_leftLandmarkColor);
ApplyRightLandmarkColor(_rightLandmarkColor);
}
}
#endif
public void SetLeftLandmarkColor(Color leftLandmarkColor)
{
_leftLandmarkColor = leftLandmarkColor;
ApplyLeftLandmarkColor(_leftLandmarkColor);
}
public void SetRightLandmarkColor(Color rightLandmarkColor)
{
_rightLandmarkColor = rightLandmarkColor;
ApplyRightLandmarkColor(_rightLandmarkColor);
}
public void SetLandmarkRadius(float landmarkRadius)
{
_landmarkListAnnotation.SetRadius(landmarkRadius);
}
public void SetConnectionColor(Color connectionColor)
{
_connectionListAnnotation.SetColor(connectionColor);
}
public void SetConnectionWidth(float connectionWidth)
{
_connectionListAnnotation.SetLineWidth(connectionWidth);
}
public void Draw(IList<Landmark> target, Vector3 scale, bool visualizeZ = false)
{
if (ActivateFor(target))
{
_landmarkListAnnotation.Draw(target, scale, visualizeZ);
// Draw explicitly because connection annotation's targets remain the same.
_connectionListAnnotation.Redraw();
}
}
public void Draw(LandmarkList target, Vector3 scale, bool visualizeZ = false)
{
Draw(target?.Landmark, scale, visualizeZ);
}
public void Draw(IList<NormalizedLandmark> target, BodyParts mask, bool visualizeZ = false)
{
if (ActivateFor(target))
{
_landmarkListAnnotation.Draw(target, visualizeZ);
ApplyMask(mask);
// Draw explicitly because connection annotation's targets remain the same.
_connectionListAnnotation.Redraw();
}
}
public void Draw(NormalizedLandmarkList target, BodyParts mask, bool visualizeZ = false)
{
Draw(target?.Landmark, mask, visualizeZ);
}
public void Draw(IList<NormalizedLandmark> target, bool visualizeZ = false)
{
Draw(target, BodyParts.All, visualizeZ);
}
public void Draw(NormalizedLandmarkList target, bool visualizeZ = false)
{
Draw(target?.Landmark, BodyParts.All, visualizeZ);
}
private void ApplyLeftLandmarkColor(Color color)
{
var annotationCount = _landmarkListAnnotation == null ? 0 : _landmarkListAnnotation.count;
if (annotationCount >= _LandmarkCount)
{
foreach (var index in _LeftLandmarks)
{
_landmarkListAnnotation[index].SetColor(color);
}
}
}
private void ApplyRightLandmarkColor(Color color)
{
var annotationCount = _landmarkListAnnotation == null ? 0 : _landmarkListAnnotation.count;
if (annotationCount >= _LandmarkCount)
{
foreach (var index in _RightLandmarks)
{
_landmarkListAnnotation[index].SetColor(color);
}
}
}
private void ApplyMask(BodyParts mask)
{
if (mask == BodyParts.All)
{
return;
}
if (!mask.HasFlag(BodyParts.Face))
{
// deactivate face landmarks
for (var i = 0; i <= 10; i++)
{
_landmarkListAnnotation[i].SetActive(false);
}
}
if (!mask.HasFlag(BodyParts.LeftArm))
{
// deactivate left elbow to hide left arm
_landmarkListAnnotation[13].SetActive(false);
}
if (!mask.HasFlag(BodyParts.LeftHand))
{
// deactive left wrist, thumb, index and pinky to hide left hand
_landmarkListAnnotation[15].SetActive(false);
_landmarkListAnnotation[17].SetActive(false);
_landmarkListAnnotation[19].SetActive(false);
_landmarkListAnnotation[21].SetActive(false);
}
if (!mask.HasFlag(BodyParts.RightArm))
{
// deactivate right elbow to hide right arm
_landmarkListAnnotation[14].SetActive(false);
}
if (!mask.HasFlag(BodyParts.RightHand))
{
// deactivate right wrist, thumb, index and pinky to hide right hand
_landmarkListAnnotation[16].SetActive(false);
_landmarkListAnnotation[18].SetActive(false);
_landmarkListAnnotation[20].SetActive(false);
_landmarkListAnnotation[22].SetActive(false);
}
if (!mask.HasFlag(BodyParts.LowerBody))
{
// deactivate lower body landmarks
for (var i = 25; i <= 32; i++)
{
_landmarkListAnnotation[i].SetActive(false);
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 39bac9dd52c31ae7aa01a7383bc44853
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _landmarkListAnnotation: {instanceID: 0}
- _connectionListAnnotation: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class PoseLandmarkListAnnotationController : AnnotationController<PoseLandmarkListAnnotation>
{
[SerializeField] private bool _visualizeZ = false;
private IList<NormalizedLandmark> _currentTarget;
public void DrawNow(IList<NormalizedLandmark> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawNow(NormalizedLandmarkList target)
{
DrawNow(target?.Landmark);
}
public void DrawLater(IList<NormalizedLandmark> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
public void DrawLater(NormalizedLandmarkList target)
{
DrawLater(target?.Landmark);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _visualizeZ);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 70c2b36b394190968977c6493e60e0af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotationPrefab: {fileID: 1915238444563462411, guid: 4418f6a92856c5b51b58a36e3be7ed5c,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
public class PoseWorldLandmarkListAnnotationController : AnnotationController<PoseLandmarkListAnnotation>
{
[SerializeField] private float _hipHeightMeter = 0.9f;
[SerializeField] private Vector3 _scale = new Vector3(100, 100, 100);
[SerializeField] private bool _visualizeZ = true;
private IList<Landmark> _currentTarget;
protected override void Start()
{
base.Start();
transform.localPosition = new Vector3(0, _hipHeightMeter * _scale.y, 0);
}
public void DrawNow(IList<Landmark> target)
{
_currentTarget = target;
SyncNow();
}
public void DrawNow(LandmarkList target)
{
DrawNow(target?.Landmark);
}
public void DrawLater(IList<Landmark> target)
{
UpdateCurrentTarget(target, ref _currentTarget);
}
public void DrawLater(LandmarkList target)
{
DrawLater(target?.Landmark);
}
protected override void SyncNow()
{
isStale = false;
annotation.Draw(_currentTarget, _scale, _visualizeZ);
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f152430ce9da8f9f2ae5cfd135b4d061
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- annotationPrefab: {fileID: 8684491236681850282, guid: e076022eee6a6227698c34a5ba1d25ea,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,148 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using Mediapipe.Unity.CoordinateSystem;
using UnityEngine;
using mplt = Mediapipe.LocationData.Types;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class RectangleAnnotation : HierarchicalAnnotation
{
[SerializeField] private LineRenderer _lineRenderer;
[SerializeField] private Color _color = Color.red;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
private static readonly Vector3[] _EmptyPositions = new Vector3[] { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero };
private void OnEnable()
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
private void OnDisable()
{
ApplyLineWidth(0.0f);
_lineRenderer.SetPositions(_EmptyPositions);
}
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
}
#endif
public void SetColor(Color color)
{
_color = color;
ApplyColor(_color);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(_lineWidth);
}
public void Draw(Vector3[] positions)
{
_lineRenderer.SetPositions(positions ?? _EmptyPositions);
}
public void Draw(Rect target, Vector2Int imageSize)
{
if (ActivateFor(target))
{
Draw(GetScreenRect().GetRectVertices(target, imageSize, rotationAngle, isMirrored));
}
}
public void Draw(NormalizedRect target)
{
if (ActivateFor(target))
{
Draw(GetScreenRect().GetRectVertices(target, rotationAngle, isMirrored));
}
}
public void Draw(LocationData target, Vector2Int imageSize)
{
if (ActivateFor(target))
{
switch (target.Format)
{
case mplt.Format.BoundingBox:
{
Draw(GetScreenRect().GetRectVertices(target.BoundingBox, imageSize, rotationAngle, isMirrored));
break;
}
case mplt.Format.RelativeBoundingBox:
{
Draw(GetScreenRect().GetRectVertices(target.RelativeBoundingBox, rotationAngle, isMirrored));
break;
}
case mplt.Format.Global:
case mplt.Format.Mask:
default:
{
throw new System.ArgumentException($"The format of the LocationData must be BoundingBox or RelativeBoundingBox, but {target.Format}");
}
}
}
}
public void Draw(LocationData target)
{
if (ActivateFor(target))
{
switch (target.Format)
{
case mplt.Format.RelativeBoundingBox:
{
Draw(GetScreenRect().GetRectVertices(target.RelativeBoundingBox, rotationAngle, isMirrored));
break;
}
case mplt.Format.BoundingBox:
case mplt.Format.Global:
case mplt.Format.Mask:
default:
{
throw new System.ArgumentException($"The format of the LocationData must be RelativeBoundingBox, but {target.Format}");
}
}
}
}
private void ApplyColor(Color color)
{
if (_lineRenderer != null)
{
_lineRenderer.startColor = color;
_lineRenderer.endColor = color;
}
}
private void ApplyLineWidth(float lineWidth)
{
if (_lineRenderer != null)
{
_lineRenderer.startWidth = lineWidth;
_lineRenderer.endWidth = lineWidth;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ba39488de81a63b298a92a9d09ac42db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using System.Collections.Generic;
using UnityEngine;
namespace Mediapipe.Unity
{
#pragma warning disable IDE0065
using Color = UnityEngine.Color;
#pragma warning restore IDE0065
public class RectangleListAnnotation : ListAnnotation<RectangleAnnotation>
{
[SerializeField] private Color _color = Color.red;
[SerializeField, Range(0, 1)] private float _lineWidth = 1.0f;
#if UNITY_EDITOR
private void OnValidate()
{
if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
{
ApplyColor(_color);
ApplyLineWidth(_lineWidth);
}
}
#endif
public void SetColor(Color color)
{
_color = color;
ApplyColor(_color);
}
public void SetLineWidth(float lineWidth)
{
_lineWidth = lineWidth;
ApplyLineWidth(_lineWidth);
}
public void Draw(IList<Rect> targets, Vector2Int imageSize)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target, imageSize); }
});
}
}
public void Draw(IList<NormalizedRect> targets)
{
if (ActivateFor(targets))
{
CallActionForAll(targets, (annotation, target) =>
{
if (annotation != null) { annotation.Draw(target); }
});
}
}
protected override RectangleAnnotation InstantiateChild(bool isActive = true)
{
var annotation = base.InstantiateChild(isActive);
annotation.SetLineWidth(_lineWidth);
annotation.SetColor(_color);
return annotation;
}
private void ApplyColor(Color color)
{
foreach (var rect in children)
{
if (rect != null) { rect.SetColor(color); }
}
}
private void ApplyLineWidth(float lineWidth)
{
foreach (var rect in children)
{
if (rect != null) { rect.SetLineWidth(lineWidth); }
}
}
}
}

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: ff689dc19c0db10608af875e2c24ade9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _annotationPrefab: {fileID: 4965192403804369243, guid: 3b696480602fe21de85315216956bd42,
type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
// Copyright (c) 2021 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
using Mediapipe.Unity.CoordinateSystem;
using UnityEngine;
namespace Mediapipe.Unity
{
public sealed class TransformAnnotation : HierarchicalAnnotation
{
[SerializeField] private Arrow _xArrow;
[SerializeField] private Arrow _yArrow;
[SerializeField] private Arrow _zArrow;
public Vector3 origin
{
get => transform.localPosition;
set => transform.localPosition = value;
}
public void SetArrowCapScale(float arrowCapScale)
{
_xArrow.SetCapScale(arrowCapScale);
_yArrow.SetCapScale(arrowCapScale);
_zArrow.SetCapScale(arrowCapScale);
}
public void SetArrowWidth(float arrowWidth)
{
_xArrow.SetLineWidth(arrowWidth);
_yArrow.SetLineWidth(arrowWidth);
_zArrow.SetLineWidth(arrowWidth);
}
public void Draw(Quaternion rotation, Vector3 scale, bool visualizeZ = true)
{
var q = Quaternion.Euler(0, 0, -(int)rotationAngle);
DrawArrow(_xArrow, scale.x * (q * rotation * Vector3.right), visualizeZ);
DrawArrow(_yArrow, scale.y * (q * rotation * Vector3.up), visualizeZ);
DrawArrow(_zArrow, scale.z * (q * rotation * Vector3.forward), visualizeZ);
}
public void Draw(ObjectAnnotation target, Vector3 position, float arrowLengthScale = 1.0f, bool visualizeZ = true)
{
origin = position;
var (xDir, yDir, zDir) = CameraCoordinate.GetDirections(target, rotationAngle, isMirrored);
DrawArrow(_xArrow, arrowLengthScale * xDir, visualizeZ);
DrawArrow(_yArrow, arrowLengthScale * yDir, visualizeZ);
DrawArrow(_zArrow, arrowLengthScale * zDir, visualizeZ);
}
private void DrawArrow(Arrow arrow, Vector3 vec, bool visualizeZ)
{
var magnitude = vec.magnitude;
var direction = vec.normalized;
if (!visualizeZ)
{
var direction2d = new Vector3(direction.x, direction.y, 0);
magnitude *= direction2d.magnitude;
direction = direction2d;
}
arrow.direction = direction;
arrow.magnitude = magnitude;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 219b3f9aac8f8d18db8e85142b28db7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: