Merge branch 'WES-XX-Mediapipe-integration' into 'development'
Wes xx mediapipe integration See merge request wesign/unity-application!36
This commit was merged in pull request #36.
This commit is contained in:
8
Assets/MediaPipeUnity.meta
Normal file
8
Assets/MediaPipeUnity.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2eeb6ad0b5f376a4c89c3d60981e3954
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/MediaPipeUnity/Common.meta
Normal file
8
Assets/MediaPipeUnity/Common.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7edf4cdcf72d7a4ebb045b80f81aa26
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/MediaPipeUnity/Common/Scripts.meta
Normal file
8
Assets/MediaPipeUnity/Common/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4cf8a63bc91e254baf15f576e0722c3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/MediaPipeUnity/Common/Scripts/AssetLoader.cs
Normal file
39
Assets/MediaPipeUnity/Common/Scripts/AssetLoader.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// 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;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public static class AssetLoader
|
||||
{
|
||||
private static ResourceManager _ResourceManager;
|
||||
|
||||
public static void Provide(ResourceManager manager)
|
||||
{
|
||||
_ResourceManager = manager;
|
||||
}
|
||||
|
||||
public static IEnumerator PrepareAssetAsync(string name, string uniqueKey, bool overwrite = false)
|
||||
{
|
||||
if (_ResourceManager == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Logger.LogWarning("ResourceManager is not provided, so default LocalResourceManager will be used");
|
||||
_ResourceManager = new LocalResourceManager();
|
||||
#else
|
||||
throw new System.InvalidOperationException("ResourceManager is not provided");
|
||||
#endif
|
||||
}
|
||||
return _ResourceManager.PrepareAssetAsync(name, uniqueKey, overwrite);
|
||||
}
|
||||
|
||||
public static IEnumerator PrepareAssetAsync(string name, bool overwrite = false)
|
||||
{
|
||||
return PrepareAssetAsync(name, name, overwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/AssetLoader.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/AssetLoader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8bd1b384b7c7414cb732e8fbabf95a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
160
Assets/MediaPipeUnity/Common/Scripts/Bootstrap.cs
Normal file
160
Assets/MediaPipeUnity/Common/Scripts/Bootstrap.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
// 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;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class Bootstrap : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public enum AssetLoaderType
|
||||
{
|
||||
StreamingAssets,
|
||||
AssetBundle,
|
||||
Local,
|
||||
}
|
||||
|
||||
private const string _TAG = nameof(Bootstrap);
|
||||
|
||||
[SerializeField] private ImageSourceType _defaultImageSource;
|
||||
[SerializeField] private InferenceMode _preferableInferenceMode;
|
||||
[SerializeField] private AssetLoaderType _assetLoaderType;
|
||||
[SerializeField] private bool _enableGlog = true;
|
||||
|
||||
public InferenceMode inferenceMode { get; private set; }
|
||||
public bool isFinished { get; private set; }
|
||||
private bool _isGlogInitialized;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var _ = StartCoroutine(Init());
|
||||
}
|
||||
|
||||
private IEnumerator Init()
|
||||
{
|
||||
Logger.SetLogger(new MemoizedLogger(100));
|
||||
Logger.MinLogLevel = Logger.LogLevel.Debug;
|
||||
|
||||
Protobuf.SetLogHandler(Protobuf.DefaultLogHandler);
|
||||
|
||||
Logger.LogInfo(_TAG, "Setting global flags...");
|
||||
GlobalConfigManager.SetFlags();
|
||||
|
||||
if (_enableGlog)
|
||||
{
|
||||
if (Glog.LogDir != null)
|
||||
{
|
||||
if (!Directory.Exists(Glog.LogDir))
|
||||
{
|
||||
Directory.CreateDirectory(Glog.LogDir);
|
||||
}
|
||||
Logger.LogVerbose(_TAG, $"Glog will output files under {Glog.LogDir}");
|
||||
}
|
||||
Glog.Initialize("MediaPipeUnityPlugin");
|
||||
_isGlogInitialized = true;
|
||||
}
|
||||
|
||||
Logger.LogInfo(_TAG, "Initializing AssetLoader...");
|
||||
switch (_assetLoaderType)
|
||||
{
|
||||
case AssetLoaderType.AssetBundle:
|
||||
{
|
||||
AssetLoader.Provide(new AssetBundleResourceManager("mediapipe"));
|
||||
break;
|
||||
}
|
||||
case AssetLoaderType.StreamingAssets:
|
||||
{
|
||||
AssetLoader.Provide(new StreamingAssetsResourceManager());
|
||||
break;
|
||||
}
|
||||
case AssetLoaderType.Local:
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
AssetLoader.Provide(new LocalResourceManager());
|
||||
break;
|
||||
#else
|
||||
Logger.LogError("LocalResourceManager is only supported on UnityEditor");
|
||||
yield break;
|
||||
#endif
|
||||
}
|
||||
default:
|
||||
{
|
||||
Logger.LogError($"AssetLoaderType is unknown: {_assetLoaderType}");
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
DecideInferenceMode();
|
||||
if (inferenceMode == InferenceMode.GPU)
|
||||
{
|
||||
Logger.LogInfo(_TAG, "Initializing GPU resources...");
|
||||
yield return GpuManager.Initialize();
|
||||
|
||||
if (!GpuManager.IsInitialized)
|
||||
{
|
||||
Logger.LogWarning("If your native library is built for CPU, change 'Preferable Inference Mode' to CPU from the Inspector Window for Bootstrap");
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInfo(_TAG, "Preparing ImageSource...");
|
||||
ImageSourceProvider.ImageSource = GetImageSource(_defaultImageSource);
|
||||
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public ImageSource GetImageSource(ImageSourceType imageSourceType)
|
||||
{
|
||||
switch (imageSourceType)
|
||||
{
|
||||
case ImageSourceType.WebCamera:
|
||||
{
|
||||
return GetComponent<WebCamSource>();
|
||||
}
|
||||
case ImageSourceType.Image:
|
||||
{
|
||||
return GetComponent<StaticImageSource>();
|
||||
}
|
||||
case ImageSourceType.Video:
|
||||
{
|
||||
return GetComponent<VideoSource>();
|
||||
}
|
||||
case ImageSourceType.Unknown:
|
||||
default:
|
||||
{
|
||||
throw new System.ArgumentException($"Unsupported source type: {imageSourceType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DecideInferenceMode()
|
||||
{
|
||||
#if UNITY_EDITOR_OSX || UNITY_EDITOR_WIN
|
||||
if (_preferableInferenceMode == InferenceMode.GPU) {
|
||||
Logger.LogWarning(_TAG, "Current platform does not support GPU inference mode, so falling back to CPU mode");
|
||||
}
|
||||
inferenceMode = InferenceMode.CPU;
|
||||
#else
|
||||
inferenceMode = _preferableInferenceMode;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
GpuManager.Shutdown();
|
||||
|
||||
if (_isGlogInitialized)
|
||||
{
|
||||
Glog.Shutdown();
|
||||
}
|
||||
|
||||
Protobuf.ResetLogHandler();
|
||||
Logger.SetLogger(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Assets/MediaPipeUnity/Common/Scripts/Bootstrap.cs.meta
Normal file
15
Assets/MediaPipeUnity/Common/Scripts/Bootstrap.cs.meta
Normal file
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4d846393f8d9f20fa64b924b0d95e68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- globalConfigManagerPrefab: {fileID: 4394522812020757894, guid: 488fbb6a3005b66c2b657204bce83fcf,
|
||||
type: 3}
|
||||
- gpuManagerPrefab: {fileID: 7458244645391269047, guid: a84904e562ecaf887b2dba62111bb901,
|
||||
type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
140
Assets/MediaPipeUnity/Common/Scripts/GlobalConfigManager.cs
Normal file
140
Assets/MediaPipeUnity/Common/Scripts/GlobalConfigManager.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
// 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.IO;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public static class GlobalConfigManager
|
||||
{
|
||||
private const string _TAG = nameof(GlobalConfigManager);
|
||||
|
||||
private static string CacheDirPath => Path.Combine(Application.persistentDataPath, "Cache");
|
||||
|
||||
private static string ConfigFilePath => Path.Combine(CacheDirPath, "globalConfig.env");
|
||||
|
||||
private const string _GlogLogtostderrKey = "GLOG_logtostderr";
|
||||
private const string _GlogStderrthresholdKey = "GLOG_stderrthreshold";
|
||||
private const string _GlogMinloglevelKey = "GLOG_minloglevel";
|
||||
private const string _GlogVKey = "GLOG_v";
|
||||
private const string _GlogLogDirKey = "GLOG_log_dir";
|
||||
|
||||
public static bool GlogLogtostderr
|
||||
{
|
||||
get => Config[_GlogLogtostderrKey] == "1";
|
||||
set => Config[_GlogLogtostderrKey] = value ? "1" : "0";
|
||||
}
|
||||
|
||||
public static int GlogStderrthreshold
|
||||
{
|
||||
get => int.Parse(Config[_GlogStderrthresholdKey]);
|
||||
set => Config[_GlogStderrthresholdKey] = value.ToString();
|
||||
}
|
||||
|
||||
public static int GlogMinloglevel
|
||||
{
|
||||
get => int.Parse(Config[_GlogMinloglevelKey]);
|
||||
set => Config[_GlogMinloglevelKey] = value.ToString();
|
||||
}
|
||||
|
||||
public static int GlogV
|
||||
{
|
||||
get => int.Parse(Config[_GlogVKey]);
|
||||
set => Config[_GlogVKey] = value.ToString();
|
||||
}
|
||||
|
||||
public static string GlogLogDir
|
||||
{
|
||||
get => Config[_GlogLogDirKey];
|
||||
set => Config[_GlogLogDirKey] = value;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> _Config;
|
||||
private static Dictionary<string, string> Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Config == null)
|
||||
{
|
||||
_Config = new Dictionary<string, string>() {
|
||||
{ _GlogLogtostderrKey, "1" },
|
||||
{ _GlogStderrthresholdKey, "2" },
|
||||
{ _GlogMinloglevelKey, "0" },
|
||||
{ _GlogLogDirKey, "" },
|
||||
{ _GlogVKey, "0" },
|
||||
};
|
||||
|
||||
if (!File.Exists(ConfigFilePath))
|
||||
{
|
||||
Logger.LogDebug(_TAG, $"Global config file does not exist: {ConfigFilePath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogDebug(_TAG, $"Reading the config file ({ConfigFilePath})...");
|
||||
foreach (var line in File.ReadLines(ConfigFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
var pair = ParseLine(line);
|
||||
_Config[pair.Item1] = pair.Item2;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Logger.LogWarning($"{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _Config;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Commit()
|
||||
{
|
||||
string[] lines = {
|
||||
$"{_GlogLogtostderrKey}={(GlogLogtostderr ? "1" : "0")}",
|
||||
$"{_GlogStderrthresholdKey}={GlogStderrthreshold}",
|
||||
$"{_GlogMinloglevelKey}={GlogMinloglevel}",
|
||||
$"{_GlogLogDirKey}={GlogLogDir}",
|
||||
$"{_GlogVKey}={GlogV}",
|
||||
};
|
||||
if (!Directory.Exists(CacheDirPath))
|
||||
{
|
||||
var _ = Directory.CreateDirectory(CacheDirPath);
|
||||
}
|
||||
File.WriteAllLines(ConfigFilePath, lines, Encoding.UTF8);
|
||||
Logger.LogInfo(_TAG, "Global config file has been updated");
|
||||
}
|
||||
|
||||
public static void SetFlags()
|
||||
{
|
||||
Glog.Logtostderr = GlogLogtostderr;
|
||||
Glog.Stderrthreshold = GlogStderrthreshold;
|
||||
Glog.Minloglevel = GlogMinloglevel;
|
||||
Glog.V = GlogV;
|
||||
Glog.LogDir = GlogLogDir == "" ? null : Path.Combine(Application.persistentDataPath, GlogLogDir);
|
||||
}
|
||||
|
||||
private static (string, string) ParseLine(string line)
|
||||
{
|
||||
var i = line.IndexOf('=');
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
throw new System.FormatException("Each line in global config file must include '=', but not found");
|
||||
}
|
||||
|
||||
var key = line.Substring(0, i);
|
||||
var value = line.Substring(i + 1);
|
||||
|
||||
return (key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b93a8796afa16fd68ef2325d00d4d9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
311
Assets/MediaPipeUnity/Common/Scripts/GraphRunner.cs
Normal file
311
Assets/MediaPipeUnity/Common/Scripts/GraphRunner.cs
Normal file
@@ -0,0 +1,311 @@
|
||||
// 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;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
using Stopwatch = System.Diagnostics.Stopwatch;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public abstract class GraphRunner : MonoBehaviour
|
||||
{
|
||||
public enum ConfigType
|
||||
{
|
||||
None,
|
||||
CPU,
|
||||
GPU,
|
||||
OpenGLES,
|
||||
}
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
// TODO: make it static
|
||||
protected string TAG => GetType().Name;
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
[SerializeField] private TextAsset _cpuConfig = null;
|
||||
[SerializeField] private TextAsset _gpuConfig = null;
|
||||
[SerializeField] private TextAsset _openGlEsConfig = null;
|
||||
[SerializeField] private long _timeoutMicrosec = 0;
|
||||
|
||||
private static readonly GlobalInstanceTable<int, GraphRunner> _InstanceTable = new GlobalInstanceTable<int, GraphRunner>(5);
|
||||
private static readonly Dictionary<IntPtr, int> _NameTable = new Dictionary<IntPtr, int>();
|
||||
|
||||
protected RunningMode runningMode { get; private set; } = RunningMode.Async;
|
||||
private bool _isRunning = false;
|
||||
|
||||
public InferenceMode inferenceMode => configType == ConfigType.CPU ? InferenceMode.CPU : InferenceMode.GPU;
|
||||
public virtual ConfigType configType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GpuManager.IsInitialized)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3 && _openGlEsConfig != null)
|
||||
{
|
||||
return ConfigType.OpenGLES;
|
||||
}
|
||||
#endif
|
||||
if (_gpuConfig != null)
|
||||
{
|
||||
return ConfigType.GPU;
|
||||
}
|
||||
}
|
||||
return _cpuConfig != null ? ConfigType.CPU : ConfigType.None;
|
||||
}
|
||||
}
|
||||
public TextAsset textConfig
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (configType)
|
||||
{
|
||||
case ConfigType.CPU: return _cpuConfig;
|
||||
case ConfigType.GPU: return _gpuConfig;
|
||||
case ConfigType.OpenGLES: return _openGlEsConfig;
|
||||
case ConfigType.None:
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long timeoutMicrosec
|
||||
{
|
||||
get => _timeoutMicrosec;
|
||||
set => _timeoutMicrosec = (long)Mathf.Max(0, value);
|
||||
}
|
||||
public long timeoutMillisec
|
||||
{
|
||||
get => timeoutMicrosec / 1000;
|
||||
set => timeoutMicrosec = value * 1000;
|
||||
}
|
||||
|
||||
public RotationAngle rotation { get; private set; } = 0;
|
||||
|
||||
private Stopwatch _stopwatch;
|
||||
protected CalculatorGraph calculatorGraph { get; private set; }
|
||||
protected Timestamp latestTimestamp;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
_InstanceTable.Add(GetInstanceID(), this);
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
public WaitForResult WaitForInit(RunningMode runningMode)
|
||||
{
|
||||
return new WaitForResult(this, Initialize(runningMode));
|
||||
}
|
||||
|
||||
public virtual IEnumerator Initialize(RunningMode runningMode)
|
||||
{
|
||||
this.runningMode = runningMode;
|
||||
|
||||
Logger.LogInfo(TAG, $"Config Type = {configType}");
|
||||
Logger.LogInfo(TAG, $"Running Mode = {runningMode}");
|
||||
|
||||
InitializeCalculatorGraph().AssertOk();
|
||||
_stopwatch = new Stopwatch();
|
||||
_stopwatch.Start();
|
||||
|
||||
Logger.LogInfo(TAG, "Loading dependent assets...");
|
||||
var assetRequests = RequestDependentAssets();
|
||||
yield return new WaitWhile(() => assetRequests.Any((request) => request.keepWaiting));
|
||||
|
||||
var errors = assetRequests.Where((request) => request.isError).Select((request) => request.error).ToList();
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Logger.LogError(TAG, error);
|
||||
}
|
||||
throw new InternalException("Failed to prepare dependent assets");
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void StartRun(ImageSource imageSource);
|
||||
|
||||
protected void StartRun(SidePacket sidePacket)
|
||||
{
|
||||
calculatorGraph.StartRun(sidePacket).AssertOk();
|
||||
_isRunning = true;
|
||||
}
|
||||
|
||||
public virtual void Stop()
|
||||
{
|
||||
if (calculatorGraph != null)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
using (var status = calculatorGraph.CloseAllPacketSources())
|
||||
{
|
||||
if (!status.Ok())
|
||||
{
|
||||
Logger.LogError(TAG, status.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
using (var status = calculatorGraph.WaitUntilDone())
|
||||
{
|
||||
if (!status.Ok())
|
||||
{
|
||||
Logger.LogError(TAG, status.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
var _ = _NameTable.Remove(calculatorGraph.mpPtr);
|
||||
calculatorGraph.Dispose();
|
||||
calculatorGraph = null;
|
||||
}
|
||||
|
||||
if (_stopwatch != null && _stopwatch.IsRunning)
|
||||
{
|
||||
_stopwatch.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddPacketToInputStream<T>(string streamName, Packet<T> packet)
|
||||
{
|
||||
calculatorGraph.AddPacketToInputStream(streamName, packet).AssertOk();
|
||||
}
|
||||
|
||||
protected void AddTextureFrameToInputStream(string streamName, TextureFrame textureFrame)
|
||||
{
|
||||
latestTimestamp = GetCurrentTimestamp();
|
||||
|
||||
if (configType == ConfigType.OpenGLES)
|
||||
{
|
||||
var gpuBuffer = textureFrame.BuildGpuBuffer(GpuManager.GlCalculatorHelper.GetGlContext());
|
||||
AddPacketToInputStream(streamName, new GpuBufferPacket(gpuBuffer, latestTimestamp));
|
||||
return;
|
||||
}
|
||||
|
||||
var imageFrame = textureFrame.BuildImageFrame();
|
||||
textureFrame.Release();
|
||||
|
||||
AddPacketToInputStream(streamName, new ImageFramePacket(imageFrame, latestTimestamp));
|
||||
}
|
||||
|
||||
protected bool TryGetNext<TPacket, TValue>(OutputStream<TPacket, TValue> stream, out TValue value, bool allowBlock, long currentTimestampMicrosec) where TPacket : Packet<TValue>, new()
|
||||
{
|
||||
var result = stream.TryGetNext(out value, allowBlock);
|
||||
return result || allowBlock || stream.ResetTimestampIfTimedOut(currentTimestampMicrosec, timeoutMicrosec);
|
||||
}
|
||||
|
||||
protected long GetCurrentTimestampMicrosec()
|
||||
{
|
||||
return _stopwatch == null || !_stopwatch.IsRunning ? -1 : _stopwatch.ElapsedTicks / (TimeSpan.TicksPerMillisecond / 1000);
|
||||
}
|
||||
|
||||
protected Timestamp GetCurrentTimestamp()
|
||||
{
|
||||
var microsec = GetCurrentTimestampMicrosec();
|
||||
return microsec < 0 ? Timestamp.Unset() : new Timestamp(microsec);
|
||||
}
|
||||
|
||||
protected Status InitializeCalculatorGraph()
|
||||
{
|
||||
calculatorGraph = new CalculatorGraph();
|
||||
_NameTable.Add(calculatorGraph.mpPtr, GetInstanceID());
|
||||
|
||||
// NOTE: There's a simpler way to initialize CalculatorGraph.
|
||||
//
|
||||
// calculatorGraph = new CalculatorGraph(config.text);
|
||||
//
|
||||
// However, if the config format is invalid, this code does not initialize CalculatorGraph and does not throw exceptions either.
|
||||
// The problem is that if you call ObserveStreamOutput in this state, the program will crash.
|
||||
// The following code is not very efficient, but it will return Non-OK status when an invalid configuration is given.
|
||||
try
|
||||
{
|
||||
var baseConfig = textConfig == null ? null : CalculatorGraphConfig.Parser.ParseFromTextFormat(textConfig.text);
|
||||
if (baseConfig == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get the text config. Check if the config is set to GraphRunner");
|
||||
}
|
||||
var status = ConfigureCalculatorGraph(baseConfig);
|
||||
return !status.Ok() || inferenceMode == InferenceMode.CPU ? status : calculatorGraph.SetGpuResources(GpuManager.GpuResources);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return Status.FailedPrecondition(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure and initialize the <see cref="CalculatorGraph" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is the main process in <see cref="InitializeCalculatorGraph" />.<br />
|
||||
/// At least, <c>calculatorGraph.Initialize</c> must be called here.
|
||||
/// In addition to that, <see cref="OutputStream" /> instances should be initialized.
|
||||
/// </remarks>
|
||||
/// <param name="config">
|
||||
/// A <see cref="CalculatorGraphConfig" /> instance corresponding to <see cref="textConfig" />.<br />
|
||||
/// It can be dynamically modified here.
|
||||
/// </param>
|
||||
protected virtual Status ConfigureCalculatorGraph(CalculatorGraphConfig config)
|
||||
{
|
||||
return calculatorGraph.Initialize(config);
|
||||
}
|
||||
|
||||
protected void SetImageTransformationOptions(SidePacket sidePacket, ImageSource imageSource, bool expectedToBeMirrored = false)
|
||||
{
|
||||
// NOTE: The origin is left-bottom corner in Unity, and right-top corner in MediaPipe.
|
||||
rotation = imageSource.rotation.Reverse();
|
||||
var inputRotation = rotation;
|
||||
var isInverted = CoordinateSystem.ImageCoordinate.IsInverted(rotation);
|
||||
var shouldBeMirrored = imageSource.isHorizontallyFlipped ^ expectedToBeMirrored;
|
||||
var inputHorizontallyFlipped = isInverted ^ shouldBeMirrored;
|
||||
var inputVerticallyFlipped = !isInverted;
|
||||
|
||||
if ((inputHorizontallyFlipped && inputVerticallyFlipped) || rotation == RotationAngle.Rotation180)
|
||||
{
|
||||
inputRotation = inputRotation.Add(RotationAngle.Rotation180);
|
||||
inputHorizontallyFlipped = !inputHorizontallyFlipped;
|
||||
inputVerticallyFlipped = !inputVerticallyFlipped;
|
||||
}
|
||||
|
||||
Logger.LogDebug($"input_rotation = {inputRotation}, input_horizontally_flipped = {inputHorizontallyFlipped}, input_vertically_flipped = {inputVerticallyFlipped}");
|
||||
|
||||
sidePacket.Emplace("input_rotation", new IntPacket((int)inputRotation));
|
||||
sidePacket.Emplace("input_horizontally_flipped", new BoolPacket(inputHorizontallyFlipped));
|
||||
sidePacket.Emplace("input_vertically_flipped", new BoolPacket(inputVerticallyFlipped));
|
||||
}
|
||||
|
||||
protected WaitForResult WaitForAsset(string assetName, string uniqueKey, long timeoutMillisec, bool overwrite = false)
|
||||
{
|
||||
return new WaitForResult(this, AssetLoader.PrepareAssetAsync(assetName, uniqueKey, overwrite), timeoutMillisec);
|
||||
}
|
||||
|
||||
protected WaitForResult WaitForAsset(string assetName, long timeoutMillisec, bool overwrite = false)
|
||||
{
|
||||
return WaitForAsset(assetName, assetName, timeoutMillisec, overwrite);
|
||||
}
|
||||
|
||||
protected WaitForResult WaitForAsset(string assetName, string uniqueKey, bool overwrite = false)
|
||||
{
|
||||
return new WaitForResult(this, AssetLoader.PrepareAssetAsync(assetName, uniqueKey, overwrite));
|
||||
}
|
||||
|
||||
protected WaitForResult WaitForAsset(string assetName, bool overwrite = false)
|
||||
{
|
||||
return WaitForAsset(assetName, assetName, overwrite);
|
||||
}
|
||||
|
||||
protected abstract IList<WaitForResult> RequestDependentAssets();
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/GraphRunner.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/GraphRunner.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00f8c92dc785a7523b94c869aaead45f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/MediaPipeUnity/Common/Scripts/ImageSource.meta
Normal file
8
Assets/MediaPipeUnity/Common/Scripts/ImageSource.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab516f3bbf28a23489219f7a966fa0cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
159
Assets/MediaPipeUnity/Common/Scripts/ImageSource/ImageSource.cs
Normal file
159
Assets/MediaPipeUnity/Common/Scripts/ImageSource/ImageSource.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
// 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.Experimental.Rendering;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public abstract class ImageSource : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public struct ResolutionStruct
|
||||
{
|
||||
public int width;
|
||||
public int height;
|
||||
public double frameRate;
|
||||
|
||||
public ResolutionStruct(int width, int height, double frameRate)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.frameRate = frameRate;
|
||||
}
|
||||
|
||||
public ResolutionStruct(Resolution resolution)
|
||||
{
|
||||
width = resolution.width;
|
||||
height = resolution.height;
|
||||
frameRate = resolution.refreshRate;
|
||||
}
|
||||
|
||||
public Resolution ToResolution()
|
||||
{
|
||||
return new Resolution() { width = width, height = height, refreshRate = (int)frameRate };
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var aspectRatio = $"{width}x{height}";
|
||||
var frameRateStr = frameRate.ToString("#.##");
|
||||
return frameRate > 0 ? $"{aspectRatio} ({frameRateStr}Hz)" : aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
public ResolutionStruct resolution { get; protected set; }
|
||||
|
||||
/// <remarks>
|
||||
/// To call this method, the image source must be prepared.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// <see cref="TextureFormat" /> that is compatible with the current texture.
|
||||
/// </returns>
|
||||
public TextureFormat textureFormat => isPrepared ? TextureFormatFor(GetCurrentTexture()) : throw new InvalidOperationException("ImageSource is not prepared");
|
||||
public virtual int textureWidth => resolution.width;
|
||||
public virtual int textureHeight => resolution.height;
|
||||
/// <remarks>
|
||||
/// If <see cref="type" /> does not support frame rate, it returns zero.
|
||||
/// </remarks>
|
||||
public virtual double frameRate => resolution.frameRate;
|
||||
public float focalLengthPx { get; } = 2.0f; // TODO: calculate at runtime
|
||||
public virtual bool isHorizontallyFlipped { get; set; } = false;
|
||||
public virtual bool isVerticallyFlipped { get; } = false;
|
||||
public virtual bool isFrontFacing { get; } = false;
|
||||
public virtual RotationAngle rotation { get; } = RotationAngle.Rotation0;
|
||||
|
||||
public abstract string sourceName { get; }
|
||||
public abstract string[] sourceCandidateNames { get; }
|
||||
public abstract ResolutionStruct[] availableResolutions { get; }
|
||||
|
||||
/// <remarks>
|
||||
/// Once <see cref="Play" /> finishes successfully, it will become true.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// Returns if the image source is prepared.
|
||||
/// </returns>
|
||||
public abstract bool isPrepared { get; }
|
||||
|
||||
public abstract bool isPlaying { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Choose the source from <see cref="sourceCandidateNames" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You need to call <see cref="Play" /> for the change to take effect.
|
||||
/// </remarks>
|
||||
/// <param name="sourceId">The index of <see cref="sourceCandidateNames" /></param>
|
||||
public abstract void SelectSource(int sourceId);
|
||||
|
||||
/// <summary>
|
||||
/// Choose the resolution from <see cref="availableResolutions" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You need to call <see cref="Play" /> for the change to take effect.
|
||||
/// </remarks>
|
||||
/// <param name="resolutionId">The index of <see cref="availableResolutions" /></param>
|
||||
public void SelectResolution(int resolutionId)
|
||||
{
|
||||
var resolutions = availableResolutions;
|
||||
|
||||
if (resolutionId < 0 || resolutionId >= resolutions.Length)
|
||||
{
|
||||
throw new ArgumentException($"Invalid resolution ID: {resolutionId}");
|
||||
}
|
||||
|
||||
resolution = resolutions[resolutionId];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepare image source.
|
||||
/// If <see cref="isPlaying" /> is true, it will reset the image source.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When it finishes successfully, <see cref="isPrepared" /> will return true.
|
||||
/// </remarks>
|
||||
/// <exception cref="InvalidOperation" />
|
||||
public abstract IEnumerator Play();
|
||||
|
||||
/// <summary>
|
||||
/// Resume image source.
|
||||
/// If <see cref="isPlaying" /> is true, it will do nothing.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperation">
|
||||
/// The image source has not been played.
|
||||
/// </exception>
|
||||
public abstract IEnumerator Resume();
|
||||
|
||||
/// <summary>
|
||||
/// Pause image source.
|
||||
/// If <see cref="isPlaying" /> is false, it will do nothing.
|
||||
/// </summary>
|
||||
public abstract void Pause();
|
||||
|
||||
/// <summary>
|
||||
/// Stop image source.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When it finishes successfully, <see cref="isPrepared" /> will return false.
|
||||
/// </remarks>
|
||||
public abstract void Stop();
|
||||
|
||||
/// <remarks>
|
||||
/// To call this method, the image source must be prepared.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// <see cref="Texture" /> that contains the current image.
|
||||
/// </returns>
|
||||
public abstract Texture GetCurrentTexture();
|
||||
|
||||
protected static TextureFormat TextureFormatFor(Texture texture)
|
||||
{
|
||||
return GraphicsFormatUtility.GetTextureFormat(texture.graphicsFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78116299de071af7094419302302ec05
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
public enum ImageSourceType
|
||||
{
|
||||
WebCamera = 0,
|
||||
Image = 1,
|
||||
Video = 2,
|
||||
Unknown = 3,
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e20c591a3836aae1abc65429b7adde1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class StaticImageSource : ImageSource
|
||||
{
|
||||
[SerializeField] private Texture[] _availableSources;
|
||||
|
||||
[SerializeField]
|
||||
private ResolutionStruct[] _defaultAvailableResolutions = new ResolutionStruct[] {
|
||||
new ResolutionStruct(512, 512, 0),
|
||||
new ResolutionStruct(640, 480, 0),
|
||||
new ResolutionStruct(1280, 720, 0),
|
||||
};
|
||||
|
||||
private Texture2D _outputTexture;
|
||||
private Texture _image;
|
||||
private Texture image
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_image == null && _availableSources != null && _availableSources.Length > 0)
|
||||
{
|
||||
image = _availableSources[0];
|
||||
}
|
||||
return _image;
|
||||
}
|
||||
set
|
||||
{
|
||||
_image = value;
|
||||
resolution = GetDefaultResolution();
|
||||
}
|
||||
}
|
||||
|
||||
public override double frameRate => 0;
|
||||
|
||||
public override string sourceName => image != null ? image.name : null;
|
||||
|
||||
public override string[] sourceCandidateNames => _availableSources?.Select(source => source.name).ToArray();
|
||||
|
||||
public override ResolutionStruct[] availableResolutions => _defaultAvailableResolutions;
|
||||
|
||||
public override bool isPrepared => _outputTexture != null;
|
||||
|
||||
private bool _isPlaying = false;
|
||||
public override bool isPlaying => _isPlaying;
|
||||
|
||||
public override void SelectSource(int sourceId)
|
||||
{
|
||||
if (sourceId < 0 || sourceId >= _availableSources.Length)
|
||||
{
|
||||
throw new ArgumentException($"Invalid source ID: {sourceId}");
|
||||
}
|
||||
|
||||
image = _availableSources[sourceId];
|
||||
}
|
||||
|
||||
public override IEnumerator Play()
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
throw new InvalidOperationException("Image is not selected");
|
||||
}
|
||||
if (isPlaying)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
InitializeOutputTexture(image);
|
||||
_isPlaying = true;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
public override IEnumerator Resume()
|
||||
{
|
||||
if (!isPrepared)
|
||||
{
|
||||
throw new InvalidOperationException("Image is not prepared");
|
||||
}
|
||||
_isPlaying = true;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
_isPlaying = false;
|
||||
}
|
||||
public override void Stop()
|
||||
{
|
||||
_isPlaying = false;
|
||||
_outputTexture = null;
|
||||
}
|
||||
|
||||
public override Texture GetCurrentTexture()
|
||||
{
|
||||
return _outputTexture;
|
||||
}
|
||||
|
||||
private ResolutionStruct GetDefaultResolution()
|
||||
{
|
||||
var resolutions = availableResolutions;
|
||||
|
||||
return (resolutions == null || resolutions.Length == 0) ? new ResolutionStruct() : resolutions[0];
|
||||
}
|
||||
|
||||
private void InitializeOutputTexture(Texture src)
|
||||
{
|
||||
_outputTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false);
|
||||
|
||||
Texture resizedTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false);
|
||||
// TODO: assert ConvertTexture finishes successfully
|
||||
var _ = Graphics.ConvertTexture(src, resizedTexture);
|
||||
|
||||
var currentRenderTexture = RenderTexture.active;
|
||||
var tmpRenderTexture = new RenderTexture(resizedTexture.width, resizedTexture.height, 32);
|
||||
Graphics.Blit(resizedTexture, tmpRenderTexture);
|
||||
RenderTexture.active = tmpRenderTexture;
|
||||
|
||||
var rect = new UnityEngine.Rect(0, 0, _outputTexture.width, _outputTexture.height);
|
||||
_outputTexture.ReadPixels(rect, 0, 0);
|
||||
_outputTexture.Apply();
|
||||
|
||||
RenderTexture.active = currentRenderTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd7955705ab46c72b9124bb116a2dca9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
378
Assets/MediaPipeUnity/Common/Scripts/ImageSource/TextureFrame.cs
Normal file
378
Assets/MediaPipeUnity/Common/Scripts/ImageSource/TextureFrame.cs
Normal file
@@ -0,0 +1,378 @@
|
||||
// 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 Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
#pragma warning disable IDE0065
|
||||
using Color = UnityEngine.Color;
|
||||
#pragma warning restore IDE0065
|
||||
|
||||
public class TextureFrame
|
||||
{
|
||||
public class ReleaseEvent : UnityEvent<TextureFrame> { }
|
||||
|
||||
private const string _TAG = nameof(TextureFrame);
|
||||
|
||||
private static readonly GlobalInstanceTable<Guid, TextureFrame> _InstanceTable = new GlobalInstanceTable<Guid, TextureFrame>(100);
|
||||
/// <summary>
|
||||
/// A dictionary to look up which native texture belongs to which <see cref="TextureFrame" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Not all the <see cref="TextureFrame" /> instances are registered.
|
||||
/// Texture names are queried only when necessary, and the corresponding data will be saved then.
|
||||
/// </remarks>
|
||||
private static readonly Dictionary<uint, Guid> _NameTable = new Dictionary<uint, Guid>();
|
||||
|
||||
private readonly Texture2D _texture;
|
||||
private IntPtr _nativeTexturePtr = IntPtr.Zero;
|
||||
private GlSyncPoint _glSyncToken;
|
||||
|
||||
// Buffers that will be used to copy texture data on CPU.
|
||||
// They won't be initialized until it's necessary.
|
||||
private Texture2D _textureBuffer;
|
||||
|
||||
private Color32[] _pixelsBuffer; // for WebCamTexture
|
||||
private Color32[] pixelsBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_pixelsBuffer == null)
|
||||
{
|
||||
_pixelsBuffer = new Color32[width * height];
|
||||
}
|
||||
return _pixelsBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Guid _instanceId;
|
||||
// NOTE: width and height can be accessed from a thread other than Main Thread.
|
||||
public readonly int width;
|
||||
public readonly int height;
|
||||
public readonly TextureFormat format;
|
||||
|
||||
private ImageFormat.Types.Format _format = ImageFormat.Types.Format.Unknown;
|
||||
public ImageFormat.Types.Format imageFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_format == ImageFormat.Types.Format.Unknown)
|
||||
{
|
||||
_format = format.ToImageFormat();
|
||||
}
|
||||
return _format;
|
||||
}
|
||||
}
|
||||
|
||||
public bool isReadable => _texture.isReadable;
|
||||
|
||||
// TODO: determine at runtime
|
||||
public GpuBufferFormat gpuBufferformat => GpuBufferFormat.kBGRA32;
|
||||
|
||||
/// <summary>
|
||||
/// The event that will be invoked when the TextureFrame is released.
|
||||
/// </summary>
|
||||
#pragma warning disable IDE1006 // UnityEvent is PascalCase
|
||||
public readonly ReleaseEvent OnRelease;
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
private TextureFrame(Texture2D texture)
|
||||
{
|
||||
_texture = texture;
|
||||
width = texture.width;
|
||||
height = texture.height;
|
||||
format = texture.format;
|
||||
OnRelease = new ReleaseEvent();
|
||||
_instanceId = Guid.NewGuid();
|
||||
_InstanceTable.Add(_instanceId, this);
|
||||
}
|
||||
|
||||
public TextureFrame(int width, int height, TextureFormat format) : this(new Texture2D(width, height, format, false)) { }
|
||||
public TextureFrame(int width, int height) : this(width, height, TextureFormat.RGBA32) { }
|
||||
|
||||
public void CopyTexture(Texture dst)
|
||||
{
|
||||
Graphics.CopyTexture(_texture, dst);
|
||||
}
|
||||
|
||||
public void CopyTextureFrom(Texture src)
|
||||
{
|
||||
Graphics.CopyTexture(src, _texture);
|
||||
}
|
||||
|
||||
public bool ConvertTexture(Texture dst)
|
||||
{
|
||||
return Graphics.ConvertTexture(_texture, dst);
|
||||
}
|
||||
|
||||
public bool ConvertTextureFrom(Texture src)
|
||||
{
|
||||
return Graphics.ConvertTexture(src, _texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy texture data from <paramref name="src" />.
|
||||
/// If <paramref name="src" /> format is different from <see cref="format" />, it converts the format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// After calling it, pixel data won't be read from CPU safely.
|
||||
/// </remarks>
|
||||
public bool ReadTextureFromOnGPU(Texture src)
|
||||
{
|
||||
if (GetTextureFormat(src) != format)
|
||||
{
|
||||
return Graphics.ConvertTexture(src, _texture);
|
||||
}
|
||||
Graphics.CopyTexture(src, _texture);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy texture data from <paramref name="src" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This operation is slow.
|
||||
/// If CPU won't access the pixel data, use <see cref="ReadTextureFromOnGPU" /> instead.
|
||||
/// </remarks>
|
||||
public void ReadTextureFromOnCPU(Texture src)
|
||||
{
|
||||
var textureBuffer = LoadToTextureBuffer(src);
|
||||
SetPixels32(textureBuffer.GetPixels32());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy texture data from <paramref name="src" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In most cases, it should be better to use <paramref name="src" /> directly.
|
||||
/// </remarks>
|
||||
public void ReadTextureFromOnCPU(Texture2D src)
|
||||
{
|
||||
SetPixels32(src.GetPixels32());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy texture data from <paramref name="src" />.
|
||||
/// </summary>
|
||||
/// <param name="src">
|
||||
/// The texture from which the pixels are read.
|
||||
/// Its width and height must match that of the TextureFrame.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// This operation is slow.
|
||||
/// If CPU won't access the pixel data, use <see cref="ReadTextureFromOnGPU" /> instead.
|
||||
/// </remarks>
|
||||
public void ReadTextureFromOnCPU(WebCamTexture src)
|
||||
{
|
||||
SetPixels32(src.GetPixels32(pixelsBuffer));
|
||||
}
|
||||
|
||||
public Color GetPixel(int x, int y)
|
||||
{
|
||||
return _texture.GetPixel(x, y);
|
||||
}
|
||||
|
||||
public Color32[] GetPixels32()
|
||||
{
|
||||
return _texture.GetPixels32();
|
||||
}
|
||||
|
||||
public void SetPixels32(Color32[] pixels)
|
||||
{
|
||||
_texture.SetPixels32(pixels);
|
||||
_texture.Apply();
|
||||
|
||||
if (!RevokeNativeTexturePtr())
|
||||
{
|
||||
// If this line was executed, there must be a bug.
|
||||
Logger.LogError("Failed to revoke the native texture.");
|
||||
}
|
||||
}
|
||||
|
||||
public NativeArray<T> GetRawTextureData<T>() where T : struct
|
||||
{
|
||||
return _texture.GetRawTextureData<T>();
|
||||
}
|
||||
|
||||
/// <returns>The texture's native pointer</returns>
|
||||
public IntPtr GetNativeTexturePtr()
|
||||
{
|
||||
if (_nativeTexturePtr == IntPtr.Zero)
|
||||
{
|
||||
_nativeTexturePtr = _texture.GetNativeTexturePtr();
|
||||
var name = (uint)_nativeTexturePtr;
|
||||
|
||||
lock (((ICollection)_NameTable).SyncRoot)
|
||||
{
|
||||
if (!AcquireName(name, _instanceId))
|
||||
{
|
||||
throw new InvalidProgramException($"Another instance (id={_instanceId}) is using the specified name ({name}) now");
|
||||
}
|
||||
_NameTable.Add(name, _instanceId);
|
||||
}
|
||||
}
|
||||
return _nativeTexturePtr;
|
||||
}
|
||||
|
||||
public uint GetTextureName()
|
||||
{
|
||||
return (uint)GetNativeTexturePtr();
|
||||
}
|
||||
|
||||
public Guid GetInstanceID()
|
||||
{
|
||||
return _instanceId;
|
||||
}
|
||||
|
||||
public ImageFrame BuildImageFrame()
|
||||
{
|
||||
return new ImageFrame(imageFormat, width, height, 4 * width, GetRawTextureData<byte>());
|
||||
}
|
||||
|
||||
public GpuBuffer BuildGpuBuffer(GlContext glContext)
|
||||
{
|
||||
#if UNITY_EDITOR_LINUX || UNITY_STANDALONE_LINUX || UNITY_ANDROID
|
||||
var glTextureBuffer = new GlTextureBuffer(GetTextureName(), width, height, gpuBufferformat, OnReleaseTextureFrame, glContext);
|
||||
return new GpuBuffer(glTextureBuffer);
|
||||
#else
|
||||
throw new NotSupportedException("This method is only supported on Linux or Android");
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RemoveAllReleaseListeners()
|
||||
{
|
||||
OnRelease.RemoveAllListeners();
|
||||
}
|
||||
|
||||
// TODO: stop invoking OnRelease when it's already released
|
||||
public void Release(GlSyncPoint token = null)
|
||||
{
|
||||
if (_glSyncToken != null)
|
||||
{
|
||||
_glSyncToken.Dispose();
|
||||
}
|
||||
_glSyncToken = token;
|
||||
OnRelease.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the GPU has executed all commands up to the sync point.
|
||||
/// This blocks the CPU, and ensures the commands are complete from the point of view of all threads and contexts.
|
||||
/// </summary>
|
||||
public void WaitUntilReleased()
|
||||
{
|
||||
if (_glSyncToken == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_glSyncToken.Wait();
|
||||
_glSyncToken.Dispose();
|
||||
_glSyncToken = null;
|
||||
}
|
||||
|
||||
[AOT.MonoPInvokeCallback(typeof(GlTextureBuffer.DeletionCallback))]
|
||||
public static void OnReleaseTextureFrame(uint textureName, IntPtr syncTokenPtr)
|
||||
{
|
||||
var isIdFound = _NameTable.TryGetValue(textureName, out var _instanceId);
|
||||
|
||||
if (!isIdFound)
|
||||
{
|
||||
Logger.LogError(_TAG, $"nameof (name={textureName}) is released, but the owner TextureFrame is not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var isTextureFrameFound = _InstanceTable.TryGetValue(_instanceId, out var textureFrame);
|
||||
|
||||
if (!isTextureFrameFound)
|
||||
{
|
||||
Logger.LogWarning(_TAG, $"nameof owner TextureFrame of the released texture (name={textureName}) is already garbage collected");
|
||||
return;
|
||||
}
|
||||
|
||||
var _glSyncToken = syncTokenPtr == IntPtr.Zero ? null : new GlSyncPoint(syncTokenPtr);
|
||||
textureFrame.Release(_glSyncToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove <paramref name="name" /> from <see cref="_NameTable" /> if it's stale.
|
||||
/// If <paramref name="name" /> does not exist in <see cref="_NameTable" />, do nothing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the instance whose id is <paramref name="ownerId" /> owns <paramref name="name" /> now, it still removes <paramref name="name" />.
|
||||
/// </remarks>
|
||||
/// <returns>Return if name is available</returns>
|
||||
private static bool AcquireName(uint name, Guid ownerId)
|
||||
{
|
||||
if (_NameTable.TryGetValue(name, out var id))
|
||||
{
|
||||
if (ownerId != id && _InstanceTable.TryGetValue(id, out var _))
|
||||
{
|
||||
// if instance is found, the instance is using the name.
|
||||
Logger.LogVerbose($"{id} is using {name} now");
|
||||
return false;
|
||||
}
|
||||
var _ = _NameTable.Remove(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static TextureFormat GetTextureFormat(Texture texture)
|
||||
{
|
||||
return GraphicsFormatUtility.GetTextureFormat(texture.graphicsFormat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the texture name from <see cref="_NameTable" /> and empty <see cref="_nativeTexturePtr" />.
|
||||
/// This method needs to be called when an operation is performed that may change the internal texture.
|
||||
/// </summary>
|
||||
private bool RevokeNativeTexturePtr()
|
||||
{
|
||||
if (_nativeTexturePtr == IntPtr.Zero)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var currentName = GetTextureName();
|
||||
if (!_NameTable.Remove(currentName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_nativeTexturePtr = IntPtr.Zero;
|
||||
return true;
|
||||
}
|
||||
|
||||
private Texture2D LoadToTextureBuffer(Texture texture)
|
||||
{
|
||||
var textureFormat = GetTextureFormat(texture);
|
||||
|
||||
if (_textureBuffer == null || _textureBuffer.format != textureFormat)
|
||||
{
|
||||
_textureBuffer = new Texture2D(width, height, textureFormat, false);
|
||||
}
|
||||
|
||||
var tmpRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 32);
|
||||
var currentRenderTexture = RenderTexture.active;
|
||||
RenderTexture.active = tmpRenderTexture;
|
||||
|
||||
Graphics.Blit(texture, tmpRenderTexture);
|
||||
|
||||
var rect = new UnityEngine.Rect(0, 0, Mathf.Min(tmpRenderTexture.width, _textureBuffer.width), Mathf.Min(tmpRenderTexture.height, _textureBuffer.height));
|
||||
_textureBuffer.ReadPixels(rect, 0, 0);
|
||||
_textureBuffer.Apply();
|
||||
RenderTexture.active = currentRenderTexture;
|
||||
RenderTexture.ReleaseTemporary(tmpRenderTexture);
|
||||
|
||||
return _textureBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa7e0da68d497cd578438e238b6a6e7c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,161 @@
|
||||
// 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 UnityEngine;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class TextureFramePool : MonoBehaviour
|
||||
{
|
||||
private const string _TAG = nameof(TextureFramePool);
|
||||
|
||||
[SerializeField] private int _poolSize = 10;
|
||||
|
||||
private readonly object _formatLock = new object();
|
||||
private int _textureWidth = 0;
|
||||
private int _textureHeight = 0;
|
||||
private TextureFormat _format = TextureFormat.RGBA32;
|
||||
|
||||
private Queue<TextureFrame> _availableTextureFrames;
|
||||
/// <remarks>
|
||||
/// key: TextureFrame's instance ID
|
||||
/// </remarks>
|
||||
private Dictionary<Guid, TextureFrame> _textureFramesInUse;
|
||||
|
||||
/// <returns>
|
||||
/// The total number of texture frames in the pool.
|
||||
/// </returns>
|
||||
public int frameCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var availableTextureFramesCount = _availableTextureFrames == null ? 0 : _availableTextureFrames.Count;
|
||||
var textureFramesInUseCount = _textureFramesInUse == null ? 0 : _textureFramesInUse.Count;
|
||||
|
||||
return availableTextureFramesCount + textureFramesInUseCount;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_availableTextureFrames = new Queue<TextureFrame>(_poolSize);
|
||||
_textureFramesInUse = new Dictionary<Guid, TextureFrame>();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
lock (((ICollection)_availableTextureFrames).SyncRoot)
|
||||
{
|
||||
_availableTextureFrames.Clear();
|
||||
_availableTextureFrames = null;
|
||||
}
|
||||
|
||||
lock (((ICollection)_textureFramesInUse).SyncRoot)
|
||||
{
|
||||
foreach (var textureFrame in _textureFramesInUse.Values)
|
||||
{
|
||||
textureFrame.OnRelease.RemoveListener(OnTextureFrameRelease);
|
||||
}
|
||||
_textureFramesInUse.Clear();
|
||||
_textureFramesInUse = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResizeTexture(int textureWidth, int textureHeight, TextureFormat format)
|
||||
{
|
||||
lock (_formatLock)
|
||||
{
|
||||
_textureWidth = textureWidth;
|
||||
_textureHeight = textureHeight;
|
||||
_format = format;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResizeTexture(int textureWidth, int textureHeight)
|
||||
{
|
||||
ResizeTexture(textureWidth, textureHeight, _format);
|
||||
}
|
||||
|
||||
public bool TryGetTextureFrame(out TextureFrame outFrame)
|
||||
{
|
||||
TextureFrame nextFrame = null;
|
||||
|
||||
lock (((ICollection)_availableTextureFrames).SyncRoot)
|
||||
{
|
||||
if (_poolSize <= frameCount)
|
||||
{
|
||||
while (_availableTextureFrames.Count > 0)
|
||||
{
|
||||
var textureFrame = _availableTextureFrames.Dequeue();
|
||||
|
||||
if (!IsStale(textureFrame))
|
||||
{
|
||||
nextFrame = textureFrame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nextFrame = CreateNewTextureFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if (nextFrame == null)
|
||||
{
|
||||
outFrame = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (((ICollection)_textureFramesInUse).SyncRoot)
|
||||
{
|
||||
_textureFramesInUse.Add(nextFrame.GetInstanceID(), nextFrame);
|
||||
}
|
||||
|
||||
nextFrame.WaitUntilReleased();
|
||||
outFrame = nextFrame;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnTextureFrameRelease(TextureFrame textureFrame)
|
||||
{
|
||||
lock (((ICollection)_textureFramesInUse).SyncRoot)
|
||||
{
|
||||
if (!_textureFramesInUse.Remove(textureFrame.GetInstanceID()))
|
||||
{
|
||||
// won't be run
|
||||
Logger.LogWarning(_TAG, "The released texture does not belong to the pool");
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameCount > _poolSize || IsStale(textureFrame))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_availableTextureFrames.Enqueue(textureFrame);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsStale(TextureFrame textureFrame)
|
||||
{
|
||||
lock (_formatLock)
|
||||
{
|
||||
return textureFrame.width != _textureWidth || textureFrame.height != _textureHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private TextureFrame CreateNewTextureFrame()
|
||||
{
|
||||
var textureFrame = new TextureFrame(_textureWidth, _textureHeight, _format);
|
||||
textureFrame.OnRelease.AddListener(OnTextureFrameRelease);
|
||||
|
||||
return textureFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5da564da19cb6b7d8e4f97f269edc5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
116
Assets/MediaPipeUnity/Common/Scripts/ImageSource/VideoSource.cs
Normal file
116
Assets/MediaPipeUnity/Common/Scripts/ImageSource/VideoSource.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Video;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class VideoSource : ImageSource
|
||||
{
|
||||
[SerializeField] private VideoClip[] _availableSources;
|
||||
|
||||
private VideoClip _video;
|
||||
private VideoClip video
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_video == null && _availableSources != null && _availableSources.Length > 0)
|
||||
{
|
||||
video = _availableSources[0];
|
||||
}
|
||||
return _video;
|
||||
}
|
||||
set
|
||||
{
|
||||
_video = value;
|
||||
resolution = new ResolutionStruct((int)_video.width, (int)_video.height, _video.frameRate);
|
||||
}
|
||||
}
|
||||
|
||||
private VideoPlayer _videoPlayer;
|
||||
|
||||
public override string sourceName => video != null ? video.name : null;
|
||||
|
||||
public override string[] sourceCandidateNames => _availableSources?.Select(source => source.name).ToArray();
|
||||
|
||||
public override ResolutionStruct[] availableResolutions => video == null ? null : new ResolutionStruct[] { new ResolutionStruct((int)video.width, (int)video.height, video.frameRate) };
|
||||
|
||||
public override bool isPlaying => _videoPlayer != null && _videoPlayer.isPlaying;
|
||||
public override bool isPrepared => _videoPlayer != null && _videoPlayer.isPrepared;
|
||||
|
||||
public override void SelectSource(int sourceId)
|
||||
{
|
||||
if (sourceId < 0 || sourceId >= _availableSources.Length)
|
||||
{
|
||||
throw new ArgumentException($"Invalid source ID: {sourceId}");
|
||||
}
|
||||
|
||||
video = _availableSources[sourceId];
|
||||
if (_videoPlayer != null)
|
||||
{
|
||||
_videoPlayer.clip = video;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerator Play()
|
||||
{
|
||||
if (video == null)
|
||||
{
|
||||
throw new InvalidOperationException("Video is not selected");
|
||||
}
|
||||
_videoPlayer = gameObject.AddComponent<VideoPlayer>();
|
||||
_videoPlayer.renderMode = VideoRenderMode.APIOnly;
|
||||
_videoPlayer.isLooping = true;
|
||||
_videoPlayer.clip = video;
|
||||
_videoPlayer.Prepare();
|
||||
|
||||
yield return new WaitUntil(() => _videoPlayer.isPrepared);
|
||||
_videoPlayer.Play();
|
||||
}
|
||||
|
||||
public override IEnumerator Resume()
|
||||
{
|
||||
if (!isPrepared)
|
||||
{
|
||||
throw new InvalidOperationException("VideoPlayer is not prepared");
|
||||
}
|
||||
if (!isPlaying)
|
||||
{
|
||||
_videoPlayer.Play();
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
if (!isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_videoPlayer.Pause();
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (_videoPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_videoPlayer.Stop();
|
||||
Destroy(gameObject.GetComponent<VideoPlayer>());
|
||||
_videoPlayer = null;
|
||||
}
|
||||
|
||||
public override Texture GetCurrentTexture()
|
||||
{
|
||||
return _videoPlayer != null ? _videoPlayer.texture : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04085488e5fac35599866a2a6fceeda3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
299
Assets/MediaPipeUnity/Common/Scripts/ImageSource/WebCamSource.cs
Normal file
299
Assets/MediaPipeUnity/Common/Scripts/ImageSource/WebCamSource.cs
Normal file
@@ -0,0 +1,299 @@
|
||||
// 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;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
using UnityEngine.Android;
|
||||
#endif
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class WebCamSource : ImageSource
|
||||
{
|
||||
[Tooltip("For the default resolution, the one whose width is closest to this value will be chosen")]
|
||||
[SerializeField] private int _preferableDefaultWidth = 1280;
|
||||
|
||||
private const string _TAG = nameof(WebCamSource);
|
||||
|
||||
[SerializeField]
|
||||
private ResolutionStruct[] _defaultAvailableResolutions = new ResolutionStruct[] {
|
||||
new ResolutionStruct(176, 144, 30),
|
||||
new ResolutionStruct(320, 240, 30),
|
||||
new ResolutionStruct(424, 240, 30),
|
||||
new ResolutionStruct(640, 360, 30),
|
||||
new ResolutionStruct(640, 480, 30),
|
||||
new ResolutionStruct(848, 480, 30),
|
||||
new ResolutionStruct(960, 540, 30),
|
||||
new ResolutionStruct(1280, 720, 30),
|
||||
new ResolutionStruct(1600, 896, 30),
|
||||
new ResolutionStruct(1920, 1080, 30),
|
||||
};
|
||||
|
||||
private static readonly object _PermissionLock = new object();
|
||||
private static bool _IsPermitted = false;
|
||||
|
||||
private WebCamTexture _webCamTexture;
|
||||
private WebCamTexture webCamTexture
|
||||
{
|
||||
get => _webCamTexture;
|
||||
set
|
||||
{
|
||||
if (_webCamTexture != null)
|
||||
{
|
||||
_webCamTexture.Stop();
|
||||
}
|
||||
_webCamTexture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override int textureWidth => !isPrepared ? 0 : webCamTexture.width;
|
||||
public override int textureHeight => !isPrepared ? 0 : webCamTexture.height;
|
||||
|
||||
public override bool isVerticallyFlipped => isPrepared && webCamTexture.videoVerticallyMirrored;
|
||||
public override bool isFrontFacing => isPrepared && (webCamDevice is WebCamDevice valueOfWebCamDevice) && valueOfWebCamDevice.isFrontFacing;
|
||||
public override RotationAngle rotation => !isPrepared ? RotationAngle.Rotation0 : (RotationAngle)webCamTexture.videoRotationAngle;
|
||||
|
||||
private WebCamDevice? _webCamDevice;
|
||||
private WebCamDevice? webCamDevice
|
||||
{
|
||||
get => _webCamDevice;
|
||||
set
|
||||
{
|
||||
if (_webCamDevice is WebCamDevice valueOfWebCamDevice)
|
||||
{
|
||||
if (value is WebCamDevice valueOfValue && valueOfValue.name == valueOfWebCamDevice.name)
|
||||
{
|
||||
// not changed
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (value == null)
|
||||
{
|
||||
// not changed
|
||||
return;
|
||||
}
|
||||
_webCamDevice = value;
|
||||
resolution = GetDefaultResolution();
|
||||
}
|
||||
}
|
||||
public override string sourceName => (webCamDevice is WebCamDevice valueOfWebCamDevice) ? valueOfWebCamDevice.name : null;
|
||||
|
||||
private WebCamDevice[] _availableSources;
|
||||
private WebCamDevice[] availableSources
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_availableSources == null)
|
||||
{
|
||||
_availableSources = WebCamTexture.devices;
|
||||
}
|
||||
|
||||
return _availableSources;
|
||||
}
|
||||
set => _availableSources = value;
|
||||
}
|
||||
|
||||
public override string[] sourceCandidateNames => availableSources?.Select(device => device.name).ToArray();
|
||||
|
||||
#pragma warning disable IDE0025
|
||||
public override ResolutionStruct[] availableResolutions
|
||||
{
|
||||
get
|
||||
{
|
||||
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
|
||||
if (webCamDevice is WebCamDevice valueOfWebCamDevice) {
|
||||
return valueOfWebCamDevice.availableResolutions.Select(resolution => new ResolutionStruct(resolution)).ToArray();
|
||||
}
|
||||
#endif
|
||||
return webCamDevice == null ? null : _defaultAvailableResolutions;
|
||||
}
|
||||
}
|
||||
#pragma warning restore IDE0025
|
||||
|
||||
public override bool isPrepared => webCamTexture != null;
|
||||
public override bool isPlaying => webCamTexture != null && webCamTexture.isPlaying;
|
||||
private bool _isInitialized;
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
yield return GetPermission();
|
||||
|
||||
if (!_IsPermitted)
|
||||
{
|
||||
_isInitialized = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
availableSources = WebCamTexture.devices;
|
||||
|
||||
if (availableSources != null && availableSources.Length > 0)
|
||||
{
|
||||
webCamDevice = availableSources[0];
|
||||
}
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private IEnumerator GetPermission()
|
||||
{
|
||||
lock (_PermissionLock)
|
||||
{
|
||||
if (_IsPermitted)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID
|
||||
if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
|
||||
{
|
||||
Permission.RequestUserPermission(Permission.Camera);
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
}
|
||||
#elif UNITY_IOS
|
||||
if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) {
|
||||
yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_ANDROID
|
||||
if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
|
||||
{
|
||||
Logger.LogWarning(_TAG, "Not permitted to use Camera");
|
||||
yield break;
|
||||
}
|
||||
#elif UNITY_IOS
|
||||
if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) {
|
||||
Logger.LogWarning(_TAG, "Not permitted to use WebCam");
|
||||
yield break;
|
||||
}
|
||||
#endif
|
||||
_IsPermitted = true;
|
||||
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public override void SelectSource(int sourceId)
|
||||
{
|
||||
if (sourceId < 0 || sourceId >= availableSources.Length)
|
||||
{
|
||||
throw new ArgumentException($"Invalid source ID: {sourceId}");
|
||||
}
|
||||
|
||||
webCamDevice = availableSources[sourceId];
|
||||
}
|
||||
|
||||
public override IEnumerator Play()
|
||||
{
|
||||
yield return new WaitUntil(() => _isInitialized);
|
||||
if (!_IsPermitted)
|
||||
{
|
||||
throw new InvalidOperationException("Not permitted to access cameras");
|
||||
}
|
||||
|
||||
InitializeWebCamTexture();
|
||||
webCamTexture.Play();
|
||||
yield return WaitForWebCamTexture();
|
||||
}
|
||||
|
||||
public override IEnumerator Resume()
|
||||
{
|
||||
if (!isPrepared)
|
||||
{
|
||||
throw new InvalidOperationException("WebCamTexture is not prepared yet");
|
||||
}
|
||||
if (!webCamTexture.isPlaying)
|
||||
{
|
||||
webCamTexture.Play();
|
||||
}
|
||||
yield return WaitForWebCamTexture();
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
if (isPlaying)
|
||||
{
|
||||
webCamTexture.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (webCamTexture != null)
|
||||
{
|
||||
webCamTexture.Stop();
|
||||
}
|
||||
webCamTexture = null;
|
||||
}
|
||||
|
||||
public override Texture GetCurrentTexture()
|
||||
{
|
||||
return webCamTexture;
|
||||
}
|
||||
|
||||
private ResolutionStruct GetDefaultResolution()
|
||||
{
|
||||
var resolutions = availableResolutions;
|
||||
return resolutions == null || resolutions.Length == 0 ? new ResolutionStruct() : resolutions.OrderBy(resolution => resolution, new ResolutionStructComparer(_preferableDefaultWidth)).First();
|
||||
}
|
||||
|
||||
private void InitializeWebCamTexture()
|
||||
{
|
||||
Stop();
|
||||
if (webCamDevice is WebCamDevice valueOfWebCamDevice)
|
||||
{
|
||||
webCamTexture = new WebCamTexture(valueOfWebCamDevice.name, resolution.width, resolution.height, (int)resolution.frameRate);
|
||||
return;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot initialize WebCamTexture because WebCamDevice is not selected");
|
||||
}
|
||||
|
||||
private IEnumerator WaitForWebCamTexture()
|
||||
{
|
||||
const int timeoutFrame = 2000;
|
||||
var count = 0;
|
||||
Logger.LogVerbose("Waiting for WebCamTexture to start");
|
||||
yield return new WaitUntil(() => count++ > timeoutFrame || webCamTexture.width > 16);
|
||||
|
||||
if (webCamTexture.width <= 16)
|
||||
{
|
||||
throw new TimeoutException("Failed to start WebCam");
|
||||
}
|
||||
}
|
||||
|
||||
private class ResolutionStructComparer : IComparer<ResolutionStruct>
|
||||
{
|
||||
private readonly int _preferableDefaultWidth;
|
||||
|
||||
public ResolutionStructComparer(int preferableDefaultWidth)
|
||||
{
|
||||
_preferableDefaultWidth = preferableDefaultWidth;
|
||||
}
|
||||
|
||||
public int Compare(ResolutionStruct a, ResolutionStruct b)
|
||||
{
|
||||
var aDiff = Mathf.Abs(a.width - _preferableDefaultWidth);
|
||||
var bDiff = Mathf.Abs(b.width - _preferableDefaultWidth);
|
||||
if (aDiff != bDiff)
|
||||
{
|
||||
return aDiff - bDiff;
|
||||
}
|
||||
if (a.height != b.height)
|
||||
{
|
||||
// prefer smaller height
|
||||
return a.height - b.height;
|
||||
}
|
||||
// prefer smaller frame rate
|
||||
return (int)(a.frameRate - b.frameRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 498146e99d4934673bd948c8be11227e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/MediaPipeUnity/Common/Scripts/ImageSourceProvider.cs
Normal file
45
Assets/MediaPipeUnity/Common/Scripts/ImageSourceProvider.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2021 homuler
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public static class ImageSourceProvider
|
||||
{
|
||||
private static ImageSource _ImageSource;
|
||||
public static ImageSource ImageSource
|
||||
{
|
||||
get => _ImageSource;
|
||||
set
|
||||
{
|
||||
if (value != null && !value.enabled)
|
||||
{
|
||||
value.enabled = true;
|
||||
}
|
||||
_ImageSource = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static ImageSourceType CurrentSourceType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ImageSource is WebCamSource)
|
||||
{
|
||||
return ImageSourceType.WebCamera;
|
||||
}
|
||||
if (_ImageSource is StaticImageSource)
|
||||
{
|
||||
return ImageSourceType.Image;
|
||||
}
|
||||
if (_ImageSource is VideoSource)
|
||||
{
|
||||
return ImageSourceType.Video;
|
||||
}
|
||||
return ImageSourceType.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 703949cb153f043aca7381a8f9b21a86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
131
Assets/MediaPipeUnity/Common/Scripts/ImageSourceSolution.cs
Normal file
131
Assets/MediaPipeUnity/Common/Scripts/ImageSourceSolution.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
// 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;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public abstract class ImageSourceSolution<T> : Solution where T : GraphRunner
|
||||
{
|
||||
[SerializeField] protected Screen screen;
|
||||
[SerializeField] protected T graphRunner;
|
||||
[SerializeField] protected TextureFramePool textureFramePool;
|
||||
|
||||
private Coroutine _coroutine;
|
||||
|
||||
public RunningMode runningMode;
|
||||
|
||||
public long timeoutMillisec
|
||||
{
|
||||
get => graphRunner.timeoutMillisec;
|
||||
set => graphRunner.timeoutMillisec = value;
|
||||
}
|
||||
|
||||
public override void Play()
|
||||
{
|
||||
if (_coroutine != null)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
base.Play();
|
||||
_coroutine = StartCoroutine(Run());
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
base.Pause();
|
||||
ImageSourceProvider.ImageSource.Pause();
|
||||
}
|
||||
|
||||
public override void Resume()
|
||||
{
|
||||
base.Resume();
|
||||
var _ = StartCoroutine(ImageSourceProvider.ImageSource.Resume());
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
base.Stop();
|
||||
StopCoroutine(_coroutine);
|
||||
ImageSourceProvider.ImageSource.Stop();
|
||||
graphRunner.Stop();
|
||||
}
|
||||
|
||||
private IEnumerator Run()
|
||||
{
|
||||
var graphInitRequest = graphRunner.WaitForInit(runningMode);
|
||||
var imageSource = ImageSourceProvider.ImageSource;
|
||||
|
||||
yield return imageSource.Play();
|
||||
|
||||
if (!imageSource.isPrepared)
|
||||
{
|
||||
Logger.LogError(TAG, "Failed to start ImageSource, exiting...");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Use RGBA32 as the input format.
|
||||
// TODO: When using GpuBuffer, MediaPipe assumes that the input format is BGRA, so the following code must be fixed.
|
||||
textureFramePool.ResizeTexture(imageSource.textureWidth, imageSource.textureHeight, TextureFormat.RGBA32);
|
||||
SetupScreen(imageSource);
|
||||
|
||||
yield return graphInitRequest;
|
||||
if (graphInitRequest.isError)
|
||||
{
|
||||
Logger.LogError(TAG, graphInitRequest.error);
|
||||
yield break;
|
||||
}
|
||||
|
||||
OnStartRun();
|
||||
graphRunner.StartRun(imageSource);
|
||||
|
||||
var waitWhilePausing = new WaitWhile(() => isPaused);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isPaused)
|
||||
{
|
||||
yield return waitWhilePausing;
|
||||
}
|
||||
|
||||
if (!textureFramePool.TryGetTextureFrame(out var textureFrame))
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy current image to TextureFrame
|
||||
ReadFromImageSource(imageSource, textureFrame);
|
||||
AddTextureFrameToInputStream(textureFrame);
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
if (runningMode.IsSynchronous())
|
||||
{
|
||||
RenderCurrentFrame(textureFrame);
|
||||
yield return WaitForNextValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetupScreen(ImageSource imageSource)
|
||||
{
|
||||
// NOTE: The screen will be resized later, keeping the aspect ratio.
|
||||
screen.Initialize(imageSource);
|
||||
}
|
||||
|
||||
protected virtual void RenderCurrentFrame(TextureFrame textureFrame)
|
||||
{
|
||||
screen.ReadSync(textureFrame);
|
||||
}
|
||||
|
||||
protected abstract void OnStartRun();
|
||||
|
||||
protected abstract void AddTextureFrameToInputStream(TextureFrame textureFrame);
|
||||
|
||||
protected abstract IEnumerator WaitForNextValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7573abd3c3f9972e964d5c526c34e0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
Assets/MediaPipeUnity/Common/Scripts/InferenceMode.cs
Normal file
15
Assets/MediaPipeUnity/Common/Scripts/InferenceMode.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// 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
|
||||
{
|
||||
[System.Serializable]
|
||||
public enum InferenceMode
|
||||
{
|
||||
GPU,
|
||||
CPU,
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/InferenceMode.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/InferenceMode.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d95cf1e3a8fe6741295a3b42972dec58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
264
Assets/MediaPipeUnity/Common/Scripts/MemoizedLogger.cs
Normal file
264
Assets/MediaPipeUnity/Common/Scripts/MemoizedLogger.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
// 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 UnityEngine;
|
||||
|
||||
using LogLevel = Mediapipe.Unity.Logger.LogLevel;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class MemoizedLogger : IExtendedLogger
|
||||
{
|
||||
public readonly struct LogStruct
|
||||
{
|
||||
public readonly LogLevel logLevel;
|
||||
public readonly string tag;
|
||||
public readonly object message;
|
||||
public readonly DateTime utcTime;
|
||||
|
||||
public LogStruct(LogLevel logLevel, string tag, object message)
|
||||
{
|
||||
this.logLevel = logLevel;
|
||||
this.tag = tag;
|
||||
this.message = message;
|
||||
utcTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public LogStruct(LogType logType, string tag, object message) : this(GetLogLevelFromLogType(logType), tag, message) { }
|
||||
|
||||
private static LogLevel GetLogLevelFromLogType(LogType logType)
|
||||
{
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Error:
|
||||
case LogType.Exception:
|
||||
{
|
||||
return LogLevel.Error;
|
||||
}
|
||||
case LogType.Warning:
|
||||
{
|
||||
return LogLevel.Warn;
|
||||
}
|
||||
case LogType.Assert:
|
||||
case LogType.Log:
|
||||
default:
|
||||
{
|
||||
return LogLevel.Info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MemoizedLogger(int historySize = 0)
|
||||
{
|
||||
this.historySize = historySize;
|
||||
}
|
||||
|
||||
private int _historySize;
|
||||
public int historySize
|
||||
{
|
||||
get => _historySize;
|
||||
set
|
||||
{
|
||||
_historySize = value;
|
||||
|
||||
while (_historySize < histories.Count)
|
||||
{
|
||||
var _ = histories.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<LogStruct> _histories;
|
||||
public Queue<LogStruct> histories
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_histories == null)
|
||||
{
|
||||
_histories = new Queue<LogStruct>(_historySize);
|
||||
}
|
||||
return _histories;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void LogOutputEventHandler(LogStruct logStruct);
|
||||
public event LogOutputEventHandler OnLogOutput;
|
||||
|
||||
private readonly ILogger _logger = Debug.unityLogger;
|
||||
|
||||
public LogType filterLogType
|
||||
{
|
||||
get => _logger.filterLogType;
|
||||
set => _logger.filterLogType = value;
|
||||
}
|
||||
|
||||
public bool logEnabled
|
||||
{
|
||||
get => _logger.logEnabled;
|
||||
set => _logger.logEnabled = value;
|
||||
}
|
||||
|
||||
public ILogHandler logHandler
|
||||
{
|
||||
get => _logger.logHandler;
|
||||
set => _logger.logHandler = value;
|
||||
}
|
||||
|
||||
public bool IsLogTypeAllowed(LogType logType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log(object message)
|
||||
{
|
||||
_logger.Log(message);
|
||||
RecordInfoLog(null, message);
|
||||
}
|
||||
|
||||
public void Log(string tag, object message)
|
||||
{
|
||||
_logger.Log(tag, message);
|
||||
RecordInfoLog(tag, message);
|
||||
}
|
||||
|
||||
public void Log(string tag, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.Log(tag, message, context);
|
||||
RecordInfoLog(tag, message);
|
||||
}
|
||||
|
||||
public void Log(LogType logType, object message)
|
||||
{
|
||||
_logger.Log(logType, message);
|
||||
RecordLog(logType, null, message);
|
||||
}
|
||||
|
||||
public void Log(LogType logType, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.Log(logType, message, context);
|
||||
RecordLog(logType, null, message);
|
||||
}
|
||||
|
||||
public void Log(LogType logType, string tag, object message)
|
||||
{
|
||||
_logger.Log(logType, tag, message);
|
||||
RecordLog(logType, tag, message);
|
||||
}
|
||||
|
||||
public void Log(LogType logType, string tag, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.Log(logType, tag, message, context);
|
||||
RecordLog(logType, tag, message);
|
||||
}
|
||||
|
||||
public void Log(LogLevel logLevel, string tag, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.Log(logLevel.GetLogType(), tag, message, context);
|
||||
RecordLog(new LogStruct(logLevel, tag, message));
|
||||
}
|
||||
|
||||
public void Log(LogLevel logLevel, string tag, object message)
|
||||
{
|
||||
_logger.Log(logLevel.GetLogType(), tag, message);
|
||||
RecordLog(new LogStruct(logLevel, tag, message));
|
||||
}
|
||||
|
||||
public void Log(LogLevel logLevel, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.Log(logLevel.GetLogType(), message, context);
|
||||
RecordLog(new LogStruct(logLevel, null, message));
|
||||
}
|
||||
|
||||
public void Log(LogLevel logLevel, object message)
|
||||
{
|
||||
_logger.Log(logLevel.GetLogType(), message);
|
||||
RecordLog(new LogStruct(logLevel, null, message));
|
||||
}
|
||||
|
||||
public void LogWarning(string tag, object message)
|
||||
{
|
||||
_logger.LogWarning(tag, message);
|
||||
RecordWarnLog(tag, message);
|
||||
}
|
||||
|
||||
public void LogWarning(string tag, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.LogWarning(tag, message, context);
|
||||
RecordWarnLog(tag, message);
|
||||
}
|
||||
|
||||
public void LogError(string tag, object message)
|
||||
{
|
||||
_logger.LogError(tag, message);
|
||||
RecordErrorLog(tag, message);
|
||||
}
|
||||
|
||||
public void LogError(string tag, object message, UnityEngine.Object context)
|
||||
{
|
||||
_logger.LogError(tag, message, context);
|
||||
RecordErrorLog(tag, message);
|
||||
}
|
||||
|
||||
public void LogFormat(LogType logType, string format, params object[] args)
|
||||
{
|
||||
_logger.LogFormat(logType, format, args);
|
||||
}
|
||||
|
||||
public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args)
|
||||
{
|
||||
_logger.LogFormat(logType, context, format, args);
|
||||
}
|
||||
|
||||
public void LogException(Exception exception)
|
||||
{
|
||||
_logger.LogException(exception);
|
||||
RecordErrorLog(null, exception);
|
||||
}
|
||||
|
||||
public void LogException(Exception exception, UnityEngine.Object context)
|
||||
{
|
||||
_logger.LogException(exception, context);
|
||||
RecordErrorLog(null, exception);
|
||||
}
|
||||
|
||||
public void RecordLog(LogStruct log)
|
||||
{
|
||||
lock (((ICollection)histories).SyncRoot)
|
||||
{
|
||||
while (histories.Count > 0 && _historySize <= histories.Count)
|
||||
{
|
||||
var _ = histories.Dequeue();
|
||||
}
|
||||
histories.Enqueue(log);
|
||||
OnLogOutput?.Invoke(log);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordLog(LogType logType, string tag, object message)
|
||||
{
|
||||
RecordLog(new LogStruct(logType, tag, message));
|
||||
}
|
||||
|
||||
private void RecordInfoLog(string tag, object message)
|
||||
{
|
||||
RecordLog(new LogStruct(LogLevel.Info, tag, message));
|
||||
}
|
||||
|
||||
private void RecordWarnLog(string tag, object message)
|
||||
{
|
||||
RecordLog(new LogStruct(LogLevel.Warn, tag, message));
|
||||
}
|
||||
|
||||
private void RecordErrorLog(string tag, object message)
|
||||
{
|
||||
RecordLog(new LogStruct(LogLevel.Error, tag, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/MemoizedLogger.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/MemoizedLogger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00164446b902a4f99ae323de716782fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Assets/MediaPipeUnity/Common/Scripts/RunningMode.cs
Normal file
24
Assets/MediaPipeUnity/Common/Scripts/RunningMode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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
|
||||
{
|
||||
[System.Serializable]
|
||||
public enum RunningMode
|
||||
{
|
||||
Async,
|
||||
NonBlockingSync,
|
||||
Sync,
|
||||
}
|
||||
|
||||
public static class RunningModeExtension
|
||||
{
|
||||
public static bool IsSynchronous(this RunningMode runningMode)
|
||||
{
|
||||
return runningMode == RunningMode.Sync || runningMode == RunningMode.NonBlockingSync;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/RunningMode.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/RunningMode.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e82590f067b6f1b96ac9dadddcd9ab26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
98
Assets/MediaPipeUnity/Common/Scripts/Screen.cs
Normal file
98
Assets/MediaPipeUnity/Common/Scripts/Screen.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2021 homuler
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class Screen : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private RawImage _screen;
|
||||
|
||||
private ImageSource _imageSource;
|
||||
|
||||
public Texture texture
|
||||
{
|
||||
private get => _screen.texture;
|
||||
set => _screen.texture = value;
|
||||
}
|
||||
|
||||
public UnityEngine.Rect uvRect
|
||||
{
|
||||
set => _screen.uvRect = value;
|
||||
}
|
||||
|
||||
public void Initialize(ImageSource imageSource)
|
||||
{
|
||||
_imageSource = imageSource;
|
||||
|
||||
Resize(_imageSource.textureWidth, _imageSource.textureHeight);
|
||||
Rotate(_imageSource.rotation.Reverse());
|
||||
ResetUvRect(RunningMode.Async);
|
||||
texture = imageSource.GetCurrentTexture();
|
||||
}
|
||||
|
||||
public void Resize(int width, int height)
|
||||
{
|
||||
_screen.rectTransform.sizeDelta = new Vector2(width, height);
|
||||
}
|
||||
|
||||
public void Rotate(RotationAngle rotationAngle)
|
||||
{
|
||||
_screen.rectTransform.localEulerAngles = rotationAngle.GetEulerAngles();
|
||||
}
|
||||
|
||||
public void ReadSync(TextureFrame textureFrame)
|
||||
{
|
||||
if (!(texture is Texture2D))
|
||||
{
|
||||
texture = new Texture2D(_imageSource.textureWidth, _imageSource.textureHeight, TextureFormat.RGBA32, false);
|
||||
ResetUvRect(RunningMode.Sync);
|
||||
}
|
||||
textureFrame.CopyTexture(texture);
|
||||
}
|
||||
|
||||
private void ResetUvRect(RunningMode runningMode)
|
||||
{
|
||||
var rect = new UnityEngine.Rect(0, 0, 1, 1);
|
||||
|
||||
if (_imageSource.isVerticallyFlipped && runningMode == RunningMode.Async)
|
||||
{
|
||||
// In Async mode, we don't need to flip the screen vertically since the image will be copied on CPU.
|
||||
rect = FlipVertically(rect);
|
||||
}
|
||||
|
||||
if (_imageSource.isFrontFacing)
|
||||
{
|
||||
// Flip the image (not the screen) horizontally.
|
||||
// It should be taken into account that the image will be rotated later.
|
||||
var rotation = _imageSource.rotation;
|
||||
|
||||
if (rotation == RotationAngle.Rotation0 || rotation == RotationAngle.Rotation180)
|
||||
{
|
||||
rect = FlipHorizontally(rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
rect = FlipVertically(rect);
|
||||
}
|
||||
}
|
||||
|
||||
uvRect = rect;
|
||||
}
|
||||
|
||||
private UnityEngine.Rect FlipHorizontally(UnityEngine.Rect rect)
|
||||
{
|
||||
return new UnityEngine.Rect(1 - rect.x, rect.y, -rect.width, rect.height);
|
||||
}
|
||||
|
||||
private UnityEngine.Rect FlipVertically(UnityEngine.Rect rect)
|
||||
{
|
||||
return new UnityEngine.Rect(rect.x, 1 - rect.y, rect.width, -rect.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/Screen.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/Screen.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 827c4431af677e057aa6f14170d0785c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
117
Assets/MediaPipeUnity/Common/Scripts/Solution.cs
Normal file
117
Assets/MediaPipeUnity/Common/Scripts/Solution.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public abstract class Solution : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
// TODO: make it static
|
||||
protected virtual string TAG => GetType().Name;
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public Bootstrap bootstrap;
|
||||
protected bool isPaused;
|
||||
|
||||
protected virtual IEnumerator Start()
|
||||
{
|
||||
bootstrap = FindBootstrap();
|
||||
yield return new WaitUntil(() => bootstrap.isFinished);
|
||||
|
||||
Play();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the main program from the beginning.
|
||||
/// </summary>
|
||||
public virtual void Play()
|
||||
{
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause the main program.
|
||||
/// <summary>
|
||||
public virtual void Pause()
|
||||
{
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume the main program.
|
||||
/// If the main program has not begun, it'll do nothing.
|
||||
/// </summary>
|
||||
public virtual void Resume()
|
||||
{
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the main program.
|
||||
/// </summary>
|
||||
public virtual void Stop()
|
||||
{
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
protected static void SetupAnnotationController<T>(AnnotationController<T> annotationController, ImageSource imageSource, bool expectedToBeMirrored = false) where T : HierarchicalAnnotation
|
||||
{
|
||||
annotationController.isMirrored = expectedToBeMirrored ^ imageSource.isHorizontallyFlipped ^ imageSource.isFrontFacing;
|
||||
annotationController.rotationAngle = imageSource.rotation.Reverse();
|
||||
}
|
||||
|
||||
protected static void ReadFromImageSource(ImageSource imageSource, TextureFrame textureFrame)
|
||||
{
|
||||
var sourceTexture = imageSource.GetCurrentTexture();
|
||||
|
||||
// For some reason, when the image is coiped on GPU, latency tends to be high.
|
||||
// So even when OpenGL ES is available, use CPU to copy images.
|
||||
var textureType = sourceTexture.GetType();
|
||||
|
||||
if (textureType == typeof(WebCamTexture))
|
||||
{
|
||||
textureFrame.ReadTextureFromOnCPU((WebCamTexture)sourceTexture);
|
||||
}
|
||||
else if (textureType == typeof(Texture2D))
|
||||
{
|
||||
textureFrame.ReadTextureFromOnCPU((Texture2D)sourceTexture);
|
||||
}
|
||||
else
|
||||
{
|
||||
textureFrame.ReadTextureFromOnCPU(sourceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
protected Bootstrap FindBootstrap()
|
||||
{
|
||||
var bootstrapObj = GameObject.Find("Bootstrap");
|
||||
|
||||
if (bootstrapObj != null)
|
||||
{
|
||||
return bootstrapObj.GetComponent<Bootstrap>();
|
||||
}
|
||||
|
||||
Logger.LogWarning(TAG, "Global Bootstrap instance is not found (maybe running a sample scene directly), "
|
||||
+ "so activating a fallback Bootstrap instance attached to each Solution object");
|
||||
|
||||
var bootstrap = GetComponent<Bootstrap>();
|
||||
bootstrap.enabled = true;
|
||||
|
||||
// hide menu button when trying a single scene.
|
||||
DisableMenuButton();
|
||||
return bootstrap;
|
||||
}
|
||||
|
||||
private void DisableMenuButton()
|
||||
{
|
||||
var menuButton = GameObject.Find("MenuButton");
|
||||
menuButton.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/Solution.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/Solution.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 589adc9f9488f9d8eaee5a408719b452
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/MediaPipeUnity/Common/Scripts/StartSceneController.cs
Normal file
36
Assets/MediaPipeUnity/Common/Scripts/StartSceneController.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// 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;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class StartSceneController : MonoBehaviour
|
||||
{
|
||||
private const string _TAG = nameof(Bootstrap);
|
||||
|
||||
[SerializeField] private Image _screen;
|
||||
[SerializeField] private GameObject _consolePrefab;
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
var _ = Instantiate(_consolePrefab, _screen.transform);
|
||||
|
||||
var bootstrap = GetComponent<Bootstrap>();
|
||||
|
||||
yield return new WaitUntil(() => bootstrap.isFinished);
|
||||
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
Logger.LogInfo(_TAG, "Loading the first scene...");
|
||||
var sceneLoadReq = SceneManager.LoadSceneAsync(1);
|
||||
yield return new WaitUntil(() => sceneLoadReq.isDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ca6b5afaa693af02957f0d68cf058d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
87
Assets/MediaPipeUnity/Common/Scripts/WaitForResult.cs
Normal file
87
Assets/MediaPipeUnity/Common/Scripts/WaitForResult.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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 Stopwatch = System.Diagnostics.Stopwatch;
|
||||
|
||||
namespace Mediapipe.Unity
|
||||
{
|
||||
public class WaitForResult : CustomYieldInstruction
|
||||
{
|
||||
public object result { get; private set; }
|
||||
|
||||
protected object tmpResult;
|
||||
protected bool isDone = false;
|
||||
|
||||
private readonly MonoBehaviour _runner;
|
||||
private readonly IEnumerator _inner;
|
||||
private readonly Coroutine _coroutine;
|
||||
|
||||
public bool isError { get; private set; } = false;
|
||||
public Exception error { get; private set; }
|
||||
public override bool keepWaiting => !isDone && !isError;
|
||||
|
||||
public WaitForResult(MonoBehaviour runner, IEnumerator inner, long timeoutMillisec = long.MaxValue)
|
||||
{
|
||||
_runner = runner;
|
||||
_inner = inner;
|
||||
_coroutine = runner.StartCoroutine(Run(timeoutMillisec));
|
||||
}
|
||||
|
||||
private IEnumerator Run(long timeoutMillisec)
|
||||
{
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (stopwatch.ElapsedMilliseconds > timeoutMillisec)
|
||||
{
|
||||
_runner.StopCoroutine(_coroutine);
|
||||
throw new TimeoutException($"{stopwatch.ElapsedMilliseconds}ms has passed");
|
||||
}
|
||||
if (!_inner.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
tmpResult = _inner.Current;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
isError = true;
|
||||
error = e;
|
||||
yield break;
|
||||
}
|
||||
yield return tmpResult;
|
||||
}
|
||||
Done(tmpResult);
|
||||
}
|
||||
|
||||
protected virtual void Done(object result)
|
||||
{
|
||||
this.result = result;
|
||||
isDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class WaitForResult<T> : WaitForResult
|
||||
{
|
||||
public new T result { get; private set; }
|
||||
|
||||
public WaitForResult(MonoBehaviour runner, IEnumerator inner, long timeoutMillisec = long.MaxValue) : base(runner, inner, timeoutMillisec) { }
|
||||
|
||||
protected override void Done(object result)
|
||||
{
|
||||
this.result = (T)result;
|
||||
isDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Common/Scripts/WaitForResult.cs.meta
Normal file
11
Assets/MediaPipeUnity/Common/Scripts/WaitForResult.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 374b5ec183b8225d0ac8b0664812f5f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
730
Assets/MediaPipeUnity/WeSign_extractor.unity
Normal file
730
Assets/MediaPipeUnity/WeSign_extractor.unity
Normal file
@@ -0,0 +1,730 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 9
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 12
|
||||
m_GIWorkflowMode: 1
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_FinalGather: 0
|
||||
m_FinalGatherFiltering: 1
|
||||
m_FinalGatherRayCount: 256
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 2
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
accuratePlacement: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &560904344
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 560904345}
|
||||
- component: {fileID: 560904348}
|
||||
- component: {fileID: 560904347}
|
||||
- component: {fileID: 560904346}
|
||||
m_Layer: 5
|
||||
m_Name: Screen
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &560904345
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 560904344}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1475592761}
|
||||
m_Father: {fileID: 884590458}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &560904346
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 560904344}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 827c4431af677e057aa6f14170d0785c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_screen: {fileID: 0}
|
||||
--- !u!114 &560904347
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 560904344}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Texture: {fileID: 0}
|
||||
m_UVRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
--- !u!222 &560904348
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 560904344}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &858349201
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 858349204}
|
||||
- component: {fileID: 858349203}
|
||||
- component: {fileID: 858349202}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &858349202
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 858349201}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_SendPointerHoverToParent: 1
|
||||
m_HorizontalAxis: Horizontal
|
||||
m_VerticalAxis: Vertical
|
||||
m_SubmitButton: Submit
|
||||
m_CancelButton: Cancel
|
||||
m_InputActionsPerSecond: 10
|
||||
m_RepeatDelay: 0.5
|
||||
m_ForceModuleActive: 0
|
||||
--- !u!114 &858349203
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 858349201}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 10
|
||||
--- !u!4 &858349204
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 858349201}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 4
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &869147449
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 869147450}
|
||||
- component: {fileID: 869147451}
|
||||
m_Layer: 0
|
||||
m_Name: OfficialSolution
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &869147450
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 869147449}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &869147451
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 869147449}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 043ccd99cf82b3cc9bf2e00956ce2b93, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_configAsset: {fileID: 4900000, guid: 6288c43cdca97374782dac1ea87aa029, type: 3}
|
||||
_screen: {fileID: 560904347}
|
||||
_width: 640
|
||||
_height: 480
|
||||
_fps: 30
|
||||
_poseLandmarkListAnnotationController: {fileID: 1475592763}
|
||||
--- !u!1 &884590454
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 884590458}
|
||||
- component: {fileID: 884590457}
|
||||
- component: {fileID: 884590456}
|
||||
- component: {fileID: 884590455}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &884590455
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 884590454}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &884590456
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 884590454}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!223 &884590457
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 884590454}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 1
|
||||
m_Camera: {fileID: 1522608648}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!224 &884590458
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 884590454}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 560904345}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!1001 &937709944
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
m_TransformParent: {fileID: 1475592761}
|
||||
m_Modifications:
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_RootOrder
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1915238444563462411, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: PoseLandmarkList Annotation
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
--- !u!4 &937709945 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 1915238444563462410, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
m_PrefabInstance: {fileID: 937709944}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &1475592760
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1475592761}
|
||||
- component: {fileID: 1475592763}
|
||||
m_Layer: 5
|
||||
m_Name: AnnotationLayer
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1475592761
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1475592760}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 937709945}
|
||||
m_Father: {fileID: 560904345}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1475592763
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1475592760}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 70c2b36b394190968977c6493e60e0af, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
annotation: {fileID: 2100643019}
|
||||
_visualizeZ: 0
|
||||
--- !u!1 &1522608646
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1522608649}
|
||||
- component: {fileID: 1522608648}
|
||||
- component: {fileID: 1522608647}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &1522608647
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1522608646}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &1522608648
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1522608646}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_FocalLength: 50
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &1522608649
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1522608646}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1802405420
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1802405422}
|
||||
- component: {fileID: 1802405421}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1802405421
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1802405420}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 10
|
||||
m_Type: 1
|
||||
m_Shape: 0
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
--- !u!4 &1802405422
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1802405420}
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!114 &2100643019 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 1915238444563462421, guid: 4418f6a92856c5b51b58a36e3be7ed5c, type: 3}
|
||||
m_PrefabInstance: {fileID: 937709944}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 39bac9dd52c31ae7aa01a7383bc44853, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
7
Assets/MediaPipeUnity/WeSign_extractor.unity.meta
Normal file
7
Assets/MediaPipeUnity/WeSign_extractor.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14d8a9174a4263d83bdc4e90eb9a2ef7
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
94
Assets/MediaPipeUnity/WeSign_extractor_cpu.txt
Normal file
94
Assets/MediaPipeUnity/WeSign_extractor_cpu.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
# Copyright 2019 The MediaPipe Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Copied from mediapipe/graphs/holistic_tracking/holistic_tracking_gpu.pbtxt
|
||||
#
|
||||
# CHANGES:
|
||||
# - Add ImageTransformationCalculator and rotate the input
|
||||
# - Remove AnnotationOverlayCalculator
|
||||
|
||||
# Tracks and renders pose + hands + face landmarks.
|
||||
|
||||
# CPU image. (ImageFrame)
|
||||
input_stream: "input_video"
|
||||
|
||||
output_stream: "pose_landmarks"
|
||||
output_stream: "pose_world_landmarks"
|
||||
output_stream: "segmentation_mask"
|
||||
output_stream: "pose_roi"
|
||||
output_stream: "pose_detection"
|
||||
output_stream: "face_landmarks"
|
||||
output_stream: "left_hand_landmarks"
|
||||
output_stream: "right_hand_landmarks"
|
||||
|
||||
# Throttles the images flowing downstream for flow control. It passes through
|
||||
# the very first incoming image unaltered, and waits for downstream nodes
|
||||
# (calculators and subgraphs) in the graph to finish their tasks before it
|
||||
# passes through another image. All images that come in while waiting are
|
||||
# dropped, limiting the number of in-flight images in most part of the graph to
|
||||
# 1. This prevents the downstream nodes from queuing up incoming images and data
|
||||
# excessively, which leads to increased latency and memory usage, unwanted in
|
||||
# real-time mobile applications. It also eliminates unnecessarily computation,
|
||||
# e.g., the output produced by a node may get dropped downstream if the
|
||||
# subsequent nodes are still busy processing previous inputs.
|
||||
node {
|
||||
calculator: "FlowLimiterCalculator"
|
||||
input_stream: "input_video"
|
||||
input_stream: "FINISHED:pose_landmarks"
|
||||
input_stream_info: {
|
||||
tag_index: "FINISHED"
|
||||
back_edge: true
|
||||
}
|
||||
output_stream: "throttled_input_video"
|
||||
node_options: {
|
||||
[type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
|
||||
max_in_flight: 1
|
||||
max_in_queue: 1
|
||||
# Timeout is disabled (set to 0) as first frame processing can take more
|
||||
# than 1 second.
|
||||
in_flight_timeout: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node: {
|
||||
calculator: "ImageTransformationCalculator"
|
||||
input_stream: "IMAGE:throttled_input_video"
|
||||
output_stream: "IMAGE:transformed_input_video"
|
||||
node_options: {
|
||||
[type.googleapis.com/mediapipe.ImageTransformationCalculatorOptions] {
|
||||
flip_vertically: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
node {
|
||||
calculator: "HolisticLandmarkCpu"
|
||||
input_stream: "IMAGE:transformed_input_video"
|
||||
output_stream: "POSE_LANDMARKS:pose_landmarks"
|
||||
output_stream: "WORLD_LANDMARKS:pose_world_landmarks"
|
||||
output_stream: "SEGMENTATION_MASK:segmentation_mask_rotated"
|
||||
output_stream: "POSE_ROI:pose_roi"
|
||||
output_stream: "POSE_DETECTION:pose_detection"
|
||||
output_stream: "FACE_LANDMARKS:face_landmarks"
|
||||
output_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
|
||||
output_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
|
||||
}
|
||||
|
||||
node: {
|
||||
calculator: "ImageTransformationCalculator"
|
||||
input_stream: "IMAGE:segmentation_mask_rotated"
|
||||
output_stream: "IMAGE:segmentation_mask"
|
||||
}
|
||||
7
Assets/MediaPipeUnity/WeSign_extractor_cpu.txt.meta
Normal file
7
Assets/MediaPipeUnity/WeSign_extractor_cpu.txt.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6288c43cdca97374782dac1ea87aa029
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
101
Assets/MediaPipeUnity/WeSign_extractor_gpu.txt
Normal file
101
Assets/MediaPipeUnity/WeSign_extractor_gpu.txt
Normal file
@@ -0,0 +1,101 @@
|
||||
# Copyright 2019 The MediaPipe Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Copied from mediapipe/graphs/holistic_tracking/holistic_tracking_gpu.pbtxt
|
||||
#
|
||||
# CHANGES:
|
||||
# - `input_video` is ImageFrame (ImageFrameToGpuBufferCalculator converts it into GpuBuffer)
|
||||
# - Add ImageTransformationCalculator and rotate the input
|
||||
# - Remove AnnotationOverlayCalculator
|
||||
|
||||
# Tracks and renders pose + hands + face landmarks.
|
||||
|
||||
# ImageFrame
|
||||
input_stream: "input_video"
|
||||
|
||||
output_stream: "pose_landmarks"
|
||||
output_stream: "pose_world_landmarks"
|
||||
output_stream: "segmentation_mask"
|
||||
output_stream: "pose_roi"
|
||||
output_stream: "pose_detection"
|
||||
output_stream: "face_landmarks"
|
||||
output_stream: "left_hand_landmarks"
|
||||
output_stream: "right_hand_landmarks"
|
||||
|
||||
# Throttles the images flowing downstream for flow control. It passes through
|
||||
# the very first incoming image unaltered, and waits for downstream nodes
|
||||
# (calculators and subgraphs) in the graph to finish their tasks before it
|
||||
# passes through another image. All images that come in while waiting are
|
||||
# dropped, limiting the number of in-flight images in most part of the graph to
|
||||
# 1. This prevents the downstream nodes from queuing up incoming images and data
|
||||
# excessively, which leads to increased latency and memory usage, unwanted in
|
||||
# real-time mobile applications. It also eliminates unnecessarily computation,
|
||||
# e.g., the output produced by a node may get dropped downstream if the
|
||||
# subsequent nodes are still busy processing previous inputs.
|
||||
node {
|
||||
calculator: "FlowLimiterCalculator"
|
||||
input_stream: "input_video"
|
||||
input_stream: "FINISHED:pose_landmarks"
|
||||
input_stream_info: {
|
||||
tag_index: "FINISHED"
|
||||
back_edge: true
|
||||
}
|
||||
output_stream: "throttled_input_video"
|
||||
node_options: {
|
||||
[type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
|
||||
max_in_flight: 1
|
||||
max_in_queue: 1
|
||||
# Timeout is disabled (set to 0) as first frame processing can take more
|
||||
# than 1 second.
|
||||
in_flight_timeout: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node: {
|
||||
calculator: "ImageFrameToGpuBufferCalculator"
|
||||
input_stream: "throttled_input_video"
|
||||
output_stream: "throttled_input_video_gpu"
|
||||
}
|
||||
|
||||
node: {
|
||||
calculator: "ImageTransformationCalculator"
|
||||
input_stream: "IMAGE_GPU:throttled_input_video_gpu"
|
||||
output_stream: "IMAGE_GPU:transformed_input_video"
|
||||
}
|
||||
|
||||
node {
|
||||
calculator: "HolisticLandmarkGpu"
|
||||
input_stream: "IMAGE:transformed_input_video"
|
||||
output_stream: "POSE_LANDMARKS:pose_landmarks"
|
||||
output_stream: "WORLD_LANDMARKS:pose_world_landmarks"
|
||||
output_stream: "SEGMENTATION_MASK:segmentation_mask_gpu"
|
||||
output_stream: "POSE_ROI:pose_roi"
|
||||
output_stream: "POSE_DETECTION:pose_detection"
|
||||
output_stream: "FACE_LANDMARKS:face_landmarks"
|
||||
output_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
|
||||
output_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
|
||||
}
|
||||
|
||||
node: {
|
||||
calculator: "ImageTransformationCalculator"
|
||||
input_stream: "IMAGE_GPU:segmentation_mask_gpu"
|
||||
output_stream: "IMAGE_GPU:segmentation_mask_unrotated_gpu"
|
||||
}
|
||||
|
||||
node: {
|
||||
calculator: "GpuBufferToImageFrameCalculator"
|
||||
input_stream: "segmentation_mask_unrotated_gpu"
|
||||
output_stream: "segmentation_mask"
|
||||
}
|
||||
7
Assets/MediaPipeUnity/WeSign_extractor_gpu.txt.meta
Normal file
7
Assets/MediaPipeUnity/WeSign_extractor_gpu.txt.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ecf0a6acdaa4fe499c1339d2d4aeddb
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
202
Assets/MediaPipeUnity/Wesign_extractor.cs
Normal file
202
Assets/MediaPipeUnity/Wesign_extractor.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
// 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.
|
||||
|
||||
// ATTENTION!: This code is for a tutorial.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Mediapipe.Unity.CoordinateSystem;
|
||||
|
||||
|
||||
namespace Mediapipe.Unity.Tutorial
|
||||
{
|
||||
public class Wesign_extractor : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Config file to set up the graph
|
||||
/// </summary>
|
||||
[SerializeField] private TextAsset _configAsset;
|
||||
|
||||
/// <summary>
|
||||
/// The screen object on which the video is displayed
|
||||
/// </summary>
|
||||
[SerializeField] private RawImage _screen;
|
||||
|
||||
/// <summary>
|
||||
/// width of the screen
|
||||
/// </summary>
|
||||
[SerializeField] private int _width;
|
||||
|
||||
/// <summary>
|
||||
/// height of the screen
|
||||
/// </summary>
|
||||
[SerializeField] private int _height;
|
||||
|
||||
/// <summary>
|
||||
/// fps of the screen
|
||||
/// </summary>
|
||||
[SerializeField] private int _fps;
|
||||
|
||||
/// <summary>
|
||||
/// Landmark annotation controller to show the landmarks on the screen
|
||||
/// </summary>
|
||||
[SerializeField] private PoseLandmarkListAnnotationController _poseLandmarkListAnnotationController;
|
||||
|
||||
/// <summary>
|
||||
/// MediaPipe graph
|
||||
/// </summary>
|
||||
private CalculatorGraph _graph;
|
||||
|
||||
/// <summary>
|
||||
/// Resource manager for graph resources
|
||||
/// </summary>
|
||||
private ResourceManager _resourceManager;
|
||||
|
||||
/// <summary>
|
||||
/// Webcam texture
|
||||
/// </summary>
|
||||
private WebCamTexture _webCamTexture;
|
||||
|
||||
/// <summary>
|
||||
/// Input texture
|
||||
/// </summary>
|
||||
private Texture2D _inputTexture;
|
||||
|
||||
/// <summary>
|
||||
/// Screen pixel data
|
||||
/// </summary>
|
||||
private Color32[] _pixelData;
|
||||
|
||||
/// <summary>
|
||||
/// Stopwatch to give a timestamp to video frames
|
||||
/// </summary>
|
||||
private Stopwatch _stopwatch;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Google Mediapipe setup & run
|
||||
/// </summary>
|
||||
/// <returns> IEnumerator </returns>
|
||||
/// <exception cref="System.Exception"></exception>
|
||||
private IEnumerator Start()
|
||||
{
|
||||
// Webcam setup
|
||||
if (WebCamTexture.devices.Length == 0)
|
||||
{
|
||||
throw new System.Exception("Web Camera devices are not found");
|
||||
}
|
||||
var webCamDevice = WebCamTexture.devices[0];
|
||||
_webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps);
|
||||
_webCamTexture.Play();
|
||||
|
||||
yield return new WaitUntil(() => _webCamTexture.width > 16);
|
||||
|
||||
_screen.rectTransform.sizeDelta = new Vector2(_width, _height);
|
||||
_screen.texture = _webCamTexture;
|
||||
|
||||
// TODO this method is kinda meh you should use ImageFrame
|
||||
_inputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false);
|
||||
_pixelData = new Color32[_width * _height];
|
||||
|
||||
_resourceManager = new LocalResourceManager();
|
||||
yield return _resourceManager.PrepareAssetAsync("pose_detection.bytes");
|
||||
yield return _resourceManager.PrepareAssetAsync("pose_landmark_full.bytes");
|
||||
yield return _resourceManager.PrepareAssetAsync("face_landmark.bytes");
|
||||
yield return _resourceManager.PrepareAssetAsync("hand_landmark_full.bytes");
|
||||
yield return _resourceManager.PrepareAssetAsync("face_detection_short_range.bytes");
|
||||
yield return _resourceManager.PrepareAssetAsync("hand_recrop.bytes");
|
||||
yield return _resourceManager.PrepareAssetAsync("handedness.txt");
|
||||
|
||||
_stopwatch = new Stopwatch();
|
||||
|
||||
// Setting up the graph
|
||||
_graph = new CalculatorGraph(_configAsset.text);
|
||||
var posestream = new OutputStream<NormalizedLandmarkListPacket, NormalizedLandmarkList>(_graph, "pose_landmarks");
|
||||
var leftstream = new OutputStream<NormalizedLandmarkListPacket, NormalizedLandmarkList>(_graph, "left_hand_landmarks");
|
||||
var rightstream = new OutputStream<NormalizedLandmarkListPacket, NormalizedLandmarkList>(_graph, "right_hand_landmarks");
|
||||
posestream.StartPolling().AssertOk();
|
||||
leftstream.StartPolling().AssertOk();
|
||||
rightstream.StartPolling().AssertOk();
|
||||
_graph.StartRun().AssertOk();
|
||||
_stopwatch.Start();
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
_inputTexture.SetPixels32(_webCamTexture.GetPixels32(_pixelData));
|
||||
var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, _width, _height, _width * 4, _inputTexture.GetRawTextureData<byte>());
|
||||
var currentTimestamp = _stopwatch.ElapsedTicks / (System.TimeSpan.TicksPerMillisecond / 1000);
|
||||
_graph.AddPacketToInputStream("input_video", new ImageFramePacket(imageFrame, new Timestamp(currentTimestamp))).AssertOk();
|
||||
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
//posestream.TryGetNext(out var poseLandmarks);
|
||||
if (posestream.TryGetNext(out var poseLandmarks))
|
||||
{
|
||||
if (poseLandmarks != null)
|
||||
{
|
||||
// Draw the poseLandmarks on the screen
|
||||
_poseLandmarkListAnnotationController.DrawNow(poseLandmarks);
|
||||
var x = poseLandmarks.Landmark[0];
|
||||
UnityEngine.Debug.Log($"Pose Coordinates: {x}");
|
||||
|
||||
}
|
||||
}
|
||||
if (leftstream.TryGetNext(out var leftLandmarks))
|
||||
{
|
||||
if (leftLandmarks != null)
|
||||
{
|
||||
|
||||
var x = leftLandmarks.Landmark[0];
|
||||
UnityEngine.Debug.Log($"Pose left Coordinates: {x}");
|
||||
|
||||
}
|
||||
}
|
||||
if (rightstream.TryGetNext(out var rightLandmarks))
|
||||
{
|
||||
if (rightLandmarks != null)
|
||||
{
|
||||
|
||||
var x = rightLandmarks.Landmark[0];
|
||||
UnityEngine.Debug.Log($"Pose right Coordinates: {x}");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Propper destruction on the Mediapipegraph
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_webCamTexture != null)
|
||||
{
|
||||
_webCamTexture.Stop();
|
||||
}
|
||||
|
||||
if (_graph != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_graph.CloseInputStream("input_video").AssertOk();
|
||||
_graph.WaitUntilDone().AssertOk();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
_graph.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/MediaPipeUnity/Wesign_extractor.cs.meta
Normal file
11
Assets/MediaPipeUnity/Wesign_extractor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 043ccd99cf82b3cc9bf2e00956ce2b93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Packages/com.github.homuler.mediapipe/LICENSE.md
Normal file
21
Packages/com.github.homuler.mediapipe/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 homuler
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
7
Packages/com.github.homuler.mediapipe/LICENSE.md.meta
Normal file
7
Packages/com.github.homuler.mediapipe/LICENSE.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e728b176561fa96ed8fde03486bbb142
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0d7e8178b74a234b982e1c9e95b06e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 997abefdb7c9a1a469fea28ffa80e73a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Line
|
||||
m_Shader: {fileID: 207, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _InvFade: 1
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _TintColor: {r: 1, g: 0, b: 0, a: 0.5}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 207b29ccc237efe2f841a58fbf9e5478
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: SolidConvergence
|
||||
m_Shader: {fileID: 4800000, guid: 349b4f383ce8440988c29923e4684694, type: 3}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ea6416eaf3d1674291ec58a548378d8
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: SolidMaterial
|
||||
m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4cdc6c3db064fca08a172ce83ee9d12
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f57990152e91970499d1fd5e93c57d89
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e77bccec954d4a4a8aa3c1a6bb94d15
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88d74f24916764d31a81ea6cbfa4ba68
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9f725766db3aa9629b417fd51e31530
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a193c7ca3dc050c6d8b728e7e129f6a5
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cf05e630aa9cd6f38522c83264115ee
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a4c697151131b35db0748e319aed581
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2c612bb3b8652c34a610554ef621c35
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa86133ea84e644f09b246134dd08cb6
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abf39997153d3bc48b9af20bb8d2c75a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,2 @@
|
||||
Left
|
||||
Right
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca5d56c926737c2929c102575e703f4a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71b173c4b3503de4a8c9875972d880a4
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25f7f8d6c78414aa6b47573191f3cb13
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 393afd72cbd0d7781ab071f3d6fb38fe
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53a5a9537d1a5ea59a5ee2dd6f154af2
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cde808ca06049fbcbdbf351c0579181
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f05d0f79da53b48858ba17062530313b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 290ea52a92f4d637289ebae96906985b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
???
|
||||
Bicycle
|
||||
Boot
|
||||
Laptop
|
||||
Person
|
||||
Chair
|
||||
Cattle
|
||||
Desk
|
||||
Cat
|
||||
Computer mouse
|
||||
Computer monitor
|
||||
Box
|
||||
Mug
|
||||
Coffee cup
|
||||
Stationary bicycle
|
||||
Table
|
||||
Bottle
|
||||
High heels
|
||||
Vehicle
|
||||
Footwear
|
||||
Dog
|
||||
Book
|
||||
Camera
|
||||
Car
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c852a0cff35fa9d1b6598816e80af6e
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user