Wes xx mediapipe integration
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e803487d9a067d4fbe25e4b2b884643
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f1980da4d69e05869d4a1cdac8f4ced
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a793788ee98ea8ea3b28457e7dd7197d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98e622123dd1b9ac1a87d8609ce21d37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e71b609a71e6232a8043e02da04b2bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90aa7330426be071f9141dd20224e6cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1fe9c61c434938249e2c9d067a337a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- annotation: {instanceID: 0}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ec6e7b1749fd598a93275c583e1ceb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 167652b13248a643192ac447671499d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d476bc13b66983ac4b7ac1f48fb6aff9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96652e94527a9f3e1b4079bc3d139d06
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6253cad9b27173aaaf2b82ac82f421c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab5e576d5ced309e9a79c434be1a9741
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f993d16dcccfcecb6892ad3cc2ea76c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ece564ad16755f1ef94c4fe04bd7ce2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 417bb930807ba51e9bfcc5d0e24ef3ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acbf25ab09c7f4115948d7981b6024c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 145060647209d6d1c86b8ccce9fcaf5a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0476f607871e33f2783b582f7a75703c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba39488de81a63b298a92a9d09ac42db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 219b3f9aac8f8d18db8e85142b28db7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 483d25e852ee39b40a6cb7b11dacbea5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,303 @@
|
||||
// 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.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides helper methods for converting camera coordinate values to local coordinate values in Unity.
|
||||
/// See <see cref="https://google.github.io/mediapipe/solutions/objectron.html#coordinate-systems" /> for more details.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Assume that the origin is common to the two coordinate systems.
|
||||
/// </remarks>
|
||||
public static class CameraCoordinate
|
||||
{
|
||||
public static Vector3 CameraToRealWorld(float x, float y, float z, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var (rx, ry) = isInverted ? (y, x) : (x, y);
|
||||
return new Vector3(IsXReversed(imageRotation, isMirrored) ? -rx : rx, IsYReversed(imageRotation, isMirrored) ? -ry : ry, -z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from camera coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="x">X in camera coordinates</param>
|
||||
/// <param name="y">Y in camera coordinates</param>
|
||||
/// <param name="z">Z in camera coordinates</param>
|
||||
/// <param name="xMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="xMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="focalLengthX">Normalized focal length X in NDC space</param>
|
||||
/// <param name="focalLengthY">Normalized focal length Y in NDC space</param>
|
||||
/// <param name="principalX">Normalized principal point X in NDC space</param>
|
||||
/// <param name="principalY">Normalized principal point Y in NDC space</param>
|
||||
/// <param name="zScale">Ratio of Z values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 CameraToLocalPoint(float x, float y, float z, float xMin, float xMax, float yMin, float yMax,
|
||||
float focalLengthX, float focalLengthY, float principalX, float principalY,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var (ndcX, ndcY) = ((-focalLengthX * x / z) + principalX, (focalLengthY * y / z) + principalY);
|
||||
var (width, height) = (xMax - xMin, yMax - yMin);
|
||||
var (nx, ny) = ((1 + ndcX) / 2.0f, (1 - ndcY) / 2.0f);
|
||||
var (rectX, rectY) = IsInverted(imageRotation) ? (ny * width, nx * height) : (nx * width, ny * height);
|
||||
var localX = (IsXReversed(imageRotation, isMirrored) ? width - rectX : rectX) + xMin;
|
||||
var localY = (IsYReversed(imageRotation, isMirrored) ? height - rectY : rectY) + yMin;
|
||||
// Reverse the sign of Z because camera coordinate system is right-handed
|
||||
var localZ = -z * zScale;
|
||||
|
||||
return new Vector3(localX, localY, localZ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from camera coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="x">X in camera coordinates</param>
|
||||
/// <param name="y">Y in camera coordinates</param>
|
||||
/// <param name="z">Z in camera coordinates</param>
|
||||
/// <param name="xMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="xMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="focalLength">Normalized focal lengths in NDC space</param>
|
||||
/// <param name="principalPoint">Normalized principal point in NDC space</param>
|
||||
/// <param name="zScale">Ratio of Z values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 CameraToLocalPoint(float x, float y, float z, float xMin, float xMax, float yMin, float yMax, Vector2 focalLength, Vector2 principalPoint,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToLocalPoint(x, y, z, xMin, xMax, yMin, yMax, focalLength.x, focalLength.y, principalPoint.x, principalPoint.y, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from camera coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="x">X in camera coordinates</param>
|
||||
/// <param name="y">Y in camera coordinates</param>
|
||||
/// <param name="z">Z in camera coordinates</param>
|
||||
/// <param name="focalLengthX">Normalized focal length X in NDC space</param>
|
||||
/// <param name="focalLengthY">Normalized focal length Y in NDC space</param>
|
||||
/// <param name="principalX">Normalized principal point X in NDC space</param>
|
||||
/// <param name="principalY">Normalized principal point Y in NDC space</param>
|
||||
/// <param name="zScale">Ratio of Z values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 CameraToPoint(UnityEngine.Rect rectangle, float x, float y, float z,
|
||||
float focalLengthX, float focalLengthY, float principalX, float principalY,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, focalLengthX, focalLengthY, principalX, principalY,
|
||||
zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from camera coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="x">X in camera coordinates</param>
|
||||
/// <param name="y">Y in camera coordinates</param>
|
||||
/// <param name="z">Z in camera coordinates</param>
|
||||
/// <param name="focalLength">Normalized focal lengths in NDC space</param>
|
||||
/// <param name="principalPoint">Normalized principal point in NDC space</param>
|
||||
/// <param name="zScale">Ratio of Z values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 CameraToPoint(UnityEngine.Rect rectangle, float x, float y, float z,
|
||||
Vector2 focalLength, Vector2 principalPoint,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, focalLength.x, focalLength.y, principalPoint.x, principalPoint.y,
|
||||
zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from camera coordinates to local coordinates in Unity.
|
||||
/// It is assumed that the principal point is (0, 0) in the camera coordinate system and the focal length is (1, 1).
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="x">X in camera coordinates</param>
|
||||
/// <param name="y">Y in camera coordinates</param>
|
||||
/// <param name="z">Z in camera coordinates</param>
|
||||
/// <param name="zScale">Ratio of Z values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 CameraToPoint(UnityEngine.Rect rectangle, float x, float y, float z,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, Vector2.one, Vector2.zero, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="point3d" /> in local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="focalLengthX">Normalized focal length X in NDC space</param>
|
||||
/// <param name="focalLengthY">Normalized focal length Y in NDC space</param>
|
||||
/// <param name="principalX">Normalized principal point X in NDC space</param>
|
||||
/// <param name="principalY">Normalized principal point Y in NDC space</param>
|
||||
/// <param name="zScale">Ratio of values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 GetPoint(this UnityEngine.Rect rectangle, Point3D point3d, float focalLengthX, float focalLengthY, float principalX, float principalY,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToPoint(rectangle, point3d.X, point3d.Y, point3d.Z, focalLengthX, focalLengthY, principalX, principalY, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="point3d" /> in local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="focalLength">Normalized focal lengths in NDC space</param>
|
||||
/// <param name="principalPoint">Normalized principal point in NDC space</param>
|
||||
/// <param name="zScale">Ratio of values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 GetPoint(this UnityEngine.Rect rectangle, Point3D point3d, Vector2 focalLength, Vector2 principalPoint,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToPoint(rectangle, point3d.X, point3d.Y, point3d.Z, focalLength, principalPoint, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="point3d" /> in local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="zScale">Ratio of values in camera coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 GetPoint(this UnityEngine.Rect rectangle, Point3D point3d,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return CameraToPoint(rectangle, point3d.X, point3d.Y, point3d.Z, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
public static Quaternion GetApproximateQuaternion(ObjectAnnotation objectAnnotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var isXReversed = IsXReversed(imageRotation, isMirrored);
|
||||
var isYReversed = IsYReversed(imageRotation, isMirrored);
|
||||
var forward = GetZDir(objectAnnotation, isXReversed, isYReversed, isInverted);
|
||||
var upward = GetYDir(objectAnnotation, isXReversed, isYReversed, isInverted);
|
||||
|
||||
return Quaternion.LookRotation(forward, upward);
|
||||
}
|
||||
|
||||
public static (Vector3, Vector3, Vector3) GetDirections(ObjectAnnotation objectAnnotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var isXReversed = IsXReversed(imageRotation, isMirrored);
|
||||
var isYReversed = IsYReversed(imageRotation, isMirrored);
|
||||
var scale = objectAnnotation.Scale;
|
||||
var xDir = scale[0] * GetXDir(objectAnnotation, isXReversed, isYReversed, isInverted);
|
||||
var yDir = scale[1] * GetYDir(objectAnnotation, isXReversed, isYReversed, isInverted);
|
||||
var zDir = scale[2] * GetZDir(objectAnnotation, isXReversed, isYReversed, isInverted);
|
||||
return (xDir, yDir, zDir);
|
||||
}
|
||||
|
||||
private static Vector3 GetXDir(ObjectAnnotation objectAnnotation, bool isXReversed, bool isYReversed, bool isInverted)
|
||||
{
|
||||
var points = objectAnnotation.Keypoints;
|
||||
var v1 = GetDirection(points[1].Point3D, points[5].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v2 = GetDirection(points[2].Point3D, points[6].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v3 = GetDirection(points[3].Point3D, points[7].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v4 = GetDirection(points[4].Point3D, points[8].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
return (v1 + v2 + v3 + v4) / 4;
|
||||
}
|
||||
|
||||
private static Vector3 GetYDir(ObjectAnnotation objectAnnotation, bool isXReversed, bool isYReversed, bool isInverted)
|
||||
{
|
||||
var points = objectAnnotation.Keypoints;
|
||||
var v1 = GetDirection(points[1].Point3D, points[3].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v2 = GetDirection(points[2].Point3D, points[4].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v3 = GetDirection(points[5].Point3D, points[7].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v4 = GetDirection(points[6].Point3D, points[8].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
return (v1 + v2 + v3 + v4) / 4;
|
||||
}
|
||||
|
||||
private static Vector3 GetZDir(ObjectAnnotation objectAnnotation, bool isXReversed, bool isYReversed, bool isInverted)
|
||||
{
|
||||
var points = objectAnnotation.Keypoints;
|
||||
var v1 = GetDirection(points[2].Point3D, points[1].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v2 = GetDirection(points[4].Point3D, points[3].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v3 = GetDirection(points[6].Point3D, points[5].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
var v4 = GetDirection(points[8].Point3D, points[7].Point3D, isXReversed, isYReversed, isInverted).normalized;
|
||||
return (v1 + v2 + v3 + v4) / 4;
|
||||
}
|
||||
|
||||
private static Vector3 GetDirection(Point3D from, Point3D to, bool isXReversed, bool isYReversed, bool isInverted)
|
||||
{
|
||||
var xDiff = to.X - from.X;
|
||||
var yDiff = to.Y - from.Y;
|
||||
var (xDir, yDir) = isInverted ? (yDiff, xDiff) : (xDiff, yDiff);
|
||||
// convert from right-handed to left-handed
|
||||
return new Vector3(isXReversed ? -xDir : xDir, isYReversed ? -yDir : yDir, from.Z - to.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates.
|
||||
/// For example, if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation180" /> and <paramRef name="isMirrored" /> is <c>false</c>, this returns <c>true</c>
|
||||
/// because the original Y axis will be exactly opposite the X axis in Unity coordinates if the image is rotated back.
|
||||
/// </summary>
|
||||
public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = false)
|
||||
{
|
||||
return isMirrored ?
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation270 :
|
||||
rotationAngle == RotationAngle.Rotation180 || rotationAngle == RotationAngle.Rotation270;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates.
|
||||
/// For example, if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation180" /> and <paramRef name="isMirrored" /> is <c>false</c>, this returns <c>true</c>
|
||||
/// because the original X axis will be exactly opposite the Y axis in Unity coordinates if the image is rotated back.
|
||||
/// </summary>
|
||||
public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = false)
|
||||
{
|
||||
return isMirrored ?
|
||||
rotationAngle == RotationAngle.Rotation180 || rotationAngle == RotationAngle.Rotation270 :
|
||||
rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation180;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> or <see cref="RotationAngle.Rotation270" />.
|
||||
/// </summary>
|
||||
public static bool IsInverted(RotationAngle rotationAngle)
|
||||
{
|
||||
return rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation270;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92d2557dc55654e76b24cf607ac18146
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,676 @@
|
||||
// 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 mplt = Mediapipe.LocationData.Types;
|
||||
|
||||
namespace Mediapipe.Unity.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides helper methods for converting from image coordinate values to local coordinate values in Unity, and vice versa.
|
||||
/// </summary>
|
||||
public static class ImageCoordinate
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert from image coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="x">Column value in the image coordinate system</param>
|
||||
/// <param name="y">Row value in the image coordinate system</param>
|
||||
/// <param name="z">Depth value in the local coordinate system</param>
|
||||
/// <param name="screenWidth">
|
||||
/// The target screen width. The returned value will be local to this screen.
|
||||
/// </param>
|
||||
/// <param name="xMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="xMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the image coordinates is mirrored</param>
|
||||
public static Vector3 ImageToLocalPoint(int x, int y, int z, float xMin, float xMax, float yMin, float yMax,
|
||||
int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var (rectX, rectY) = isInverted ? (y, x) : (x, y);
|
||||
var (width, height) = (xMax - xMin, yMax - yMin);
|
||||
var localX = ((IsXReversed(imageRotation, isMirrored) ? imageWidth - rectX : rectX) * width / imageWidth) + xMin;
|
||||
var localY = ((IsYReversed(imageRotation, isMirrored) ? imageHeight - rectY : rectY) * height / imageHeight) + yMin;
|
||||
return new Vector3(localX, localY, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from image coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="x">Column value in the image coordinate system</param>
|
||||
/// <param name="y">Row value in the image coordinate system</param>
|
||||
/// <param name="z">Depth value in the local coordinate system</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the image coordinates is mirrored</param>
|
||||
public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, int x, int y, int z,
|
||||
int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToLocalPoint(x, y, z, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from image coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="position">
|
||||
/// The position in the image coordinate system.
|
||||
/// If <c>position.z</c> is not zero, it's assumed to be the depth value in the local coordinate system.
|
||||
/// </param>
|
||||
/// <param name="imageSize">Image size in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the image coordinates is mirrored</param>
|
||||
public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, Vector3Int position,
|
||||
Vector2Int imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToPoint(rectangle, position.x, position.y, position.z, imageSize.x, imageSize.y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from image coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="x">Column value in the image coordinate system</param>
|
||||
/// <param name="y">Row value in the image coordinate system</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the image coordinates is mirrored</param>
|
||||
public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, int x, int y,
|
||||
int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToLocalPoint(x, y, 0, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from image coordinates to local coordinates in Unity.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="position">The position in the image coordinate system</param>
|
||||
/// <param name="imageSize">Image size in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the image coordinates is mirrored</param>
|
||||
public static Vector3 ImageToPoint(UnityEngine.Rect rectangle, Vector2Int position, Vector2Int imageSize,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToPoint(rectangle, position.x, position.y, imageSize.x, imageSize.y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="normalizedX">Normalized x value in the image coordinate system</param>
|
||||
/// <param name="normalizedY">Normalized y value in the image coordinate system</param>
|
||||
/// <param name="normalizedZ">Normalized z value in the image coordinate system</param>
|
||||
/// <param name="xMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="xMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="zScale">Ratio of Z value in image coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToLocalPoint(float normalizedX, float normalizedY, float normalizedZ, float xMin, float xMax, float yMin, float yMax,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var (nx, ny) = isInverted ? (normalizedY, normalizedX) : (normalizedX, normalizedY);
|
||||
var x = IsXReversed(imageRotation, isMirrored) ? Mathf.LerpUnclamped(xMax, xMin, nx) : Mathf.LerpUnclamped(xMin, xMax, nx);
|
||||
var y = IsYReversed(imageRotation, isMirrored) ? Mathf.LerpUnclamped(yMax, yMin, ny) : Mathf.LerpUnclamped(yMin, yMax, ny);
|
||||
var z = zScale * normalizedZ;
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="normalizedX">Normalized x value in the image coordinate system</param>
|
||||
/// <param name="normalizedY">Normalized y value in the image coordinate system</param>
|
||||
/// <param name="normalizedZ">Normalized z value in the image coordinate system</param>
|
||||
/// <param name="zScale">Ratio of Z value in image coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, float normalizedX, float normalizedY, float normalizedZ,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToLocalPoint(normalizedX, normalizedY, normalizedZ, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="position">The position in the image coordinate system</param>
|
||||
/// <param name="zScale">Ratio of Z value in image coordinates to local coordinates in Unity</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, Vector3 position,
|
||||
float zScale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, position.x, position.y, position.z, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="normalizedX">Normalized x value in the image coordinate system</param>
|
||||
/// <param name="normalizedY">Normalized y value in the image coordinate system</param>
|
||||
/// <param name="normalizedZ">Normalized z value in the image coordinate system</param>
|
||||
/// <param name="xMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="xMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMin">The minimum X coordinate of the target rectangle</param>
|
||||
/// <param name="yMax">The maximum X coordinate of the target rectangle</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToLocalPoint(float normalizedX, float normalizedY, float normalizedZ, float xMin, float xMax, float yMin, float yMax,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
// Z usually uses roughly the same scale as X
|
||||
var zScale = IsInverted(imageRotation) ? (yMax - yMin) : (xMax - xMin);
|
||||
return ImageNormalizedToLocalPoint(normalizedX, normalizedY, normalizedZ, xMin, xMax, yMin, yMax, zScale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="normalizedX">Normalized x value in the image coordinate system</param>
|
||||
/// <param name="normalizedY">Normalized y value in the image coordinate system</param>
|
||||
/// <param name="normalizedZ">Normalized z value in the image coordinate system</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, float normalizedX, float normalizedY, float normalizedZ,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToLocalPoint(normalizedX, normalizedY, normalizedZ, rectangle.xMin, rectangle.xMax, rectangle.yMin, rectangle.yMax, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="position">The position in the image coordinate system</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, Vector3 position, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, position.x, position.y, position.z, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="normalizedX">Normalized x value in the image coordinate system</param>
|
||||
/// <param name="normalizedY">Normalized y value in the image coordinate system</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, float normalizedX, float normalizedY,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, normalizedX, normalizedY, 0, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert normalized values in the image coordinate system to local coordinate values in Unity.
|
||||
/// If the normalized values are out of [0, 1], the return value will be off the target screen.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="position">The position in the image coordinate system</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 ImageNormalizedToPoint(UnityEngine.Rect rectangle, Vector2 position, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, position.x, position.y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector3" /> array which represents a rectangle's vertices.
|
||||
/// They are in clockwise order, starting from the coordinate that was in the bottom-left.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Z values are always zero.
|
||||
/// </remarks>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="xMin">Leftmost X value</param>
|
||||
/// <param name="yMin">Topmost Y value</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xMin, int yMin, int width, int height,
|
||||
int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var p = ImageToPoint(rectangle, xMin, yMin + height, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
var q = ImageToPoint(rectangle, xMin + width, yMin, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
return GetRectVertices(p, q);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector3" /> array which represents a rectangle's vertices.
|
||||
/// They are in clockwise order, starting from the coordinate that was in the bottom-left.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Z values are always zero.
|
||||
/// </remarks>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="xMin">Leftmost X value</param>
|
||||
/// <param name="yMin">Topmost Y value</param>
|
||||
/// <param name="imageSize">Image size in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xMin, int yMin, int width, int height,
|
||||
Vector2Int imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToRectVertices(rectangle, xMin, yMin, width, height, imageSize.x, imageSize.y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector3" /> array which represents a rectangle's vertices.
|
||||
/// They are in clockwise order, starting from the coordinate that was in the bottom-left.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Z values are always zero.
|
||||
/// </remarks>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="normalizedXMin">Normalized leftmost X value</param>
|
||||
/// <param name="normalizedYMin">Normalized topmost Y value</param>
|
||||
/// <param name="normalizedWidth">Normalized width</param>
|
||||
/// <param name="normalizedHeight">Normalized height</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] ImageNormalizedToRectVertices(UnityEngine.Rect rectangle, float normalizedXMin, float normalizedYMin, float normalizedWidth, float normalizedHeight,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var p = ImageNormalizedToPoint(rectangle, normalizedXMin, normalizedYMin + normalizedHeight, imageRotation, isMirrored);
|
||||
var q = ImageNormalizedToPoint(rectangle, normalizedXMin + normalizedWidth, normalizedYMin, imageRotation, isMirrored);
|
||||
return GetRectVertices(p, q);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector3" /> array which represents a rectangle's vertices.
|
||||
/// They are in clockwise order, starting from the coordinate that was in the bottom-left before the rotation.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="xCenter">X value of the rectangle's center coordinate</param>
|
||||
/// <param name="yCenter">Y value of the rectangle's center coordinate</param>
|
||||
/// <param name="rotation">Clockwise rotation angle in radians</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xCenter, int yCenter, int width, int height, float rotation,
|
||||
int imageWidth, int imageHeight, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var (rectWidth, rectHeight) = IsInverted(imageRotation) ? (height, width) : (width, height);
|
||||
|
||||
var center = ImageToPoint(rectangle, xCenter, yCenter, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
var isRotationReversed = isInverted ^ IsXReversed(imageRotation, isMirrored) ^ IsYReversed(imageRotation, isMirrored);
|
||||
var quaternion = Quaternion.Euler(0, 0, (isRotationReversed ? -1 : 1) * Mathf.Rad2Deg * rotation);
|
||||
|
||||
var bottomLeftRel = quaternion * new Vector3(-rectWidth / 2, -rectHeight / 2, 0);
|
||||
var topLeftRel = quaternion * new Vector3(-rectWidth / 2, rectHeight / 2, 0);
|
||||
|
||||
return new Vector3[] {
|
||||
center + bottomLeftRel,
|
||||
center + topLeftRel,
|
||||
center - bottomLeftRel,
|
||||
center - topLeftRel,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector3" /> array which represents a rectangle's vertices.
|
||||
/// They are in clockwise order, starting from the coordinate that was in the bottom-left before the rotation.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="xCenter">X value of the rectangle's center coordinate</param>
|
||||
/// <param name="yCenter">Y value of the rectangle's center coordinate</param>
|
||||
/// <param name="rotation">Clockwise rotation angle in radians</param>
|
||||
/// <param name="imageSize">Image size in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] ImageToRectVertices(UnityEngine.Rect rectangle, int xCenter, int yCenter, int width, int height, float rotation,
|
||||
Vector2Int imageSize, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToRectVertices(rectangle, xCenter, yCenter, width, height, rotation, imageSize.x, imageSize.y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Vector3" /> array which represents a rectangle's vertices.
|
||||
/// They are in clockwise order, starting from the coordinate that was in the bottom-left before the rotation.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="normalizedXCenter">X value of the rectangle's center coordinate</param>
|
||||
/// <param name="normalizedYCenter">Y value of the rectangle's center coordinate</param>
|
||||
/// <param name="normalizedWidth">Normalized width</param>
|
||||
/// <param name="normalizedHeight">Normalized height</param>
|
||||
/// <param name="rotation">Clockwise rotation angle in radians</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] ImageNormalizedToRectVertices(UnityEngine.Rect rectangle, float normalizedXCenter, float normalizedYCenter, float normalizedWidth, float normalizedHeight,
|
||||
float rotation, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var isInverted = IsInverted(imageRotation);
|
||||
var width = rectangle.width * (isInverted ? normalizedHeight : normalizedWidth);
|
||||
var height = rectangle.height * (isInverted ? normalizedWidth : normalizedHeight);
|
||||
|
||||
var center = ImageNormalizedToPoint(rectangle, normalizedXCenter, normalizedYCenter, imageRotation, isMirrored);
|
||||
var isRotationReversed = isInverted ^ IsXReversed(imageRotation, isMirrored) ^ IsYReversed(imageRotation, isMirrored);
|
||||
var quaternion = Quaternion.Euler(0, 0, (isRotationReversed ? -1 : 1) * Mathf.Rad2Deg * rotation);
|
||||
|
||||
var bottomLeftRel = quaternion * new Vector3(-width / 2, -height / 2, 0);
|
||||
var topLeftRel = quaternion * new Vector3(-width / 2, height / 2, 0);
|
||||
|
||||
return new Vector3[] {
|
||||
center + bottomLeftRel,
|
||||
center + topLeftRel,
|
||||
center - bottomLeftRel,
|
||||
center - topLeftRel,
|
||||
};
|
||||
}
|
||||
|
||||
private static Vector3[] GetRectVertices(Vector2 p, Vector2 q)
|
||||
{
|
||||
var leftX = Mathf.Min(p.x, q.x);
|
||||
var rightX = Mathf.Max(p.x, q.x);
|
||||
var bottomY = Mathf.Min(p.y, q.y);
|
||||
var topY = Mathf.Max(p.y, q.y);
|
||||
|
||||
var bottomLeft = new Vector3(leftX, bottomY);
|
||||
var topLeft = new Vector3(leftX, topY);
|
||||
var topRight = new Vector3(rightX, topY);
|
||||
var bottomRight = new Vector3(rightX, bottomY);
|
||||
|
||||
return new Vector3[] { bottomLeft, topLeft, topRight, bottomRight };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="relativeKeypoint" /> in the local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector2 GetPoint(this UnityEngine.Rect rectangle, mplt.RelativeKeypoint relativeKeypoint,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, relativeKeypoint.X, relativeKeypoint.Y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="normalizedLandmark" /> in the local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 GetPoint(this UnityEngine.Rect rectangle, NormalizedLandmark normalizedLandmark,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, normalizedLandmark.X, normalizedLandmark.Y, normalizedLandmark.Z, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="point2d" /> in the local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 GetPoint(this UnityEngine.Rect rectangle, NormalizedPoint2D point2d, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, point2d.X, point2d.Y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="anchor3d" /> in the local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector2 GetPoint(this UnityEngine.Rect rectangle, Anchor3d anchor3d, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToPoint(rectangle, anchor3d.x, anchor3d.y, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Vector3 array which represents <paramref name="boundingBox" />'s vertex coordinates in the local coordinate system.
|
||||
/// They are ordered clockwise from bottom-left point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, mplt.BoundingBox boundingBox, int imageWidth, int imageHeight,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToRectVertices(rectangle, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Vector3 array which represents <paramref name="boundingBox" />'s vertex coordinates in the local coordinate system.
|
||||
/// They are ordered clockwise from bottom-left point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageSize">Image size in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, mplt.BoundingBox boundingBox, Vector2Int imageSize,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToRectVertices(rectangle, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageSize, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Vector3 array which represents <paramref name="boundingBox" />'s vertex coordinates in the local coordinate system.
|
||||
/// They are ordered clockwise from bottom-left point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, mplt.RelativeBoundingBox boundingBox,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToRectVertices(rectangle, boundingBox.Xmin, boundingBox.Ymin, boundingBox.Width, boundingBox.Height, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Vector3 array which represents <paramref name="rect" />'s vertex coordinates in the local coordinate system.
|
||||
/// They are ordered clockwise from bottom-left point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageWidth">Image width in pixels</param>
|
||||
/// <param name="imageHeight">Image width in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, Rect rect, int imageWidth, int imageHeight,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToRectVertices(rectangle, rect.XCenter, rect.YCenter, rect.Width, rect.Height, rect.Rotation, imageWidth, imageHeight, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Vector3 array which represents <paramref name="rect" />'s vertex coordinates in the local coordinate system.
|
||||
/// They are ordered clockwise from bottom-left point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageSize">Image size in pixels</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, Rect rect, Vector2Int imageSize,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageToRectVertices(rectangle, rect.XCenter, rect.YCenter, rect.Width, rect.Height, rect.Rotation, imageSize, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Vector3 array which represents <paramref name="normalizedRect" />'s vertex coordinates in the local coordinate system.
|
||||
/// They are ordered clockwise from bottom-left point.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3[] GetRectVertices(this UnityEngine.Rect rectangle, NormalizedRect normalizedRect,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return ImageNormalizedToRectVertices(rectangle, normalizedRect.XCenter, normalizedRect.YCenter, normalizedRect.Width, normalizedRect.Height, normalizedRect.Rotation, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the image normalized point corresponding to <paramref name="localPosition" />.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">Rectangle to get a point inside</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector2 PointToImageNormalized(this UnityEngine.Rect rectangle, Vector2 localPosition, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var normalizedX = IsXReversed(imageRotation, isMirrored) ?
|
||||
Mathf.InverseLerp(rectangle.width / 2, -rectangle.width / 2, localPosition.x) :
|
||||
Mathf.InverseLerp(-rectangle.width / 2, rectangle.width / 2, localPosition.x);
|
||||
var normalizedY = IsYReversed(imageRotation, isMirrored) ?
|
||||
Mathf.InverseLerp(rectangle.height / 2, -rectangle.height / 2, localPosition.y) :
|
||||
Mathf.InverseLerp(-rectangle.height / 2, rectangle.height / 2, localPosition.y);
|
||||
return IsInverted(imageRotation) ? new Vector2(normalizedY, normalizedX) : new Vector2(normalizedX, normalizedY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates.
|
||||
/// For example, if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> and <paramRef name="isMirrored" /> is <c>false</c>, this returns <c>true</c>
|
||||
/// because the original Y axis will be exactly opposite the X axis in Unity coordinates if the image is rotated back.
|
||||
/// </summary>
|
||||
public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = false)
|
||||
{
|
||||
return isMirrored ?
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation90 :
|
||||
rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation180;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates.
|
||||
/// For example, if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> and <paramRef name="isMirrored" /> is <c>false</c>, this returns <c>true</c>
|
||||
/// because the original X axis will be exactly opposite the Y axis in Unity coordinates if the image is rotated back.
|
||||
/// </summary>
|
||||
public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = false)
|
||||
{
|
||||
return isMirrored ?
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation270 :
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation90;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> or <see cref="RotationAngle.Rotation270" />.
|
||||
/// </summary>
|
||||
public static bool IsInverted(RotationAngle rotationAngle)
|
||||
{
|
||||
return rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation270;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0e382e967c8301cfb71c0baf698cb45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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.CoordinateSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides helper methods for converting real-world coordinate values to local coordinate values.
|
||||
/// In real-world coordinate system, X axis is toward the right, Y axis is toward the bottom, and Z axis is toward the back.
|
||||
/// </summary>
|
||||
public static class RealWorldCoordinate
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert from real world coordinates to Unity local coordinates.
|
||||
/// Assume that the origin is common to the two coordinate systems.
|
||||
/// </summary>
|
||||
/// <param name="x">X in real world coordinates</param>
|
||||
/// <param name="y">Y in real world coordinates</param>
|
||||
/// <param name="z">Z in real world coordinates</param>
|
||||
/// <param name="scale">Ratio of real world coordinate values to local coordinate values</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 RealWorldToLocalPoint(float x, float y, float z, Vector3 scale,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
var (rx, ry) = IsInverted(imageRotation) ? (y, x) : (x, y);
|
||||
var realX = IsXReversed(imageRotation, isMirrored) ? -rx : rx;
|
||||
var realY = IsYReversed(imageRotation, isMirrored) ? -ry : ry;
|
||||
return Vector3.Scale(new Vector3(realX, realY, z), scale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from real world coordinates to Unity local coordinates.
|
||||
/// Assume that the origin is common to the two coordinate systems.
|
||||
/// </summary>
|
||||
/// <param name="x">X in real world coordinates</param>
|
||||
/// <param name="y">Y in real world coordinates</param>
|
||||
/// <param name="z">Z in real world coordinates</param>
|
||||
/// <param name="scaleX">Ratio of real world coordinate X to local coordinate X</param>
|
||||
/// <param name="scaleY">Ratio of real world coordinate Y to local coordinate Y</param>
|
||||
/// <param name="scaleZ">Ratio of real world coordinate Z to local coordinate Z</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 RealWorldToLocalPoint(float x, float y, float z, float scaleX = 1, float scaleY = 1, float scaleZ = 1,
|
||||
RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return RealWorldToLocalPoint(x, y, z, new Vector3(scaleX, scaleY, scaleZ), imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the coordinates represented by <paramref name="landmark" /> in the local coordinate system.
|
||||
/// </summary>
|
||||
/// <param name="scale">Ratio of real world coordinate values to local coordinate values</param>
|
||||
/// <param name="imageRotation">
|
||||
/// Counterclockwise rotation angle of the input image in the image coordinate system.
|
||||
/// In the local coordinate system, this value will often represent a clockwise rotation angle.
|
||||
/// </param>
|
||||
/// <param name="isMirrored">Set to true if the original coordinates is mirrored</param>
|
||||
public static Vector3 GetPoint(this UnityEngine.Rect _, Landmark landmark, Vector3 scale, RotationAngle imageRotation = RotationAngle.Rotation0, bool isMirrored = false)
|
||||
{
|
||||
return RealWorldToLocalPoint(landmark.X, landmark.Y, landmark.Z, scale, imageRotation, isMirrored);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates.
|
||||
/// For example, if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> and <paramRef name="isMirrored" /> is <c>false</c>, this returns <c>true</c>
|
||||
/// because the original Y axis will be exactly opposite the X axis in Unity coordinates if the image is rotated back.
|
||||
/// </summary>
|
||||
public static bool IsXReversed(RotationAngle rotationAngle, bool isMirrored = false)
|
||||
{
|
||||
return isMirrored ?
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation90 :
|
||||
rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation180;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the image is rotated back, returns whether the axis parallel to the X axis of the Unity coordinates is pointing in the same direction as the X axis of the Unity coordinates.
|
||||
/// For example, if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> and <paramRef name="isMirrored" /> is <c>false</c>, this returns <c>true</c>
|
||||
/// because the original X axis will be exactly opposite the Y axis in Unity coordinates if the image is rotated back.
|
||||
/// </summary>
|
||||
public static bool IsYReversed(RotationAngle rotationAngle, bool isMirrored = false)
|
||||
{
|
||||
return isMirrored ?
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation270 :
|
||||
rotationAngle == RotationAngle.Rotation0 || rotationAngle == RotationAngle.Rotation90;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if <paramref name="rotationAngle" /> is <see cref="RotationAngle.Rotation90" /> or <see cref="RotationAngle.Rotation270" />.
|
||||
/// </summary>
|
||||
public static bool IsInverted(RotationAngle rotationAngle)
|
||||
{
|
||||
return rotationAngle == RotationAngle.Rotation90 || rotationAngle == RotationAngle.Rotation270;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bd1bbb8c8056065c940c7d9d211b3d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b608fd45c3e23274082f4cd4919c23f4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 pb = Google.Protobuf;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public static class CalculatorGraphConfigExtension
|
||||
{
|
||||
public static string AddPacketPresenceCalculator(this CalculatorGraphConfig config, string outputStreamName)
|
||||
{
|
||||
var presenceStreamName = Tool.GetUnusedStreamName(config, $"{outputStreamName}_presence");
|
||||
var packetPresenceCalculatorNode = new CalculatorGraphConfig.Types.Node()
|
||||
{
|
||||
Calculator = "PacketPresenceCalculator"
|
||||
};
|
||||
packetPresenceCalculatorNode.InputStream.Add($"PACKET:{outputStreamName}");
|
||||
packetPresenceCalculatorNode.OutputStream.Add($"PRESENCE:{presenceStreamName}");
|
||||
|
||||
config.Node.Add(packetPresenceCalculatorNode);
|
||||
return presenceStreamName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfad83caa3e9e0012b327e6409a19937
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,936 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public static class ImageFrameExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Read the specific channel data only.
|
||||
/// It's useful when only one channel is used (e.g. Hair Segmentation mask).
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the channel data is read successfully; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="channelNumber">
|
||||
/// Specify from which channel (0-indexed) the data will be retrieved.
|
||||
/// For example, if the format is RGB, 0 means R channel, 1 means G channel, and 2 means B channel.
|
||||
/// </param>
|
||||
/// <param name="channelData" >
|
||||
/// The array to which the output data will be written.
|
||||
/// </param>
|
||||
/// <param name="isHorizontallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped horizontally.
|
||||
/// </param>
|
||||
/// <param name="isVerticallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped vertically.
|
||||
/// </param>
|
||||
public static bool TryReadChannel(this ImageFrame imageFrame, int channelNumber, byte[] channelData, bool isHorizontallyFlipped = false, bool isVerticallyFlipped = false)
|
||||
{
|
||||
var format = imageFrame.Format();
|
||||
var channelCount = ImageFrame.NumberOfChannelsForFormat(format);
|
||||
if (!IsChannelNumberValid(channelCount, channelNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Types.Format.Srgb:
|
||||
case ImageFormat.Types.Format.Srgba:
|
||||
case ImageFormat.Types.Format.Sbgra:
|
||||
case ImageFormat.Types.Format.Gray8:
|
||||
case ImageFormat.Types.Format.Lab8:
|
||||
return TryReadChannel(imageFrame, channelCount, channelNumber, channelData, isHorizontallyFlipped, isVerticallyFlipped);
|
||||
default:
|
||||
Logger.LogWarning("The channel data is not stored in bytes");
|
||||
return false;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the specific channel data only.
|
||||
/// It's useful when only one channel is used (e.g. Hair Segmentation mask).
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the channel data is read successfully; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="channelNumber">
|
||||
/// Specify from which channel (0-indexed) the data will be retrieved.
|
||||
/// For example, if the format is RGB, 0 means R channel, 1 means G channel, and 2 means B channel.
|
||||
/// </param>
|
||||
/// <param name="channelData" >
|
||||
/// The array to which the output data will be written.
|
||||
/// </param>
|
||||
/// <param name="isHorizontallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped horizontally.
|
||||
/// </param>
|
||||
/// <param name="isVerticallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped vertically.
|
||||
/// </param>
|
||||
public static bool TryReadChannel(this ImageFrame imageFrame, int channelNumber, ushort[] channelData, bool isHorizontallyFlipped = false, bool isVerticallyFlipped = false)
|
||||
{
|
||||
var format = imageFrame.Format();
|
||||
var channelCount = ImageFrame.NumberOfChannelsForFormat(format);
|
||||
if (!IsChannelNumberValid(channelCount, channelNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Types.Format.Srgb48:
|
||||
case ImageFormat.Types.Format.Srgba64:
|
||||
case ImageFormat.Types.Format.Gray16:
|
||||
return TryReadChannel(imageFrame, channelCount, channelNumber, channelData, isHorizontallyFlipped, isVerticallyFlipped);
|
||||
default:
|
||||
Logger.LogWarning("The channel data is not stored in ushorts");
|
||||
return false;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the specific channel data only.
|
||||
/// It's useful when only one channel is used (e.g. Selfie Segmentation mask).
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the channel data is read successfully; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="channelNumber">
|
||||
/// Specify from which channel (0-indexed) the data will be retrieved.
|
||||
/// </param>
|
||||
/// <param name="channelData" >
|
||||
/// The array to which the output data will be written.
|
||||
/// </param>
|
||||
/// <param name="isHorizontallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped horizontally.
|
||||
/// </param>
|
||||
/// <param name="isVerticallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped vertically.
|
||||
/// </param>
|
||||
public static bool TryReadChannel(this ImageFrame imageFrame, int channelNumber, float[] channelData, bool isHorizontallyFlipped = false, bool isVerticallyFlipped = false)
|
||||
{
|
||||
var format = imageFrame.Format();
|
||||
var channelCount = ImageFrame.NumberOfChannelsForFormat(format);
|
||||
if (!IsChannelNumberValid(channelCount, channelNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Types.Format.Vec32F1:
|
||||
case ImageFormat.Types.Format.Vec32F2:
|
||||
return TryReadChannel(imageFrame, channelCount, channelNumber, channelData, isHorizontallyFlipped, isVerticallyFlipped);
|
||||
default:
|
||||
Logger.LogWarning("The channel data is not stored in floats");
|
||||
return false;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the specific channel data only.
|
||||
/// Each value in <paramref name="normalizedChannelData" /> will be normalized to [0.0, 1.0].
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the channel data is read successfully; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
/// <param name="channelNumber">
|
||||
/// Specify from which channel (0-indexed) the data will be retrieved.
|
||||
/// </param>
|
||||
/// <param name="channelData" >
|
||||
/// The array to which the output data will be written.
|
||||
/// </param>
|
||||
/// <param name="isHorizontallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped horizontally.
|
||||
/// </param>
|
||||
/// <param name="isVerticallyFlipped">
|
||||
/// Set <c>true</c> if the <paramref name="imageFrame" /> is flipped vertically.
|
||||
/// </param>
|
||||
public static bool TryReadChannelNormalized(this ImageFrame imageFrame, int channelNumber, float[] normalizedChannelData, bool isHorizontallyFlipped = false, bool isVerticallyFlipped = false)
|
||||
{
|
||||
var format = imageFrame.Format();
|
||||
var channelCount = ImageFrame.NumberOfChannelsForFormat(format);
|
||||
if (!IsChannelNumberValid(channelCount, channelNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0010
|
||||
switch (format)
|
||||
{
|
||||
case ImageFormat.Types.Format.Srgb:
|
||||
case ImageFormat.Types.Format.Srgba:
|
||||
case ImageFormat.Types.Format.Sbgra:
|
||||
case ImageFormat.Types.Format.Gray8:
|
||||
case ImageFormat.Types.Format.Lab8:
|
||||
return TryReadChannel<byte, float>(imageFrame, channelCount, channelNumber, ByteNormalizer, normalizedChannelData, isHorizontallyFlipped, isVerticallyFlipped);
|
||||
case ImageFormat.Types.Format.Srgb48:
|
||||
case ImageFormat.Types.Format.Srgba64:
|
||||
case ImageFormat.Types.Format.Gray16:
|
||||
return TryReadChannel<ushort, float>(imageFrame, channelCount, channelNumber, UshortNormalizer, normalizedChannelData, isHorizontallyFlipped, isVerticallyFlipped);
|
||||
case ImageFormat.Types.Format.Vec32F1:
|
||||
case ImageFormat.Types.Format.Vec32F2:
|
||||
return TryReadChannel(imageFrame, channelCount, channelNumber, normalizedChannelData, isHorizontallyFlipped, isVerticallyFlipped);
|
||||
default:
|
||||
Logger.LogWarning("Channels don't make sense in the current context");
|
||||
return false;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
|
||||
public static bool TryReadPixelData(this ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
#pragma warning disable IDE0010
|
||||
switch (imageFrame.Format())
|
||||
{
|
||||
case ImageFormat.Types.Format.Srgb:
|
||||
return TryReadSrgb(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Srgba:
|
||||
return TryReadSrgba(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Sbgra:
|
||||
return TryReadSbgra(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Gray8:
|
||||
return TryReadGray8(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Lab8:
|
||||
return TryReadLab8(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Srgb48:
|
||||
return TryReadSrgb48(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Srgba64:
|
||||
return TryReadSrgba64(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Gray16:
|
||||
return TryReadGray16(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Vec32F1:
|
||||
return TryReadVec32f1(imageFrame, colors);
|
||||
case ImageFormat.Types.Format.Vec32F2:
|
||||
return TryReadVec32f2(imageFrame, colors);
|
||||
default:
|
||||
Logger.LogWarning("Channels don't make sense in the current context");
|
||||
return false;
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadChannel<T>(ImageFrame imageFrame, int channelCount, int channelNumber, T[] channelData, bool isHorizontallyFlipped, bool isVerticallyFlipped) where T : unmanaged
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (channelData.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of channelData ({channelData.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (T* dest = channelData)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
pLine += byteDepth * channelNumber;
|
||||
|
||||
if (isVerticallyFlipped)
|
||||
{
|
||||
if (isHorizontallyFlipped)
|
||||
{
|
||||
// The first element is at bottom-right.
|
||||
var pDest = dest + width - 1;
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (T*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest-- = *pSrc;
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest += 2 * width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first element is at bottom-left.
|
||||
// NOTE: In the Unity coordinate system, the image can be considered as not flipped.
|
||||
var pDest = dest;
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (T*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest++ = *pSrc;
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isHorizontallyFlipped)
|
||||
{
|
||||
// The first element is at top-right.
|
||||
var pDest = dest + length - 1;
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (T*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest-- = *pSrc;
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (T*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest++ = *pSrc;
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private delegate TDst ChannelTransformer<TSrc, TDst>(TSrc channel);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool TryReadChannel<TSrc, TDst>(ImageFrame imageFrame, int channelCount, int channelNumber, ChannelTransformer<TSrc, TDst> transformer,
|
||||
TDst[] channelData, bool isHorizontallyFlipped, bool isVerticallyFlipped) where TSrc : unmanaged where TDst : unmanaged
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (channelData.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of channelData ({channelData.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (TDst* dest = channelData)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
pLine += byteDepth * channelNumber;
|
||||
|
||||
if (isVerticallyFlipped)
|
||||
{
|
||||
if (isHorizontallyFlipped)
|
||||
{
|
||||
// The first element is at bottom-right.
|
||||
var pDest = dest + width - 1;
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (TSrc*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest-- = transformer(*pSrc);
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest += 2 * width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first element is at bottom-left.
|
||||
// NOTE: In the Unity coordinate system, the image can be considered as not flipped.
|
||||
var pDest = dest;
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (TSrc*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest++ = transformer(*pSrc);
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isHorizontallyFlipped)
|
||||
{
|
||||
// The first element is at top-right.
|
||||
var pDest = dest + length - 1;
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (TSrc*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest-- = transformer(*pSrc);
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (TSrc*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
*pDest++ = transformer(*pSrc);
|
||||
pSrc += channelCount;
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static T Identity<T>(T x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
private static float ByteNormalizer(byte x)
|
||||
{
|
||||
return (float)x / ((1 << 8) - 1);
|
||||
}
|
||||
|
||||
private static float UshortNormalizer(ushort x)
|
||||
{
|
||||
return (float)x / ((1 << 16) - 1);
|
||||
}
|
||||
|
||||
private static bool IsChannelNumberValid(int channelCount, int channelNumber)
|
||||
{
|
||||
if (channelNumber < 0)
|
||||
{
|
||||
Logger.LogWarning($"{channelNumber} must be >= 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (channelCount == 0)
|
||||
{
|
||||
Logger.LogWarning("Channels don't make sense in the current context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (channelNumber >= channelCount)
|
||||
{
|
||||
Logger.LogWarning($"channelNumber must be <= {channelCount - 1} since there are only {channelCount} channels, but {channelNumber} is given");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadSrgb(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var r = *pSrc++;
|
||||
var g = *pSrc++;
|
||||
var b = *pSrc++;
|
||||
*pDest++ = new Color32(r, g, b, 255);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadSrgba(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var r = *pSrc++;
|
||||
var g = *pSrc++;
|
||||
var b = *pSrc++;
|
||||
var a = *pSrc++;
|
||||
*pDest++ = new Color32(r, g, b, a);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadSbgra(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var b = *pSrc++;
|
||||
var g = *pSrc++;
|
||||
var r = *pSrc++;
|
||||
var a = *pSrc++;
|
||||
*pDest++ = new Color32(r, g, b, a);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadGray8(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var v = *pSrc++;
|
||||
*pDest++ = new Color32(v, v, v, 255);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadLab8(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var l = *pSrc++;
|
||||
var a = *pSrc++;
|
||||
var b = *pSrc++;
|
||||
*pDest++ = Lab8ToRgb(l, a, b);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadSrgb48(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (ushort*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var r = *pSrc++;
|
||||
var g = *pSrc++;
|
||||
var b = *pSrc++;
|
||||
*pDest++ = new Color32((byte)(r / 255), (byte)(g / 255), (byte)(b / 255), 255);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadSrgba64(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (ushort*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var r = *pSrc++;
|
||||
var g = *pSrc++;
|
||||
var b = *pSrc++;
|
||||
var a = *pSrc++;
|
||||
*pDest++ = new Color32((byte)(r / 255), (byte)(g / 255), (byte)(b / 255), (byte)(a / 255));
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadGray16(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (ushort*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var v = *pSrc++;
|
||||
*pDest++ = new Color32((byte)(v / 255), (byte)(v / 255), (byte)(v / 255), 255);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadVec32f1(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (float*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var v = *pSrc++;
|
||||
*pDest++ = new Color32((byte)(v * 255), (byte)(v * 255), (byte)(v * 255), 255);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadVec32f2(ImageFrame imageFrame, Color32[] colors)
|
||||
{
|
||||
var width = imageFrame.Width();
|
||||
var height = imageFrame.Height();
|
||||
var length = width * height;
|
||||
|
||||
if (colors.Length != length)
|
||||
{
|
||||
Logger.LogWarning($"The length of colors ({colors.Length}) does not equal {width} * {height} = {length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var widthStep = imageFrame.WidthStep();
|
||||
var byteDepth = imageFrame.ByteDepth();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color32* dest = colors)
|
||||
{
|
||||
// NOTE: We cannot assume that the pixel data is aligned properly.
|
||||
var pLine = (byte*)imageFrame.MutablePixelData();
|
||||
// The first element is at top-left (the image is not flipped at all).
|
||||
var pDest = dest + (width * (height - 1));
|
||||
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
var pSrc = (float*)pLine;
|
||||
for (var j = 0; j < width; j++)
|
||||
{
|
||||
var x = *pSrc++;
|
||||
var y = *pSrc++;
|
||||
var magnitude = Mathf.Sqrt((x * x) + (y * y));
|
||||
var angle = Mathf.Atan2(y, x);
|
||||
*pDest++ = UnityEngine.Color.HSVToRGB(magnitude, 1.0f, angle);
|
||||
}
|
||||
pLine += widthStep;
|
||||
pDest -= 2 * width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static (float, float, float) Lab8ToXYZ(byte l, sbyte a, sbyte b)
|
||||
{
|
||||
// cf. https://en.wikipedia.org/wiki/CIELAB_color_space
|
||||
var delta = 6.0f / 29;
|
||||
var kappa = 903.3f; // 24389 / 27
|
||||
var fy = (float)(l + 16) / 116;
|
||||
var fx = ((float)a / 500) + fy;
|
||||
var fz = fy - ((float)b / 200);
|
||||
var xr = fx > delta ? fx * fx * fx : ((116 * fx) - 16) / kappa;
|
||||
var yr = fy > delta ? fy * fy * fy : ((116 * fy) - 16) / kappa;
|
||||
var zr = fz > delta ? fz * fz * fz : ((116 * fz) - 16) / kappa;
|
||||
|
||||
// use D65 as the reference white
|
||||
return (0.950489f * xr, yr, 1.088840f * zr);
|
||||
}
|
||||
|
||||
private static Color32 XYZToRgb(float x, float y, float z)
|
||||
{
|
||||
// cf. https://stackoverflow.com/questions/66360637/which-matrix-is-correct-to-map-xyz-to-linear-rgb-for-srgb
|
||||
var rl = (3.24096994f * x) - (1.53738318f * y) - (0.49861076f * z);
|
||||
var gl = (-0.96924364f * x) + (1.87596750f * y) + (0.04155506f * z);
|
||||
var bl = (0.05563008f * x) - (0.20397696f * y) + (1.05697151f * z);
|
||||
var r = rl <= 0.0031308f ? 12.92f * rl : (1.055f * Mathf.Pow(rl, 1 / 2.4f)) - 0.055f;
|
||||
var g = gl <= 0.0031308f ? 12.92f * gl : (1.055f * Mathf.Pow(gl, 1 / 2.4f)) - 0.055f;
|
||||
var b = bl <= 0.0031308f ? 12.92f * bl : (1.055f * Mathf.Pow(bl, 1 / 2.4f)) - 0.055f;
|
||||
|
||||
return new UnityEngine.Color(r, g, b, 255);
|
||||
}
|
||||
|
||||
private static Color32 Lab8ToRgb(byte l, byte a, byte b)
|
||||
{
|
||||
var (x, y, z) = Lab8ToXYZ(l, (sbyte)a, (sbyte)b);
|
||||
return XYZToRgb(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d2ed92677011f5249a32983f023f216
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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
|
||||
{
|
||||
public static class TextureFormatExtension
|
||||
{
|
||||
public static ImageFormat.Types.Format ToImageFormat(this TextureFormat textureFormat)
|
||||
{
|
||||
#pragma warning disable IDE0010
|
||||
switch (textureFormat)
|
||||
{
|
||||
case TextureFormat.RGB24:
|
||||
{
|
||||
return ImageFormat.Types.Format.Srgb;
|
||||
}
|
||||
case TextureFormat.RGBA32:
|
||||
{
|
||||
return ImageFormat.Types.Format.Srgba;
|
||||
}
|
||||
case TextureFormat.Alpha8:
|
||||
{
|
||||
return ImageFormat.Types.Format.Gray8;
|
||||
}
|
||||
case TextureFormat.RGB48:
|
||||
{
|
||||
return ImageFormat.Types.Format.Srgb48;
|
||||
}
|
||||
case TextureFormat.RGBA64:
|
||||
{
|
||||
return ImageFormat.Types.Format.Srgba64;
|
||||
}
|
||||
case TextureFormat.RFloat:
|
||||
{
|
||||
return ImageFormat.Types.Format.Vec32F1;
|
||||
}
|
||||
case TextureFormat.RGFloat:
|
||||
{
|
||||
return ImageFormat.Types.Format.Vec32F2;
|
||||
}
|
||||
case TextureFormat.BGRA32:
|
||||
{
|
||||
return ImageFormat.Types.Format.Sbgra;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return ImageFormat.Types.Format.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore IDE0010
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1175bfd0d6ad20e858e8cc31a58187eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class GlobalInstanceTable<TKey, TValue> where TValue : class
|
||||
{
|
||||
private readonly Dictionary<TKey, WeakReference<TValue>> _table;
|
||||
private readonly int _maxSize;
|
||||
|
||||
public GlobalInstanceTable(int maxSize)
|
||||
{
|
||||
_table = new Dictionary<TKey, WeakReference<TValue>>();
|
||||
_maxSize = maxSize;
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (_table.Count >= _maxSize)
|
||||
{
|
||||
ClearUnusedKeys();
|
||||
}
|
||||
|
||||
lock (((ICollection)_table).SyncRoot)
|
||||
{
|
||||
if (_table.Count >= _maxSize)
|
||||
{
|
||||
throw new InvalidOperationException("The table is full");
|
||||
}
|
||||
|
||||
if (_table.ContainsKey(key))
|
||||
{
|
||||
if (_table[key].TryGetTarget(out var currentValue))
|
||||
{
|
||||
throw new ArgumentException("An instance with the same key already exists");
|
||||
}
|
||||
_table[key].SetTarget(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_table[key] = new WeakReference<TValue>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
lock (((ICollection)_table).SyncRoot)
|
||||
{
|
||||
if (_table.ContainsKey(key))
|
||||
{
|
||||
return _table[key].TryGetTarget(out value);
|
||||
}
|
||||
}
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (((ICollection)_table).SyncRoot)
|
||||
{
|
||||
_table.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return _table.ContainsKey(key);
|
||||
}
|
||||
|
||||
private void ClearUnusedKeys()
|
||||
{
|
||||
lock (((ICollection)_table).SyncRoot)
|
||||
{
|
||||
var deadKeys = _table.Where(x => !x.Value.TryGetTarget(out var target)).Select(x => x.Key).ToArray();
|
||||
|
||||
foreach (var key in deadKeys)
|
||||
{
|
||||
var _ = _table.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2732ac5c3629755e19f5eeeba29bfe79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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 System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public static class GpuManager
|
||||
{
|
||||
private const string _TAG = nameof(GpuManager);
|
||||
|
||||
private delegate void PluginCallback(int eventId);
|
||||
|
||||
private static readonly object _SetupLock = new object();
|
||||
#pragma warning disable IDE0044
|
||||
private static IntPtr _CurrentContext = IntPtr.Zero;
|
||||
#pragma warning restore IDE0044
|
||||
private static bool _IsContextInitialized = false;
|
||||
|
||||
public static GpuResources GpuResources { get; private set; }
|
||||
public static GlCalculatorHelper GlCalculatorHelper { get; private set; }
|
||||
|
||||
public static bool IsInitialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize GPU resources.
|
||||
/// If it finishes successfully, <see cref="IsInitialized" /> will be set to <c>true</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If <see cref="IsInitialized" /> is <c>true</c>, it will do nothing.
|
||||
/// Before the application exits, don't forget to call <see cref="Shutdown" />.
|
||||
/// </remarks>
|
||||
public static IEnumerator Initialize()
|
||||
{
|
||||
lock (_SetupLock)
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
Logger.LogInfo(_TAG, "Already initialized");
|
||||
yield break;
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID
|
||||
_IsContextInitialized = SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES3;
|
||||
if (!_IsContextInitialized)
|
||||
{
|
||||
PluginCallback callback = GetCurrentContext;
|
||||
|
||||
var fp = Marshal.GetFunctionPointerForDelegate(callback);
|
||||
GL.IssuePluginEvent(fp, 1);
|
||||
}
|
||||
#else
|
||||
_IsContextInitialized = true;
|
||||
#endif
|
||||
|
||||
var count = 100;
|
||||
yield return new WaitUntil(() =>
|
||||
{
|
||||
return --count < 0 || _IsContextInitialized;
|
||||
});
|
||||
|
||||
if (!_IsContextInitialized)
|
||||
{
|
||||
Logger.LogError(_TAG, "Failed to get GlContext");
|
||||
yield break;
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID
|
||||
if (_CurrentContext == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogWarning(_TAG, "EGL context is not found, so MediaPipe won't share their EGL contexts with Unity");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogVerbose(_TAG, $"EGL context is found: {_CurrentContext}");
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
Logger.LogInfo(_TAG, "Initializing GpuResources...");
|
||||
var statusOrGpuResources = GpuResources.Create(_CurrentContext);
|
||||
|
||||
statusOrGpuResources.status.AssertOk();
|
||||
GpuResources = statusOrGpuResources.Value();
|
||||
|
||||
Logger.LogInfo(_TAG, "Initializing GlCalculatorHelper...");
|
||||
GlCalculatorHelper = new GlCalculatorHelper();
|
||||
GlCalculatorHelper.InitializeForTest(GpuResources);
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
catch (EntryPointNotFoundException e)
|
||||
{
|
||||
Logger.LogException(e);
|
||||
Logger.LogError(_TAG, "Failed to create GpuResources. Did you build libraries with GPU enabled?");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose GPU resources.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This has to be called before the application exits.
|
||||
/// Otherwise, UnityEditor can freeze.
|
||||
/// </remarks>
|
||||
public static void Shutdown()
|
||||
{
|
||||
if (GpuResources != null)
|
||||
{
|
||||
GpuResources.Dispose();
|
||||
GpuResources = null;
|
||||
}
|
||||
|
||||
if (GlCalculatorHelper != null)
|
||||
{
|
||||
GlCalculatorHelper.Dispose();
|
||||
GlCalculatorHelper = null;
|
||||
}
|
||||
|
||||
IsInitialized = false;
|
||||
}
|
||||
|
||||
// Currently, it works only on Android
|
||||
#if UNITY_ANDROID
|
||||
[AOT.MonoPInvokeCallback(typeof(PluginCallback))]
|
||||
private static void GetCurrentContext(int eventId) {
|
||||
_CurrentContext = Egl.GetCurrentContext();
|
||||
_IsContextInitialized = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user