deleted a bunch of stuff I'm not making use of
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 697b139e69dcb414498dcd93210620ff
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 735f1f2cba9bb364892bc2c7e7a30c19
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRM;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class OpenSeeVRMExpression : MonoBehaviour
|
||||
{
|
||||
public OpenSee openSee;
|
||||
public OpenSeeExpression openSeeExpression;
|
||||
public VRMBlendShapeProxy vrmBlendShapeProxy;
|
||||
public string expressionNeutral = "neutral";
|
||||
public string expressionFun = "smile";
|
||||
public string expressionSorrow = "sad";
|
||||
public string expressionAngry = "angry";
|
||||
public string expressionJoy = "shocked";
|
||||
|
||||
private BlendShapeKey lastKey;
|
||||
|
||||
void Start()
|
||||
{
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (openSeeExpression == null || vrmBlendShapeProxy == null) {
|
||||
return;
|
||||
}
|
||||
float rightEyeOpen = 1f;
|
||||
float leftEyeOpen = 1f;
|
||||
if (openSee != null) {
|
||||
var openSeeData = openSee.trackingData;
|
||||
if (openSeeData == null || openSeeData.Length < 1)
|
||||
return;
|
||||
int idx = -1;
|
||||
int i = 0;
|
||||
foreach (var item in openSeeData) {
|
||||
if (item.id == openSeeExpression.faceId)
|
||||
idx = i;
|
||||
i++;
|
||||
}
|
||||
if (idx > -1) {
|
||||
rightEyeOpen = openSeeData[idx].rightEyeOpen;
|
||||
leftEyeOpen = openSeeData[idx].leftEyeOpen;
|
||||
}
|
||||
}
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Neutral), 0f);
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Fun), 0f);
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Sorrow), 0f);
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Angry), 0f);
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Joy), 0f);
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 0f);
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 0f);
|
||||
if (openSeeExpression.expression == expressionFun) {
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Fun), 1f);
|
||||
return;
|
||||
} else if (openSeeExpression.expression == expressionSorrow) {
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Sorrow), 1f);
|
||||
return;
|
||||
} else if (openSeeExpression.expression == expressionAngry) {
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Angry), 1f);
|
||||
return;
|
||||
} else if (openSeeExpression.expression == expressionJoy) {
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Joy), 1f);
|
||||
return;
|
||||
} else {
|
||||
/*if (rightEyeOpen < 0.05) {
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(new BlendShapeKey(BlendShapePreset.Blink_R), 1f);
|
||||
}
|
||||
if (leftEyeOpen < 0.05) {
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(new BlendShapeKey(BlendShapePreset.Blink_L), 1f);
|
||||
}*/
|
||||
}
|
||||
vrmBlendShapeProxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Neutral), 1f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3c7910e1fb62bf449920cea2fdf07326
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
[Serializable]
|
||||
public enum OpenSeeWebcamType { Unknown = -1, DirectShow, Blackmagic };
|
||||
|
||||
[Serializable]
|
||||
public enum OpenSeeWebcamFormat { Any = 0, Unknown = 1, ARGB = 100, XRGB, I420 = 200, NV12, YV12, Y800, YVYU = 300, YUY2, UYVY, HDYC, MJPEG = 400, H264 };
|
||||
|
||||
[Serializable]
|
||||
public struct OpenSeeWebcamCapability {
|
||||
public int id;
|
||||
|
||||
// DirectShow
|
||||
public int minCX;
|
||||
public int minCY;
|
||||
public int maxCX;
|
||||
public int maxCY;
|
||||
public int granularityCX;
|
||||
public int granularityCY;
|
||||
public int minInterval;
|
||||
public int maxInterval;
|
||||
public int rating;
|
||||
public OpenSeeWebcamFormat format;
|
||||
|
||||
// Blackmagic
|
||||
public int bmTimescale;
|
||||
public int bmFrameduration;
|
||||
public int bmModecode;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class OpenSeeWebcam {
|
||||
public OpenSeeWebcamType type = OpenSeeWebcamType.Unknown;
|
||||
public int id;
|
||||
public string name;
|
||||
|
||||
// DirectShow
|
||||
public string path;
|
||||
|
||||
// Blackmagic
|
||||
public long bmId;
|
||||
public int bmFlags;
|
||||
|
||||
// General
|
||||
public OpenSeeWebcamCapability[] caps;
|
||||
|
||||
public List<string> prettyCaps = null;
|
||||
|
||||
private List<Tuple<OpenSeeWebcamCapability, int>> splitCaps = null;
|
||||
|
||||
bool ge(bool a, bool b) {
|
||||
return a || (!a && !b);
|
||||
}
|
||||
|
||||
private int CompareCaps(OpenSeeWebcamCapability a, OpenSeeWebcamCapability b) {
|
||||
int aRes = a.minCX * a.minCY;
|
||||
int bRes = b.minCX * b.minCY;
|
||||
bool aGoodFps = a.minInterval <= 670000;
|
||||
bool bGoodFps = a.minInterval <= 670000;
|
||||
bool aGoodRes = aRes >= 850000 && aRes <= 2200000;
|
||||
bool bGoodRes = bRes >= 850000 && bRes <= 2200000;
|
||||
if (a.rating < b.rating && ge(aGoodFps, bGoodFps) && ge(aGoodRes, bGoodRes))
|
||||
return -1;
|
||||
if (a.rating > b.rating && ge(bGoodFps, aGoodFps) && ge(bGoodRes, aGoodRes))
|
||||
return 1;
|
||||
if (aRes > bRes && ge(aGoodFps, bGoodFps) && ge(aGoodRes, bGoodRes))
|
||||
return -1;
|
||||
if (aRes < bRes && ge(bGoodFps, aGoodFps) && ge(bGoodRes, aGoodRes))
|
||||
return 1;
|
||||
if (a.minInterval < b.minInterval)
|
||||
return -1;
|
||||
if (a.minInterval > b.minInterval)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private string GetPrettyCapability(OpenSeeWebcamCapability cap) {
|
||||
float fps = 10000000f / (float)cap.minInterval;
|
||||
string prettyCap = cap.minCX + "x" + cap.minCY + ", " + fps.ToString("0.##") + "fps (" + cap.format.ToString() + ")";
|
||||
return prettyCap;
|
||||
}
|
||||
|
||||
public List<string> GetPrettyCapabilities() {
|
||||
if (splitCaps != null && prettyCaps != null)
|
||||
return prettyCaps;
|
||||
|
||||
splitCaps = new List<Tuple<OpenSeeWebcamCapability, int>>();
|
||||
|
||||
for (int i = 0; i < caps.Length; i++) {
|
||||
if (caps[i].minCX == caps[i].maxCX && caps[i].minCY == caps[i].maxCY)
|
||||
splitCaps.Add(new Tuple<OpenSeeWebcamCapability, int>(caps[i], i));
|
||||
else {
|
||||
OpenSeeWebcamCapability min = caps[i];
|
||||
OpenSeeWebcamCapability max = caps[i];
|
||||
min.maxCX = min.minCX;
|
||||
min.maxCY = min.minCY;
|
||||
max.minCX = max.maxCX;
|
||||
max.minCY = max.maxCY;
|
||||
splitCaps.Add(new Tuple<OpenSeeWebcamCapability, int>(min, i));
|
||||
splitCaps.Add(new Tuple<OpenSeeWebcamCapability, int>(max, i));
|
||||
}
|
||||
}
|
||||
|
||||
splitCaps.Sort((a, b) => CompareCaps(a.Item1, b.Item1));
|
||||
|
||||
prettyCaps = new List<string>();
|
||||
if (type == OpenSeeWebcamType.DirectShow)
|
||||
prettyCaps.Add("Default settings");
|
||||
foreach (var cap in splitCaps) {
|
||||
prettyCaps.Add(GetPrettyCapability(cap.Item1));
|
||||
}
|
||||
|
||||
return prettyCaps;
|
||||
}
|
||||
|
||||
public OpenSeeWebcamCapability GetCapabilityByPrettyIndex(int index) {
|
||||
int dshowOffset = 0;
|
||||
if (type == OpenSeeWebcamType.DirectShow) {
|
||||
if (index == 0) {
|
||||
OpenSeeWebcamCapability cap = new OpenSeeWebcamCapability();
|
||||
cap.id = -1;
|
||||
cap.format = OpenSeeWebcamFormat.Any;
|
||||
cap.minInterval = 10000000 / 60;
|
||||
return cap;
|
||||
}
|
||||
dshowOffset = 1;
|
||||
}
|
||||
|
||||
if (splitCaps == null || index >= splitCaps.Count + dshowOffset || index < 0)
|
||||
throw new InvalidOperationException("Invalid capability index.");
|
||||
|
||||
return caps[splitCaps[index - dshowOffset].Item2];
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultExecutionOrder(-50)]
|
||||
public class OpenSeeWebcamInfo : MonoBehaviour {
|
||||
#region DllImport
|
||||
// DirectShow
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_capture")]
|
||||
private static extern System.IntPtr create_capture_x64();
|
||||
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json_length")]
|
||||
private static extern int get_json_length_x64(System.IntPtr cap);
|
||||
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json")]
|
||||
private static extern void get_json_x64(System.IntPtr cap, [Out] StringBuilder namebuffer, int bufferlength);
|
||||
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "destroy_capture")]
|
||||
private static extern void destroy_capture_x64(System.IntPtr cap);
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_capture")]
|
||||
private static extern System.IntPtr create_capture_x86();
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json_length")]
|
||||
private static extern int get_json_length_x86(System.IntPtr cap);
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json")]
|
||||
private static extern void get_json_x86(System.IntPtr cap, [Out] StringBuilder namebuffer, int bufferlength);
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "destroy_capture")]
|
||||
private static extern void destroy_capture_x86(System.IntPtr cap);
|
||||
|
||||
// Blackmagic
|
||||
[DllImport("libminibmcapture64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json_length")]
|
||||
private static extern int bm_get_json_length_x64();
|
||||
|
||||
[DllImport("libminibmcapture64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json")]
|
||||
private static extern void bm_get_json_x64([Out] StringBuilder namebuffer, int bufferlength);
|
||||
|
||||
[DllImport("libminibmcapture32", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json_length")]
|
||||
private static extern int bm_get_json_length_x86();
|
||||
|
||||
[DllImport("libminibmcapture32", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_json")]
|
||||
private static extern void bm_get_json_x86([Out] StringBuilder namebuffer, int bufferlength);
|
||||
#endregion
|
||||
|
||||
[Serializable]
|
||||
public class OpenSeeWebcamList {
|
||||
public List<OpenSeeWebcam> list;
|
||||
}
|
||||
|
||||
public List<OpenSeeWebcam> cameras;
|
||||
public bool dumpJson = false;
|
||||
|
||||
private static bool dumpJsonStatic = false;
|
||||
|
||||
static private string ListCameraDetails_x64() {
|
||||
System.IntPtr cap = create_capture_x64();
|
||||
int length = get_json_length_x64(cap);
|
||||
StringBuilder buffer = new StringBuilder(length);
|
||||
get_json_x64(cap, buffer, length);
|
||||
destroy_capture_x64(cap);
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
static private string ListCameraDetails_x86() {
|
||||
System.IntPtr cap = create_capture_x86();
|
||||
int length = get_json_length_x86(cap);
|
||||
StringBuilder buffer = new StringBuilder(length);
|
||||
get_json_x86(cap, buffer, length);
|
||||
destroy_capture_x86(cap);
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
static private string ListBlackMagicDetails_x64() {
|
||||
int length = bm_get_json_length_x64();
|
||||
StringBuilder buffer = new StringBuilder(length);
|
||||
bm_get_json_x64(buffer, length);
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
static private string ListBlackMagicDetails_x86() {
|
||||
int length = bm_get_json_length_x86();
|
||||
StringBuilder buffer = new StringBuilder(length);
|
||||
bm_get_json_x86(buffer, length);
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
static public List<OpenSeeWebcam> ListCameraDetails(bool includeBlackMagic) {
|
||||
string jsonData = null;
|
||||
string bmJsonData = null;
|
||||
if (Environment.Is64BitProcess) {
|
||||
jsonData = ListCameraDetails_x64();
|
||||
if (includeBlackMagic)
|
||||
bmJsonData = ListBlackMagicDetails_x64();
|
||||
} else {
|
||||
jsonData = ListCameraDetails_x86();
|
||||
if (includeBlackMagic)
|
||||
bmJsonData = ListBlackMagicDetails_x86();
|
||||
}
|
||||
if (dumpJsonStatic) {
|
||||
UnityEngine.Debug.Log("Camera JSON: " + jsonData);
|
||||
if (includeBlackMagic)
|
||||
UnityEngine.Debug.Log("Blackmagic JSON: " + bmJsonData);
|
||||
}
|
||||
List<OpenSeeWebcam> details = JsonUtility.FromJson<OpenSeeWebcamList>("{\"list\":" + jsonData + "}").list;
|
||||
foreach (var cam in details)
|
||||
cam.type = OpenSeeWebcamType.DirectShow;
|
||||
if (includeBlackMagic) {
|
||||
List<OpenSeeWebcam> bmDetails = JsonUtility.FromJson<OpenSeeWebcamList>("{\"list\":" + bmJsonData + "}").list;
|
||||
foreach (var cam in bmDetails)
|
||||
cam.type = OpenSeeWebcamType.Blackmagic;
|
||||
details.AddRange(bmDetails);
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
void Start() {
|
||||
Initialize(false);
|
||||
}
|
||||
|
||||
public void Initialize(bool includeBlackMagic) {
|
||||
dumpJsonStatic = dumpJson;
|
||||
cameras = ListCameraDetails(includeBlackMagic);
|
||||
foreach (var camera in cameras)
|
||||
camera.GetPrettyCapabilities();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e9598f1218283b418c7e72604be0335
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -50
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
Before Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
BIN
Images/OSF.png
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
BIN
Images/Raw1.png
|
Before Width: | Height: | Size: 70 KiB |
BIN
Images/Raw2.png
|
Before Width: | Height: | Size: 57 KiB |
BIN
Images/Raw3.png
|
Before Width: | Height: | Size: 113 KiB |
BIN
Images/Raw4.png
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 175 KiB |
|
Before Width: | Height: | Size: 338 KiB |
|
Before Width: | Height: | Size: 150 KiB |
279
Unity/OpenSee.cs
|
|
@ -1,279 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class OpenSee : MonoBehaviour {
|
||||
[Header("UDP server settings")]
|
||||
[Tooltip("To listen for remote connections, change this to 0.0.0.0 or your actual IP on the desired interface")]
|
||||
public string listenAddress = "127.0.0.1";
|
||||
[Tooltip("This is the port the server will listen for tracking packets on")]
|
||||
public int listenPort = 11573;
|
||||
|
||||
private const int nPoints = 68;
|
||||
private const int packetFrameSize = 8 + 4 + 2 * 4 + 2 * 4 + 1 + 4 + 3 * 4 + 3 * 4 + 4 * 4 + 4 * 68 + 4 * 2 * 68 + 4 * 3 * 70 + 4 * 14;
|
||||
|
||||
[Header("Tracking data")]
|
||||
[Tooltip("This is an informational property that tells you how many packets have been received")]
|
||||
public int receivedPackets = 0;
|
||||
[Tooltip("This contains the actual tracking data")]
|
||||
public OpenSeeData[] trackingData = null;
|
||||
|
||||
public bool listening { get; private set; } = false;
|
||||
|
||||
[HideInInspector]
|
||||
public float maxFit3DError = 100f;
|
||||
|
||||
[System.Serializable]
|
||||
public class OpenSeeData {
|
||||
[Tooltip("The time this tracking data was captured at.")]
|
||||
public double time;
|
||||
[Tooltip("This is the id of the tracked face. When tracking multiple faces, they might get reordered due to faces coming and going, but as long as tracking is not lost on a face, its id should stay the same. Face ids depend only on the order of first detection and locations of the faces.")]
|
||||
public int id;
|
||||
[Tooltip("This field gives the resolution of the camera or video being tracked.")]
|
||||
public Vector2 cameraResolution;
|
||||
[Tooltip("This field tells you how likely it is that the right eye is open.")]
|
||||
public float rightEyeOpen;
|
||||
[Tooltip("This field tells you how likely it is that the left eye is open.")]
|
||||
public float leftEyeOpen;
|
||||
[Tooltip("This field contains the rotation of the right eyeball.")]
|
||||
public Quaternion rightGaze;
|
||||
[Tooltip("This field contains the rotation of the left eyeball.")]
|
||||
public Quaternion leftGaze;
|
||||
[Tooltip("This field tells you if 3D points have been successfully estimated from the 2D points. If this is false, do not rely on pose or 3D data.")]
|
||||
public bool got3DPoints;
|
||||
[Tooltip("This field contains the error for fitting the original 3D points. It shouldn't matter much, but it it is very high, something is probably wrong")]
|
||||
public float fit3DError;
|
||||
[Tooltip("This is the rotation vector for the 3D points to turn into the estimated face pose.")]
|
||||
public Vector3 rotation;
|
||||
[Tooltip("This is the translation vector for the 3D points to turn into the estimated face pose.")]
|
||||
public Vector3 translation;
|
||||
[Tooltip("This is the raw rotation quaternion calculated from the OpenCV rotation matrix. It does not match Unity's coordinate system, but it still might be useful.")]
|
||||
public Quaternion rawQuaternion;
|
||||
[Tooltip("This is the raw rotation euler angles calculated by OpenCV from the rotation matrix. It does not match Unity's coordinate system, but it still might be useful.")]
|
||||
public Vector3 rawEuler;
|
||||
[Tooltip("This field tells you how certain the tracker is.")]
|
||||
public float[] confidence;
|
||||
[Tooltip("These are the detected face landmarks in image coordinates. There are 68 points. The last too points are pupil points from the gaze tracker.")]
|
||||
public Vector2[] points;
|
||||
[Tooltip("These are 3D points estimated from the 2D points. The should be rotation and translation compensated. There are 70 points with guesses for the eyeball center positions being added at the end of the 68 2D points.")]
|
||||
public Vector3[] points3D;
|
||||
[Tooltip("This field contains a number of action unit like features.")]
|
||||
public OpenSeeFeatures features;
|
||||
|
||||
[System.Serializable]
|
||||
public class OpenSeeFeatures {
|
||||
[Tooltip("This field indicates whether the left eye is opened(0) or closed (-1). A value of 1 means open wider than normal.")]
|
||||
public float EyeLeft;
|
||||
[Tooltip("This field indicates whether the right eye is opened(0) or closed (-1). A value of 1 means open wider than normal.")]
|
||||
public float EyeRight;
|
||||
[Tooltip("This field indicates how steep the left eyebrow is, compared to the median steepness.")]
|
||||
public float EyebrowSteepnessLeft;
|
||||
[Tooltip("This field indicates how far up or down the left eyebrow is, compared to its median position.")]
|
||||
public float EyebrowUpDownLeft;
|
||||
[Tooltip("This field indicates how quirked the left eyebrow is, compared to its median quirk.")]
|
||||
public float EyebrowQuirkLeft;
|
||||
[Tooltip("This field indicates how steep the right eyebrow is, compared to the average steepness.")]
|
||||
public float EyebrowSteepnessRight;
|
||||
[Tooltip("This field indicates how far up or down the right eyebrow is, compared to its median position.")]
|
||||
public float EyebrowUpDownRight;
|
||||
[Tooltip("This field indicates how quirked the right eyebrow is, compared to its median quirk.")]
|
||||
public float EyebrowQuirkRight;
|
||||
[Tooltip("This field indicates how far up or down the left mouth corner is, compared to its median position.")]
|
||||
public float MouthCornerUpDownLeft;
|
||||
[Tooltip("This field indicates how far in or out the left mouth corner is, compared to its median position.")]
|
||||
public float MouthCornerInOutLeft;
|
||||
[Tooltip("This field indicates how far up or down the right mouth corner is, compared to its median position.")]
|
||||
public float MouthCornerUpDownRight;
|
||||
[Tooltip("This field indicates how far in or out the right mouth corner is, compared to its median position.")]
|
||||
public float MouthCornerInOutRight;
|
||||
[Tooltip("This field indicates how open or closed the mouth is, compared to its median pose.")]
|
||||
public float MouthOpen;
|
||||
[Tooltip("This field indicates how wide the mouth is, compared to its median pose.")]
|
||||
public float MouthWide;
|
||||
}
|
||||
|
||||
public OpenSeeData() {
|
||||
confidence = new float[nPoints];
|
||||
points = new Vector2[nPoints];
|
||||
points3D = new Vector3[nPoints + 2];
|
||||
}
|
||||
|
||||
private Vector3 swapX(Vector3 v) {
|
||||
v.x = -v.x;
|
||||
return v;
|
||||
}
|
||||
|
||||
private float readFloat(byte[] b, ref int o) {
|
||||
float v = System.BitConverter.ToSingle(b, o);
|
||||
o += 4;
|
||||
return v;
|
||||
}
|
||||
|
||||
private Quaternion readQuaternion(byte[] b, ref int o) {
|
||||
float x = readFloat(b, ref o);
|
||||
float y = readFloat(b, ref o);
|
||||
float z = readFloat(b, ref o);
|
||||
float w = readFloat(b, ref o);
|
||||
Quaternion q = new Quaternion(x, y, z, w);
|
||||
return q;
|
||||
}
|
||||
|
||||
private Vector3 readVector3(byte[] b, ref int o) {
|
||||
Vector3 v = new Vector3(readFloat(b, ref o), -readFloat(b, ref o), readFloat(b, ref o));
|
||||
return v;
|
||||
}
|
||||
|
||||
private Vector2 readVector2(byte[] b, ref int o) {
|
||||
Vector2 v = new Vector2(readFloat(b, ref o), readFloat(b, ref o));
|
||||
return v;
|
||||
}
|
||||
|
||||
public void readFromPacket(byte[] b, int o) {
|
||||
time = System.BitConverter.ToDouble(b, o);
|
||||
o += 8;
|
||||
id = System.BitConverter.ToInt32(b, o);
|
||||
o += 4;
|
||||
|
||||
cameraResolution = readVector2(b, ref o);
|
||||
rightEyeOpen = readFloat(b, ref o);
|
||||
leftEyeOpen = readFloat(b, ref o);
|
||||
|
||||
byte got3D = b[o];
|
||||
o++;
|
||||
got3DPoints = false;
|
||||
if (got3D != 0)
|
||||
got3DPoints = true;
|
||||
|
||||
fit3DError = readFloat(b, ref o);
|
||||
rawQuaternion = readQuaternion(b, ref o);
|
||||
Quaternion convertedQuaternion = new Quaternion(-rawQuaternion.x, rawQuaternion.y, -rawQuaternion.z, rawQuaternion.w);
|
||||
rawEuler = readVector3(b, ref o);
|
||||
|
||||
rotation = rawEuler;
|
||||
rotation.z = (rotation.z - 90) % 360;
|
||||
rotation.x = -(rotation.x + 180) % 360;
|
||||
|
||||
float x = readFloat(b, ref o);
|
||||
float y = readFloat(b, ref o);
|
||||
float z = readFloat(b, ref o);
|
||||
translation = new Vector3(-y, x, -z);
|
||||
|
||||
for (int i = 0; i < nPoints; i++) {
|
||||
confidence[i] = readFloat(b, ref o);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nPoints; i++) {
|
||||
points[i] = readVector2(b, ref o);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nPoints + 2; i++) {
|
||||
points3D[i] = readVector3(b, ref o);
|
||||
}
|
||||
|
||||
rightGaze = Quaternion.LookRotation(swapX(points3D[66]) - swapX(points3D[68])) * Quaternion.AngleAxis(180, Vector3.right) * Quaternion.AngleAxis(180, Vector3.forward);
|
||||
leftGaze = Quaternion.LookRotation(swapX(points3D[67]) - swapX(points3D[69])) * Quaternion.AngleAxis(180, Vector3.right) * Quaternion.AngleAxis(180, Vector3.forward);
|
||||
|
||||
features = new OpenSeeFeatures();
|
||||
features.EyeLeft = readFloat(b, ref o);
|
||||
features.EyeRight = readFloat(b, ref o);
|
||||
features.EyebrowSteepnessLeft = readFloat(b, ref o);
|
||||
features.EyebrowUpDownLeft = readFloat(b, ref o);
|
||||
features.EyebrowQuirkLeft = readFloat(b, ref o);
|
||||
features.EyebrowSteepnessRight = readFloat(b, ref o);
|
||||
features.EyebrowUpDownRight = readFloat(b, ref o);
|
||||
features.EyebrowQuirkRight = readFloat(b, ref o);
|
||||
features.MouthCornerUpDownLeft = readFloat(b, ref o);
|
||||
features.MouthCornerInOutLeft = readFloat(b, ref o);
|
||||
features.MouthCornerUpDownRight = readFloat(b, ref o);
|
||||
features.MouthCornerInOutRight = readFloat(b, ref o);
|
||||
features.MouthOpen = readFloat(b, ref o);
|
||||
features.MouthWide = readFloat(b, ref o);
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<int, OpenSeeData> openSeeDataMap;
|
||||
private Socket socket;
|
||||
private byte[] buffer;
|
||||
private Thread receiveThread = null;
|
||||
private volatile bool stopReception = false;
|
||||
|
||||
public OpenSeeData GetOpenSeeData(int faceId) {
|
||||
if (openSeeDataMap == null)
|
||||
return null;
|
||||
if (!openSeeDataMap.ContainsKey(faceId))
|
||||
return null;
|
||||
return openSeeDataMap[faceId];
|
||||
}
|
||||
|
||||
void performReception() {
|
||||
EndPoint senderRemote = new IPEndPoint(IPAddress.Any, 0);
|
||||
listening = true;
|
||||
while (!stopReception) {
|
||||
try {
|
||||
int receivedBytes = socket.ReceiveFrom(buffer, SocketFlags.None, ref senderRemote);
|
||||
if (receivedBytes < 1 || receivedBytes % packetFrameSize != 0) {
|
||||
continue;
|
||||
}
|
||||
receivedPackets++;
|
||||
int i = 0;
|
||||
for (int offset = 0; offset < receivedBytes; offset += packetFrameSize) {
|
||||
OpenSeeData newData = new OpenSeeData();
|
||||
newData.readFromPacket(buffer, offset);
|
||||
openSeeDataMap[newData.id] = newData;
|
||||
i++;
|
||||
}
|
||||
trackingData = new OpenSeeData[openSeeDataMap.Count];
|
||||
openSeeDataMap.Values.CopyTo(trackingData, 0);
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
|
||||
void Start () {
|
||||
if (openSeeDataMap == null)
|
||||
openSeeDataMap = new Dictionary<int, OpenSeeData>();
|
||||
buffer = new byte[65535];
|
||||
|
||||
if (socket == null) {
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
IPAddress ip;
|
||||
IPAddress.TryParse(listenAddress, out ip);
|
||||
socket.Bind(new IPEndPoint(ip, listenPort));
|
||||
socket.ReceiveTimeout = 15;
|
||||
}
|
||||
|
||||
receiveThread = new Thread(() => performReception());
|
||||
receiveThread.Start();
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (receiveThread != null && !receiveThread.IsAlive) {
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
void EndReceiver() {
|
||||
if (receiveThread != null) {
|
||||
stopReception = true;
|
||||
receiveThread.Join();
|
||||
stopReception = false;
|
||||
}
|
||||
if (socket != null)
|
||||
socket.Close();
|
||||
}
|
||||
|
||||
void OnApplicationQuit() {
|
||||
EndReceiver();
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
EndReceiver();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b5af009eeb86f9469d2be8970e5a0f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,725 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class OpenSeeExpression : MonoBehaviour
|
||||
{
|
||||
[Header("Settings")]
|
||||
[Tooltip("This is the source of face tracking data. If it is not set, any OpenSee component on the current object is used.")]
|
||||
public OpenSee openSee;
|
||||
[Tooltip("When enabled ThunderSVM is used instead of LibSVM, which is usually faster, but may cause issues on some systems.")]
|
||||
public bool useThunderSVM = false;
|
||||
[Header("Simple mode")]
|
||||
[Tooltip("When enabled, some simple heuristics will be used to detect expressions, rather than an SVM model. Outputs one of: neutral, fun, angry, surprise")]
|
||||
public bool simpleMode = false;
|
||||
[Tooltip("This smoothing factor is applied to the features used for simple expression detection.")]
|
||||
[Range(0f, 1f)]
|
||||
public float simpleSmoothing = 0.6f;
|
||||
[Tooltip("This smoothing factor is applied to the features used for simple expression detection.")]
|
||||
[Range(0f, 5f)]
|
||||
public float simpleSensitivity = 1f;
|
||||
[Header("Calibration")]
|
||||
[Tooltip("This specifies which expression calibration data should be collected for.")]
|
||||
public string calibrationExpression = "neutral";
|
||||
[Tooltip("This specifies the id of the face for which data should be collected and predictions should be made. Face ids depend only on the order of first detection and locations of the faces.")]
|
||||
public int faceId = 0;
|
||||
[Tooltip("This keeps low numbers of misdetections from changing the expression, but introduces a bit of lag to expression changes. For example, a value of 1 means that a new expression has to be detected twice in a row, before the predicted expression changes.")]
|
||||
public int expressionStabilizer = 1;
|
||||
[Tooltip("Setting this to a value above 1 will record only every recordingSkip-th frame when capture calibration data. This can be useful with cameras that have a high framerate leading to only a short time for capturing varied expression information. Usually at least 20-30 seconds are required to capture a good variety of angles and expression variation, so this should be set accordingly when using the recording flag. At 1, no frames are skipped.")]
|
||||
public int recordingSkip = 2;
|
||||
[Tooltip("If allowOverRecording is enabled and expression data is recorded beyond 100%, this is used instead of recordingSkip to avoid quickly replacing the existing data.")]
|
||||
public int overRecordingSkip = 5;
|
||||
[Tooltip("This is the filename used for loading and saving expression data using the load and save flags.")]
|
||||
public string filename = "";
|
||||
[Tooltip("When enabled, training will be done in such a way that the model can output probabilities. This is a lot slower!")]
|
||||
public bool enableProbabilityTraining = false;
|
||||
[Tooltip("This is the SVM training C value.")]
|
||||
public float C = 2f;
|
||||
[Tooltip("This map allows you to set weights for different classes.")]
|
||||
public Dictionary<string, float> weightMap = null;
|
||||
[Header("Toggles")]
|
||||
[Tooltip("When enabled, calibration data will be collected from the given OpenSee component.")]
|
||||
public bool recording = false;
|
||||
[Tooltip("When enabled, you can keep recording past 100%. When doing so, random frames in the already collected data will be overwritten with new data. To prevent quickly replacing all the collected data, overRecordingSkip is used instead of recordingSkip, which should be set to a higher value.")]
|
||||
public bool allowOverRecording = false;
|
||||
[Tooltip("When enabled, the calibration data collected for the current expression is cleared and this flag is set back to false.")]
|
||||
public bool clear = false;
|
||||
[Tooltip("When enabled, a new prediction model is trained and this flag is set back to false. Only expressions for which percentRecorded is 100% will be included in training.")]
|
||||
public bool train = false;
|
||||
[Tooltip("When enabled and a model is loaded, the current expression will be predicted every frame.")]
|
||||
public bool predict = false;
|
||||
[Tooltip("When enabled, the expression data from the specified filename will be loaded and this flag is set back to false.")]
|
||||
public bool load = false;
|
||||
[Tooltip("When enabled, the expression data will be saved to the specified filename and this flag is set back to false.")]
|
||||
public bool save = false;
|
||||
[Header("Point selection")]
|
||||
[Tooltip("These settings can be used to train an expression detection model on a subset of points.")]
|
||||
public PointSelection pointSelection;
|
||||
[Header("Information")]
|
||||
[Tooltip("This is the number of expressions for which calibration data was collected. The maximum number is 10.")]
|
||||
public int expressionNumber = 0;
|
||||
[Tooltip("This is the percentage of necessary training data collected for the current expression.")]
|
||||
public float percentRecorded = 0.0f;
|
||||
[Tooltip("This shows whether a model is ready to predict expressions with.")]
|
||||
public bool modelReady = false;
|
||||
[Tooltip("This is the time the current expression was detected.")]
|
||||
public float expressionTime = 0f;
|
||||
[Tooltip("This is currently detected expression.")]
|
||||
public string expression = "";
|
||||
[Tooltip("These are the probabilities of all expressions. The labels in order can be retrieved through the GetClassLabels methods.")]
|
||||
public double[] probabilities;
|
||||
[Header("Training statistics")]
|
||||
[Tooltip("This shows the accuracy result of the last training run.")]
|
||||
public float accuracy = 0.0f;
|
||||
[Tooltip("This is the confusion matrix for the last training run's validation set.")]
|
||||
public int[,] confusionMatrix = null;
|
||||
[Tooltip("This is a pretty printed string representation of the confusion matrix.")]
|
||||
public string confusionMatrixString = "";
|
||||
[Tooltip("This shows any accuracy warnings that resulted from the last training run.")]
|
||||
public string[] warnings = null;
|
||||
|
||||
private float lastMouthCorner = 0f;
|
||||
private float lastEyebrows = 0f;
|
||||
private bool hadFun = false;
|
||||
private bool hadAngry = false;
|
||||
private bool hadSurprised = false;
|
||||
float AdjustThreshold(bool active) {
|
||||
if (active)
|
||||
return 0.8f;
|
||||
else
|
||||
return 1f;
|
||||
}
|
||||
void ThresholdDetection() {
|
||||
lastMouthCorner = lastMouthCorner * simpleSmoothing + (openSeeData.features.MouthCornerUpDownLeft + openSeeData.features.MouthCornerUpDownRight) * 0.5f * (1f - simpleSmoothing);
|
||||
lastEyebrows = lastEyebrows * simpleSmoothing + (openSeeData.features.EyebrowUpDownLeft + openSeeData.features.EyebrowUpDownRight) * 0.5f * (1f - simpleSmoothing);
|
||||
if (lastMouthCorner * simpleSensitivity < -0.2f * AdjustThreshold(hadFun)) {
|
||||
expression = "fun";
|
||||
hadFun = true;
|
||||
hadSurprised = false;
|
||||
hadAngry = false;
|
||||
} else if (lastEyebrows * simpleSensitivity > 0.2f * AdjustThreshold(hadSurprised)) {
|
||||
expression = "surprise";
|
||||
hadFun = false;
|
||||
hadSurprised = true;
|
||||
hadAngry = false;
|
||||
} else if (lastEyebrows * simpleSensitivity < -0.25f * AdjustThreshold(hadAngry) && lastMouthCorner * simpleSensitivity > -0.3f * (2f - AdjustThreshold(hadAngry))) {
|
||||
expression = "angry";
|
||||
hadFun = false;
|
||||
hadSurprised = false;
|
||||
hadAngry = true;
|
||||
} else {
|
||||
expression = "neutral";
|
||||
hadFun = false;
|
||||
hadSurprised = false;
|
||||
hadAngry = false;
|
||||
}
|
||||
expressionTime = Time.time;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PointSelection {
|
||||
[Tooltip("When enabled, the points of the face contour will be used to determine the expression.")]
|
||||
public bool pointsFaceContour = true;
|
||||
[Tooltip("When enabled, the points of the right brow will be used to determine the expression.")]
|
||||
public bool pointsBrowRight = true;
|
||||
[Tooltip("When enabled, the points of the left brow will be used to determine the expression.")]
|
||||
public bool pointsBrowLeft = true;
|
||||
[Tooltip("When enabled, the points of the right eye will be used to determine the expression.")]
|
||||
public bool pointsEyeRight = true;
|
||||
[Tooltip("When enabled, the points of the left eye will be used to determine the expression.")]
|
||||
public bool pointsEyeLeft = true;
|
||||
[Tooltip("When enabled, the points of the nose will be used to determine the expression.")]
|
||||
public bool pointsNose = true;
|
||||
[Tooltip("When enabled, the points of the corners of the mouth will be used to determine the expression.")]
|
||||
public bool pointsMouthCorner = true;
|
||||
[Tooltip("When enabled, the points of the upper lip will be used to determine the expression.")]
|
||||
public bool pointsLipUpper = true;
|
||||
[Tooltip("When enabled, the points of the lower lip will be used to determine the expression.")]
|
||||
public bool pointsLipLower = true;
|
||||
[Tooltip("When enabled, certain manually designed features will be used to determine the expression.")]
|
||||
public bool features = true;
|
||||
[Tooltip("When enabled, the depth value of points is also used for training.")]
|
||||
public bool includeDepth = false;
|
||||
}
|
||||
[Serializable]
|
||||
private class OpenSeeExpressionRepresentation {
|
||||
private Dictionary<string, List<float[]>> expressions;
|
||||
private byte[] modelBytes = null;
|
||||
private string[] classLabels = null;
|
||||
private int[] indices = null;
|
||||
private PointSelection pointSelection;
|
||||
private bool thunderSVM = false;
|
||||
//private bool newModel = false;
|
||||
|
||||
static public void LoadSerialized(byte[] modelBytes, out Dictionary<string, List<float[]>> expressions, out SVMModel model, out string[] classLabels, out int[] indices, ref PointSelection pointSelection) {
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
MemoryStream memoryStream = new MemoryStream(modelBytes);
|
||||
memoryStream.Position = 0;
|
||||
OpenSeeExpressionRepresentation oser;
|
||||
using (GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) {
|
||||
oser = formatter.Deserialize(gzipStream) as OpenSeeExpressionRepresentation;
|
||||
}
|
||||
expressions = oser.expressions;
|
||||
/*if (oser.thunderSVM)
|
||||
model = new ThunderSVMModel(oser.modelBytes);
|
||||
else*/
|
||||
model = new SVMModel(oser.modelBytes);
|
||||
classLabels = oser.classLabels;
|
||||
indices = oser.indices;
|
||||
//pointSelection = oser.pointSelection;
|
||||
if (indices == null) {
|
||||
indices = new int[1 + 1 + 3 + 4 + 3 + 3 * 66];
|
||||
for (int i = 0; i < 1 + 1 + 3 + 4 + 3 + 3 * 66; i++)
|
||||
indices[i] = i;
|
||||
}
|
||||
/*if (pointSelection == null)
|
||||
pointSelection = new PointSelection();
|
||||
if (!oser.newModel)
|
||||
pointSelection.features = true;*/
|
||||
}
|
||||
|
||||
static public byte[] ToSerialized(Dictionary<string, List<float[]>> expressions, SVMModel model, string[] classLabels, int[] indices, PointSelection pointSelection) {
|
||||
OpenSeeExpressionRepresentation oser = new OpenSeeExpressionRepresentation();
|
||||
oser.expressions = expressions;
|
||||
oser.modelBytes = model.SaveModel();
|
||||
oser.classLabels = classLabels;
|
||||
oser.indices = indices;
|
||||
oser.pointSelection = pointSelection;
|
||||
oser.thunderSVM = model is ThunderSVMModel;
|
||||
//oser.newModel = true;
|
||||
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
using (GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress)) {
|
||||
formatter.Serialize(gzipStream, oser);
|
||||
gzipStream.Flush();
|
||||
}
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private OpenSee.OpenSeeData openSeeData = null;
|
||||
private Dictionary<string, List<float[]>> expressions;
|
||||
private SVMModel model = null;
|
||||
private string[] classLabels = null;
|
||||
private int maxSamples = 1666;
|
||||
// rightEyeOpen, leftEyeOpen, translation, rawQuaternion, rawEuler, confidence, points/(width, height), points3D, features
|
||||
private int colsFull = 1 + 1 + 3 + 4 + 3 + /*66 + 2 * 66 +*/ 3 * 66 + 14;
|
||||
private int colsBase = 1 + 1 + 3 + 4 + 3;
|
||||
private int cols;
|
||||
private double lastCapture = 0.0;
|
||||
private float warningThreshold = 5;
|
||||
private int currentPrediction = -1;
|
||||
private int lastPrediction = -1;
|
||||
private int lastPredictionCount = 0;
|
||||
private int frameCount = 0;
|
||||
private System.Random rnd;
|
||||
|
||||
private int[] indices;
|
||||
|
||||
private int[] indicesFaceContour = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||
private int[] indicesBrowRight = new int[] {17, 18, 19, 20, 21};
|
||||
private int[] indicesBrowLeft = new int[] {22, 23, 24, 25, 26};
|
||||
private int[] indicesEyeRight = new int[] {36, 37, 38, 39, 40, 41};
|
||||
private int[] indicesEyeLeft = new int[] {42, 43, 44, 45, 46, 47};
|
||||
private int[] indicesNose = new int[] {27, 28, 29, /*30, */31, 32, 33, 34, 35};
|
||||
private int[] indicesMouthCorner = new int[] {58, 62};
|
||||
private int[] indicesLipUpper = new int[] {48, 49, 50, 51, 52, 59, 60, 61};
|
||||
private int[] indicesLipLower = new int[] {53, 54, 55, 56, 57, 63, 64, 65};
|
||||
|
||||
private void SelectPoints() {
|
||||
List<int> indexList = new List<int>();
|
||||
for (int i = 0; i < colsBase; i++)
|
||||
if (i > 4) // Translation and (sometimes unavailable) eye open fields should not be used for training!
|
||||
indexList.Add(i);
|
||||
cols = indexList.Count;
|
||||
if (pointSelection.pointsFaceContour)
|
||||
foreach (int i in indicesFaceContour) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsBrowRight)
|
||||
foreach (int i in indicesBrowRight) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsBrowLeft)
|
||||
foreach (int i in indicesBrowLeft) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsEyeRight)
|
||||
foreach (int i in indicesEyeRight) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsEyeLeft)
|
||||
foreach (int i in indicesEyeLeft) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsNose)
|
||||
foreach (int i in indicesNose) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsNose)
|
||||
foreach (int i in indicesMouthCorner) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsLipUpper)
|
||||
foreach (int i in indicesLipUpper) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.pointsLipLower)
|
||||
foreach (int i in indicesLipLower) {
|
||||
indexList.Add(colsBase + i * 3);
|
||||
indexList.Add(colsBase + i * 3 + 1);
|
||||
cols += 2;
|
||||
if (pointSelection.includeDepth) {
|
||||
indexList.Add(colsBase + i * 3 + 2);
|
||||
cols++;
|
||||
}
|
||||
}
|
||||
if (pointSelection.features)
|
||||
for (int i = 0; i < 14; i++) {
|
||||
indexList.Add(colsBase + 66 * 3 + i);
|
||||
cols++;
|
||||
}
|
||||
indices = indexList.ToArray();
|
||||
}
|
||||
|
||||
private void ResetInfo() {
|
||||
recording = false;
|
||||
clear = false;
|
||||
train = false;
|
||||
modelReady = false;
|
||||
accuracy = 0.0f;
|
||||
confusionMatrix = null;
|
||||
warnings = null;
|
||||
percentRecorded = 0.0f;
|
||||
expression = "";
|
||||
confusionMatrixString = "";
|
||||
lastPrediction = -1;
|
||||
currentPrediction = -1;
|
||||
lastPredictionCount = 0;
|
||||
expressionTime = 0f;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (openSee == null)
|
||||
openSee = GetComponent<OpenSee>();
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return;
|
||||
}
|
||||
ResetInfo();
|
||||
expressions = new Dictionary<string, List<float[]>>();
|
||||
if (useThunderSVM)
|
||||
model = new ThunderSVMModel();
|
||||
else
|
||||
model = new SVMModel();
|
||||
rnd = new System.Random();
|
||||
}
|
||||
|
||||
private float[] GetData(OpenSee.OpenSeeData t) {
|
||||
float[] data = new float[colsFull];
|
||||
data[0] = t.rightEyeOpen;
|
||||
data[1] = t.leftEyeOpen;
|
||||
data[2] = t.translation.x;
|
||||
data[3] = t.translation.y;
|
||||
data[4] = t.translation.z;
|
||||
data[5] = t.rawQuaternion.x;
|
||||
data[6] = t.rawQuaternion.y;
|
||||
data[7] = t.rawQuaternion.z;
|
||||
data[8] = t.rawQuaternion.w;
|
||||
data[9] = t.rawEuler.x;
|
||||
data[10] = t.rawEuler.y;
|
||||
data[11] = t.rawEuler.z;
|
||||
/*for (int i = 0; i < 66; i++)
|
||||
data[12 + i] = t.confidence[i];
|
||||
for (int i = 0; i < 66; i++) {
|
||||
data[12 + 66 + 2 * i] = t.points[i].x / t.cameraResolution.x;
|
||||
data[12 + 66 + 2 * i + 1] = t.points[i].y / t.cameraResolution.y;
|
||||
}*/
|
||||
for (int i = 0; i < 66; i++) {
|
||||
data[12 + /*66 + 2 * 66 +*/ 3 * i] = t.points3D[i].x;
|
||||
data[12 + /*66 + 2 * 66 +*/ 3 * i + 1] = t.points3D[i].y;
|
||||
data[12 + /*66 + 2 * 66 +*/ 3 * i + 2] = t.points3D[i].z;
|
||||
}
|
||||
data[12 + 66 * 3 + 0] = t.features.EyeLeft;
|
||||
data[12 + 66 * 3 + 1] = t.features.EyeRight;
|
||||
data[12 + 66 * 3 + 2] = t.features.EyebrowSteepnessLeft;
|
||||
data[12 + 66 * 3 + 3] = t.features.EyebrowUpDownLeft;
|
||||
data[12 + 66 * 3 + 4] = t.features.EyebrowQuirkLeft;
|
||||
data[12 + 66 * 3 + 5] = t.features.EyebrowSteepnessRight;
|
||||
data[12 + 66 * 3 + 6] = t.features.EyebrowUpDownRight;
|
||||
data[12 + 66 * 3 + 7] = t.features.EyebrowQuirkRight;
|
||||
data[12 + 66 * 3 + 8] = t.features.MouthCornerUpDownLeft;
|
||||
data[12 + 66 * 3 + 9] = t.features.MouthCornerInOutLeft;
|
||||
data[12 + 66 * 3 + 10] = t.features.MouthCornerUpDownRight;
|
||||
data[12 + 66 * 3 + 11] = t.features.MouthCornerInOutRight;
|
||||
data[12 + 66 * 3 + 12] = t.features.MouthOpen;
|
||||
data[12 + 66 * 3 + 13] = t.features.MouthWide;
|
||||
return data;
|
||||
}
|
||||
|
||||
public bool TrainModel() {
|
||||
Debug.Log("---------------------");
|
||||
train = false;
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return false;
|
||||
}
|
||||
modelReady = false;
|
||||
if (model is SVMModel && useThunderSVM)
|
||||
model = new ThunderSVMModel();
|
||||
else if (model is ThunderSVMModel && !useThunderSVM)
|
||||
model = new SVMModel();
|
||||
SelectPoints();
|
||||
List<string> keys = new List<string>();
|
||||
List<string> accuracyWarnings = new List<string>();
|
||||
int samples = 0;
|
||||
foreach (string key in expressions.Keys) {
|
||||
List<float[]> list = new List<float[]>(expressions[key]);
|
||||
list.RemoveAll(x => x.Length != colsFull);
|
||||
if (list != null && list.Count == maxSamples) {
|
||||
Debug.Log("[Training info] Adding expression " + key + " to training data.");
|
||||
keys.Add(key);
|
||||
samples += list.Count;
|
||||
} else {
|
||||
if (list != null && list.Count > 20) {
|
||||
Debug.Log("[Training warning] Expression " + key + " has little data and might be inaccurate. (" + list.Count + ")");
|
||||
accuracyWarnings.Add("Expression " + key + " has little data and might be inaccurate. (" + list.Count + ")");
|
||||
samples += list.Count;
|
||||
keys.Add(key);
|
||||
} else {
|
||||
Debug.Log("[Training warning] Skipping expression " + key + " due to lack of collected data. (" + list.Count + ")");
|
||||
accuracyWarnings.Add("Skipping expression " + key + " due to lack of collected data. (" + list.Count + ")");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
int classes = keys.Count;
|
||||
if (classes < 2 || classes > 10) {
|
||||
Debug.Log("[Training error] The number of expressions that can be used for training is " + classes + ", which is either below 2 or higher than 10.");
|
||||
accuracyWarnings.Add("[Training error] The number of expressions that can be used for training is " + classes + ", which is either below 2 or higher than 10.");
|
||||
return false;
|
||||
}
|
||||
keys.Sort();
|
||||
classLabels = keys.ToArray();
|
||||
|
||||
float[] weights = null;
|
||||
if (weightMap != null) {
|
||||
weights = new float[classLabels.Length];
|
||||
for (int i = 0; i < classLabels.Length; i++) {
|
||||
if (weightMap.ContainsKey(classLabels[i]))
|
||||
weights[i] = weightMap[classLabels[i]];
|
||||
else
|
||||
weights[i] = 1f;
|
||||
Debug.Log("[Training info] Adding weight " + weights[i] + " for " + classLabels[i] + ".");
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("[Training info] Preparing trainig data.");
|
||||
|
||||
int train_split = maxSamples * 3 / 4;
|
||||
int test_split = maxSamples - train_split;
|
||||
int rows_train = classes * train_split;
|
||||
int rows_test = classes * test_split;
|
||||
float[] X_train = new float[rows_train * cols];
|
||||
float[] X_test = new float[rows_test * cols];
|
||||
float[] y_train = new float[rows_train];
|
||||
float[] y_test = new float[rows_test];
|
||||
int i_train = 0;
|
||||
int i_test = 0;
|
||||
System.Random rnd = new System.Random();
|
||||
for (int i = 0; i < classes; i++) {
|
||||
List<float[]> list = new List<float[]>(expressions[keys[i]]);
|
||||
list.RemoveAll(x => x.Length != colsFull);
|
||||
int local_train_split = list.Count * 3 / 4;
|
||||
test_split = list.Count - local_train_split;
|
||||
for (int j = list.Count; j > 1;) {
|
||||
j--;
|
||||
int k = rnd.Next(j + 1);
|
||||
float[] tmp = list[k];
|
||||
list[k] = list[j];
|
||||
list[j]= tmp;
|
||||
}
|
||||
int train_split_current = train_split;
|
||||
if (classLabels[i] == "neutral")
|
||||
train_split_current *= 1;
|
||||
for (int j = 0; j < train_split_current; j++) {
|
||||
float factor = 1f;
|
||||
float adder = 0f;
|
||||
/*if (j > train_split || j > list.Count) {
|
||||
factor = UnityEngine.Random.Range(0.98f, 1.02f);
|
||||
adder = UnityEngine.Random.Range(-0.02f, 0.02f);
|
||||
}*/
|
||||
for (int k = 0; k < cols; k++) {
|
||||
float v = list[j % local_train_split][indices[k]] * factor + adder;
|
||||
X_train[i_train * cols + k] = v;
|
||||
}
|
||||
y_train[i_train] = i;
|
||||
i_train++;
|
||||
}
|
||||
for (int j = local_train_split; j < local_train_split + test_split; j++) {
|
||||
for (int k = 0; k < cols; k++) {
|
||||
float v = list[j][indices[k]];
|
||||
X_test[i_test * cols + k] = v;
|
||||
}
|
||||
y_test[i_test] = i;
|
||||
i_test++;
|
||||
}
|
||||
}
|
||||
Debug.Log("[Training info] Preparation complete.");
|
||||
int probability = 0;
|
||||
if (enableProbabilityTraining)
|
||||
probability = 1;
|
||||
model.TrainModel(X_train, y_train, weights, i_train, cols, probability, C);
|
||||
confusionMatrix = model.ConfusionMatrix(X_test, y_test, i_test, out accuracy);
|
||||
confusionMatrixString = SVMModel.FormatMatrix(confusionMatrix, classLabels);
|
||||
for (int label = 0; label < classes; label++) {
|
||||
int total = 0;
|
||||
for (int p = 0; p < classes; p++) {
|
||||
total += confusionMatrix[label, p];
|
||||
}
|
||||
float error = 100f * (1f - ((float)confusionMatrix[label, label] / (float)total));
|
||||
if (error > warningThreshold) {
|
||||
accuracyWarnings.Add("[Training warning] The expression \"" + classLabels[label] + "\" is misclassified with a chance of " + error.ToString("0.00") + "%.");
|
||||
Debug.Log("[Training warning] The expression \"" + classLabels[label] + "\" is misclassified with a chance of " + error.ToString("0.00") + "%.");
|
||||
}
|
||||
}
|
||||
warnings = accuracyWarnings.ToArray();
|
||||
modelReady = true;
|
||||
Debug.Log("[Training info] Trained model.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PredictExpression() {
|
||||
if (openSee == null)
|
||||
return false;
|
||||
if (!simpleMode && !(modelReady && model.Ready())) {
|
||||
ResetInfo();
|
||||
return false;
|
||||
}
|
||||
openSeeData = openSee.GetOpenSeeData(faceId);
|
||||
if (openSeeData == null || openSeeData.time <= lastCapture)
|
||||
return false;
|
||||
if (simpleMode) {
|
||||
ThresholdDetection();
|
||||
return true;
|
||||
}
|
||||
float[] faceData = GetData(openSeeData);
|
||||
float[] predictionData = new float[cols];
|
||||
for (int i = 0; i < cols; i++)
|
||||
predictionData[i] = faceData[indices[i]];
|
||||
float[] prediction = model.Predict(predictionData, out probabilities, 1);
|
||||
int predictedExpression = (int)Mathf.Round(prediction[0]);
|
||||
if (predictedExpression == lastPrediction)
|
||||
lastPredictionCount++;
|
||||
else {
|
||||
lastPrediction = predictedExpression;
|
||||
lastPredictionCount = 1;
|
||||
}
|
||||
if (lastPredictionCount > expressionStabilizer) {
|
||||
currentPrediction = lastPrediction;
|
||||
expressionTime = Time.time;
|
||||
}
|
||||
if (currentPrediction >= 0 && currentPrediction <= classLabels.Length)
|
||||
expression = classLabels[currentPrediction];
|
||||
else
|
||||
expression = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CaptureCalibrationData() {
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return false;
|
||||
}
|
||||
if (recordingSkip < 1)
|
||||
recordingSkip = 1;
|
||||
if (calibrationExpression == "")
|
||||
return false;
|
||||
if (expressions.ContainsKey(calibrationExpression) || expressions.Keys.Count < 10) {
|
||||
openSeeData = openSee.GetOpenSeeData(faceId);
|
||||
if (openSeeData == null || openSeeData.time <= lastCapture)
|
||||
return false;
|
||||
if (!expressions.ContainsKey(calibrationExpression)) {
|
||||
expressions.Add(calibrationExpression, new List<float[]>());
|
||||
}
|
||||
List<float[]> list = expressions[calibrationExpression];
|
||||
if (list.Count >= maxSamples) {
|
||||
percentRecorded = 100f;
|
||||
recording = allowOverRecording;
|
||||
if (!recording)
|
||||
return false;
|
||||
}
|
||||
int skip = recordingSkip;
|
||||
if (percentRecorded >= 100f)
|
||||
skip = overRecordingSkip;
|
||||
if (frameCount % skip != 0)
|
||||
return true;
|
||||
if (percentRecorded >= 100f) {
|
||||
list[rnd.Next(maxSamples)] = GetData(openSeeData);
|
||||
} else
|
||||
list.Add(GetData(openSeeData));
|
||||
percentRecorded = 100f * (float)list.Count/(float)maxSamples;
|
||||
return true;
|
||||
} else {
|
||||
recording = false;
|
||||
calibrationExpression = "";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] GetClassLabels() {
|
||||
List<string> labels = new List<string>();
|
||||
foreach (string l in classLabels)
|
||||
labels.Add(String.Copy(l));
|
||||
return labels.ToArray();
|
||||
}
|
||||
|
||||
public string[] GetExpressions() {
|
||||
List<string> keys = new List<string>();
|
||||
foreach (string key in expressions.Keys)
|
||||
keys.Add(key);
|
||||
keys.Sort();
|
||||
return keys.ToArray();
|
||||
}
|
||||
|
||||
public void ClearExpression() {
|
||||
if (expressions.ContainsKey(calibrationExpression))
|
||||
expressions.Remove(calibrationExpression);
|
||||
clear = false;
|
||||
}
|
||||
|
||||
public void LoadFromBytes(byte[] data) {
|
||||
load = false;
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return;
|
||||
}
|
||||
OpenSeeExpressionRepresentation.LoadSerialized(data, out expressions, out model, out classLabels, out indices, ref pointSelection);
|
||||
cols = indices.Length;
|
||||
if (model.Ready())
|
||||
modelReady = true;
|
||||
}
|
||||
|
||||
public byte[] SaveToBytes() {
|
||||
save = false;
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return null;
|
||||
}
|
||||
return OpenSeeExpressionRepresentation.ToSerialized(expressions, model, classLabels, indices, pointSelection);
|
||||
}
|
||||
|
||||
public void LoadFromFile() {
|
||||
load = false;
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return;
|
||||
}
|
||||
byte[] data = File.ReadAllBytes(filename);
|
||||
LoadFromBytes(data);
|
||||
}
|
||||
|
||||
public void SaveToFile() {
|
||||
save = false;
|
||||
if (openSee == null) {
|
||||
ResetInfo();
|
||||
return;
|
||||
}
|
||||
byte[] data = SaveToBytes();
|
||||
File.WriteAllBytes(filename, data);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
frameCount++;
|
||||
if (openSee == null || expressions == null) {
|
||||
ResetInfo();
|
||||
return;
|
||||
}
|
||||
if (clear)
|
||||
ClearExpression();
|
||||
if (recording && calibrationExpression != "") {
|
||||
CaptureCalibrationData();
|
||||
}
|
||||
if (train)
|
||||
TrainModel();
|
||||
expressionNumber = expressions.Keys.Count;
|
||||
if (load)
|
||||
LoadFromFile();
|
||||
if (save)
|
||||
SaveToFile();
|
||||
if (!predict)
|
||||
expression = "";
|
||||
else if (simpleMode || modelReady)
|
||||
PredictExpression();
|
||||
else
|
||||
predict = false;
|
||||
|
||||
if (expressions.ContainsKey(calibrationExpression))
|
||||
percentRecorded = 100f * (float)expressions[calibrationExpression].Count/(float)maxSamples;
|
||||
else
|
||||
percentRecorded = 0f;
|
||||
|
||||
if (openSeeData == null)
|
||||
return;
|
||||
lastCapture = openSeeData.time;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9066de4c7793c1e40b212724f72fc064
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,331 +0,0 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
[DefaultExecutionOrder(-1)]
|
||||
public class OpenSeeIKTarget : MonoBehaviour
|
||||
{
|
||||
[Header("Settings")]
|
||||
[Tooltip("This is the source of face tracking data.")]
|
||||
public OpenSee openSee;
|
||||
[Tooltip("When this is set to another game object, its kinematic interpolation component will get updated from values of this.")]
|
||||
public OpenSeeKinematicInterpolation kinematicInterpolation;
|
||||
[Tooltip("When set, the this object will follow the specified transform instead and automatically recalibrate based on it.")]
|
||||
public Transform externalTarget = null;
|
||||
[Tooltip("This can be used to skip the first few tracking frames.")]
|
||||
public int skipFirst = 5;
|
||||
[Tooltip("When set to true, the next rotation and translation values will be set as zero rotation and zero translation.")]
|
||||
public bool calibrate = true;
|
||||
[Tooltip("This sets which face id to look for in the OpenSee data.")]
|
||||
public int faceId = 0;
|
||||
[Tooltip("When enabled, the IK target will not move up and down.")]
|
||||
public bool preventVerticalMotion = false;
|
||||
[Tooltip("When this is enabled, rotations and movement are mirrored.")]
|
||||
public bool mirrorMotion = false;
|
||||
[Tooltip("Often, the translation vector's scale is too high. Setting this somewhere between 0.05 to 0.3 seems stabilize things, but it also reduces the range of motion.")]
|
||||
public float translationScale = 0.3f;
|
||||
[Tooltip("This smoothed out the detected motion. Values can be between 0 (no smoothing) and 1 (no motion). The appropriate value probably depends on the camera's frame rate.")]
|
||||
[Range(0, 1)]
|
||||
public float smooth = 0.2f;
|
||||
[Tooltip("When enabled, tracking will lag by one video capture frame, but the captured motion information is interpolated between frames, making things look smoother and less jittery and jumpy.")]
|
||||
public bool interpolate = true;
|
||||
[Tooltip("When set to true, the transform will be updated in FixedUpdate, otherwise it will be updated in Update.")]
|
||||
public bool fixedUpdate = false;
|
||||
[Tooltip("When set to true, the IK target's position and location will slowly drift back to its neutral pose.")]
|
||||
public bool driftBack = false;
|
||||
[Tooltip("This sets the strength of the drifting effect. At 0 there is now drifting, while the IK target will be static at 1.")]
|
||||
public float driftFactor = 0.005f;
|
||||
[Tooltip("This sets the minimum angle difference between two poses after which the pose will be considered an outlier.")]
|
||||
[Range(0, 360)]
|
||||
public float outlierThresholdAngle = 35f;
|
||||
[Tooltip("This sets the number of seconds for which angle outliers are ignored before being accepted. Setting this to zero will disable outlier skipping.")]
|
||||
public float outlierSkipPeriod = 0.4f;
|
||||
[Tooltip("This sets the minimum distance difference between two poses after which the pose will be considered an outlier.")]
|
||||
public float outlierThresholdDistance = 2f;
|
||||
[Tooltip("This sets the number of seconds for which distance outliers are ignored before being accepted. Once this period elapses, automatic recalibration is triggered. Setting this to zero will disable outlier skipping.")]
|
||||
public float outlierSkipPeriodDistance = 0.4f;
|
||||
[Tooltip("If set to true, persistent positional outliers will cause automatic recalibration.")]
|
||||
public bool outlierRecalibrate = false;
|
||||
[Tooltip("If set to true, recalibration caused by positional outliers will keep the current position and rotation of the head.")]
|
||||
public bool outlierKeepOffset = false;
|
||||
[Header("Information")]
|
||||
[Tooltip("This is the number of received tracking frames.")]
|
||||
public int trackingFrames = 0;
|
||||
[Tooltip("This is the current rotation calibration value as euler angles.")]
|
||||
public Vector3 rotationOffset = new Vector3(0f, 0f, -90f);
|
||||
[Tooltip("This is the current translation calibration.")]
|
||||
public Vector3 translationOffset = new Vector3(0f, 0f, 0f);
|
||||
[Tooltip("This is the average number of interpolated frames per received tracking data.")]
|
||||
public float averageInterpolations = 0f;
|
||||
[Tooltip("This is the number of outlier skipped tracking data frames so far.")]
|
||||
public int outlierSkips = 0;
|
||||
[Tooltip("This is the number of distance outlier skipped tracking data frames so far.")]
|
||||
public int outlierSkipsDistance = 0;
|
||||
[Tooltip("This is the number of times a recalibration was triggered due to positional outliers.")]
|
||||
public int outlierCalibrations = 0;
|
||||
|
||||
private double updated = 0.0f;
|
||||
private Quaternion dR = Quaternion.Euler(0f, 0f, -90f);
|
||||
private Vector3 dT = new Vector3(0f, 0f, 0f);
|
||||
|
||||
private int interpolationCount = 1;
|
||||
private int interpolateState = 0;
|
||||
private float avgInterps = 1f;
|
||||
private Quaternion lastR;
|
||||
private Vector3 lastT;
|
||||
private Quaternion currentR;
|
||||
private Vector3 currentT;
|
||||
private bool lastMirror = false;
|
||||
|
||||
private bool gotAccepted = false;
|
||||
private Quaternion lastAccepted;
|
||||
private bool skipped = false;
|
||||
private float skipStartTime = -1f;
|
||||
|
||||
private bool gotAcceptedPosition = false;
|
||||
private Vector3 lastAcceptedPosition;
|
||||
private bool skippedPosition = false;
|
||||
private float skipStartTimePosition = -1f;
|
||||
|
||||
private Transform lastExternalTarget = null;
|
||||
|
||||
public void Calibrate() {
|
||||
calibrate = true;
|
||||
}
|
||||
|
||||
public static Vector3 PrettyEuler(Vector3 last, Vector3 current) {
|
||||
// Ew, but how would you do componentwise filtering/interpolation with quaternions?
|
||||
if (Mathf.Abs(current.x - last.x) > Mathf.Abs((current.x - 360f) - last.x))
|
||||
current.x -= 360f;
|
||||
else if (Mathf.Abs(current.x - last.x) > Mathf.Abs((current.x + 360f) - last.x))
|
||||
current.x += 360f;
|
||||
if (Mathf.Abs(current.y - last.y) > Mathf.Abs((current.y - 360f) - last.y))
|
||||
current.y -= 360f;
|
||||
else if (Mathf.Abs(current.y - last.y) > Mathf.Abs((current.y + 360f) - last.y))
|
||||
current.x += 360f;
|
||||
if (Mathf.Abs(current.z - last.z) > Mathf.Abs((current.z - 360f) - last.z))
|
||||
current.z -= 360f;
|
||||
else if (Mathf.Abs(current.z - last.z) > Mathf.Abs((current.z + 360f) - last.z))
|
||||
current.z += 360f;
|
||||
return current;
|
||||
}
|
||||
|
||||
Vector3 FilterPos(Vector3 a, Vector3 b) {
|
||||
return Vector3.Lerp(a, b, 1f - smooth);
|
||||
}
|
||||
|
||||
Quaternion FilterRot(Quaternion a, Quaternion b) {
|
||||
return Quaternion.Lerp(a, b, 1f - smooth);
|
||||
}
|
||||
|
||||
void Interpolate() {
|
||||
if (!interpolate || interpolateState < 2)
|
||||
return;
|
||||
float t = Mathf.Clamp((float)interpolationCount / avgInterps, 0f, 1.5f);
|
||||
transform.localPosition = Vector3.SlerpUnclamped(lastT, currentT, t);
|
||||
transform.localRotation = Quaternion.SlerpUnclamped(lastR, currentR, t);
|
||||
interpolationCount++;
|
||||
//transform.localPosition = new Vector3(0.3f * ((Time.time/3f) % 3f) - 0.5f, 0.3f * Mathf.Sin(Mathf.Rad2Deg * ((Time.time/5f) % 3f)), 0f);
|
||||
//transform.localRotation = Quaternion.identity * Quaternion.AngleAxis(20f * Mathf.Sin(Mathf.Rad2Deg * ((Time.time/5f) % 3f)) - 10f, Vector3.up);
|
||||
}
|
||||
|
||||
Quaternion MirrorQuaternion(Quaternion q) {
|
||||
return new Quaternion(-q.x, q.y, q.z, -q.w);
|
||||
}
|
||||
|
||||
Vector3 MirrorTranslation(Vector3 v) {
|
||||
return new Vector3(-v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
void RunUpdate() {
|
||||
var openSeeData = openSee.GetOpenSeeData(faceId);
|
||||
Quaternion convertedQuaternion;
|
||||
Vector3 t;
|
||||
Vector3 convertedTranslation;
|
||||
|
||||
if (externalTarget == null) {
|
||||
if (openSeeData == null || openSeeData.fit3DError > openSee.maxFit3DError)
|
||||
return;
|
||||
if (openSeeData.time > updated) {
|
||||
updated = openSeeData.time;
|
||||
trackingFrames++;
|
||||
if (trackingFrames < skipFirst)
|
||||
return;
|
||||
} else {
|
||||
Interpolate();
|
||||
return;
|
||||
}
|
||||
|
||||
convertedQuaternion = new Quaternion(-openSeeData.rawQuaternion.y, -openSeeData.rawQuaternion.x, openSeeData.rawQuaternion.z, openSeeData.rawQuaternion.w);
|
||||
t = openSeeData.translation;
|
||||
t.x = -t.x;
|
||||
t.z = -t.z;
|
||||
convertedTranslation = new Vector3(t.x, t.y, t.z);
|
||||
} else {
|
||||
convertedQuaternion = externalTarget.localRotation;
|
||||
convertedTranslation = externalTarget.localPosition;
|
||||
t = convertedTranslation;
|
||||
}
|
||||
|
||||
// Check for angular outliers
|
||||
if (gotAccepted) {
|
||||
float angularDifference = Quaternion.Angle(lastAccepted, convertedQuaternion);
|
||||
if (angularDifference >= outlierThresholdAngle) {
|
||||
if (!skipped) {
|
||||
if (outlierSkipPeriod >= 0.0000001f) {
|
||||
skipStartTime = Time.time;
|
||||
skipped = true;
|
||||
}
|
||||
} else {
|
||||
if (Time.time > skipStartTime + outlierSkipPeriod) {
|
||||
lastAccepted = convertedQuaternion;
|
||||
skipped = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skipped = false;
|
||||
lastAccepted = convertedQuaternion;
|
||||
}
|
||||
} else {
|
||||
lastAccepted = convertedQuaternion;
|
||||
skipped = false;
|
||||
}
|
||||
gotAccepted = true;
|
||||
if (skipped) {
|
||||
outlierSkips += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for positional outliers
|
||||
bool keepOffset = false;
|
||||
if (gotAcceptedPosition) {
|
||||
float distance = Vector3.Distance(t, lastAcceptedPosition);
|
||||
if (distance > outlierThresholdDistance) {
|
||||
if (!skippedPosition) {
|
||||
if (outlierSkipPeriodDistance >= 0.0000001f) {
|
||||
skipStartTimePosition = Time.time;
|
||||
skippedPosition = true;
|
||||
}
|
||||
} else {
|
||||
if (Time.time > skipStartTimePosition + outlierSkipPeriodDistance) {
|
||||
lastAcceptedPosition = t;
|
||||
skippedPosition = false;
|
||||
if (outlierRecalibrate) {
|
||||
calibrate = true;
|
||||
keepOffset = outlierKeepOffset;
|
||||
outlierCalibrations += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skippedPosition = false;
|
||||
lastAcceptedPosition = t;
|
||||
}
|
||||
} else {
|
||||
skippedPosition = false;
|
||||
lastAcceptedPosition = t;
|
||||
}
|
||||
gotAcceptedPosition = true;
|
||||
if (skippedPosition) {
|
||||
outlierSkipsDistance += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
calibrate = calibrate || (externalTarget != lastExternalTarget);
|
||||
lastExternalTarget = externalTarget;
|
||||
|
||||
if (calibrate) {
|
||||
dR = convertedQuaternion;
|
||||
dR = Quaternion.Inverse(dR);
|
||||
dT = t;
|
||||
rotationOffset = new Vector3(dR.eulerAngles.x, dR.eulerAngles.y, dR.eulerAngles.z);
|
||||
translationOffset = new Vector3(dT.x, dT.y, dT.z);
|
||||
lastAcceptedPosition = translationOffset;
|
||||
skippedPosition = false;
|
||||
}
|
||||
|
||||
if (mirrorMotion != lastMirror || (mirrorMotion && calibrate)) {
|
||||
dR = Quaternion.Inverse(MirrorQuaternion(Quaternion.Inverse(dR)));
|
||||
dT = MirrorTranslation(dT);
|
||||
lastMirror = mirrorMotion;
|
||||
}
|
||||
if (calibrate && keepOffset) {
|
||||
dR = Quaternion.Inverse(Quaternion.Inverse(transform.localRotation) * Quaternion.Inverse(dR));
|
||||
dT -= transform.localPosition;
|
||||
}
|
||||
calibrate = false;
|
||||
|
||||
if (mirrorMotion) {
|
||||
convertedQuaternion = MirrorQuaternion(convertedQuaternion);
|
||||
t = MirrorTranslation(t);
|
||||
}
|
||||
|
||||
if (interpolateState > 1)
|
||||
avgInterps = Mathf.Lerp(avgInterps, (float)interpolationCount, 0.15f);
|
||||
interpolationCount = 0;
|
||||
averageInterpolations = avgInterps;
|
||||
|
||||
lastT = transform.localPosition;
|
||||
lastR = transform.localRotation;
|
||||
if (interpolateState > 0) {
|
||||
currentT = FilterPos(transform.localPosition, (t - dT) * translationScale);
|
||||
currentR = FilterRot(transform.localRotation, convertedQuaternion * dR);
|
||||
} else {
|
||||
currentT = (t - dT) * translationScale;
|
||||
currentR = convertedQuaternion * dR;
|
||||
}
|
||||
if (interpolateState < 2)
|
||||
interpolateState++;
|
||||
|
||||
if (interpolate) {
|
||||
Interpolate();
|
||||
} else {
|
||||
transform.localPosition = FilterPos(transform.localPosition, (t - dT) * translationScale);
|
||||
transform.localRotation = FilterRot(transform.localRotation, convertedQuaternion * dR);
|
||||
}
|
||||
|
||||
if (preventVerticalMotion) {
|
||||
currentT.y = 0f;
|
||||
Vector3 pos = transform.localPosition;
|
||||
pos.y = 0f;
|
||||
transform.localPosition = pos;
|
||||
}
|
||||
|
||||
if (kinematicInterpolation != null && kinematicInterpolation.gameObject != gameObject) {
|
||||
if (interpolate)
|
||||
kinematicInterpolation.UpdateKI(updated, currentT, currentR);
|
||||
else
|
||||
kinematicInterpolation.UpdateKI(updated, transform.localPosition, transform.localRotation);
|
||||
}
|
||||
|
||||
if (driftBack) {
|
||||
if (mirrorMotion) {
|
||||
dT = Vector3.Lerp(dT, MirrorTranslation(convertedTranslation), driftFactor);
|
||||
dR = Quaternion.Lerp(dR, Quaternion.Inverse(convertedQuaternion), driftFactor);
|
||||
} else {
|
||||
dT = Vector3.Lerp(dT, convertedTranslation, driftFactor);
|
||||
dR = Quaternion.Lerp(dR, Quaternion.Inverse(convertedQuaternion), driftFactor);
|
||||
}
|
||||
rotationOffset = new Vector3(dR.eulerAngles.x, dR.eulerAngles.y, dR.eulerAngles.z);
|
||||
translationOffset = new Vector3(dT.x, dT.y, dT.z);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPreventVerticalMotion(bool v) {
|
||||
preventVerticalMotion = v;
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (fixedUpdate)
|
||||
RunUpdate();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!fixedUpdate)
|
||||
RunUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 16cac0ed1df366e4cbb82f6c6a93ef1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -5
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
public class InterpolatedMap<T, V, U> where V : InterpolatedValue<U>, new() {
|
||||
private Dictionary<T, V> map = new Dictionary<T, V>();
|
||||
private float clamp = 1f;
|
||||
private float smoothing = 0f;
|
||||
private bool gotDefault = false;
|
||||
private U defaultValue;
|
||||
|
||||
public void Clear() {
|
||||
map.Clear();
|
||||
}
|
||||
|
||||
public void SetSmoothing(float smoothing) {
|
||||
this.smoothing = smoothing;
|
||||
foreach (var entry in map) {
|
||||
if (entry.Value != null)
|
||||
entry.Value.SetSmoothing(smoothing);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetClamp(float clamp) {
|
||||
this.clamp = clamp;
|
||||
foreach (var entry in map) {
|
||||
if (entry.Value != null)
|
||||
entry.Value.SetClamp(clamp);
|
||||
}
|
||||
}
|
||||
|
||||
void Prepare(T key) {
|
||||
if (!map.ContainsKey(key)) {
|
||||
map.Add(key, new V());
|
||||
map[key].SetSmoothing(smoothing);
|
||||
map[key].SetClamp(clamp);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValue(T key, U v) {
|
||||
Prepare(key);
|
||||
map[key].SetValue(v);
|
||||
}
|
||||
|
||||
public void Store(T key, U v) {
|
||||
Prepare(key);
|
||||
map[key].UpdateValue(v);
|
||||
}
|
||||
|
||||
public void Store(T key, U v, double nowT) {
|
||||
if (!map.ContainsKey(key)) {
|
||||
map.Add(key, new V());
|
||||
map[key].SetSmoothing(smoothing);
|
||||
map[key].SetClamp(clamp);
|
||||
}
|
||||
map[key].UpdateValue(v, nowT);
|
||||
}
|
||||
|
||||
public U GetDefault() {
|
||||
if (gotDefault)
|
||||
return defaultValue;
|
||||
V tmp = new V();
|
||||
defaultValue = tmp.GetDefault();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public bool Check(T key) {
|
||||
return map.ContainsKey(key);
|
||||
}
|
||||
|
||||
public U Get(T key) {
|
||||
if (!map.ContainsKey(key))
|
||||
return GetDefault();
|
||||
return map[key].Interpolate();
|
||||
}
|
||||
}
|
||||
|
||||
abstract public class InterpolatedValue<V> {
|
||||
private float interpolateT = 0f;
|
||||
private float updateT = 0f;
|
||||
private double lastT = 0f;
|
||||
private double currentT = 0f;
|
||||
private float clamp = 1f;
|
||||
private float smoothing = 0f;
|
||||
private int gotData = 0;
|
||||
|
||||
private bool haveDefault = false;
|
||||
private V defaultValue;
|
||||
|
||||
private bool valueSet = false;
|
||||
private V dataValue;
|
||||
private V dataValueLast;
|
||||
|
||||
abstract public V ValueLerpUnclamped(V a, V b, float t);
|
||||
abstract public V GetBuiltinDefault();
|
||||
|
||||
public void SetDefault(V defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
haveDefault = true;
|
||||
}
|
||||
|
||||
public void ResetDefault() {
|
||||
haveDefault = false;
|
||||
}
|
||||
|
||||
public V GetDefault() {
|
||||
if (haveDefault)
|
||||
return defaultValue;
|
||||
return GetBuiltinDefault();
|
||||
}
|
||||
|
||||
public void SetSmoothing(float smoothing) {
|
||||
this.smoothing = smoothing;
|
||||
}
|
||||
|
||||
public void SetClamp(float clamp) {
|
||||
this.clamp = clamp;
|
||||
}
|
||||
|
||||
public void SetValue(V v) {
|
||||
dataValueLast = v;
|
||||
dataValue = v;
|
||||
valueSet = true;
|
||||
}
|
||||
|
||||
public InterpolatedValue(float smoothing = 0f, float clamp = 1f) {
|
||||
this.smoothing = smoothing;
|
||||
this.clamp = clamp;
|
||||
}
|
||||
|
||||
public void UpdateValue(V v) {
|
||||
UpdateValue(v, Time.realtimeSinceStartup);
|
||||
}
|
||||
|
||||
public void UpdateValue(V v, double nowT) {
|
||||
if (nowT < 0f)
|
||||
return;
|
||||
lock (this) {
|
||||
if (nowT < currentT || gotData == 0) {
|
||||
// Reinitialize
|
||||
gotData = 1;
|
||||
updateT = Time.time;
|
||||
lastT = 0f;
|
||||
currentT = nowT;
|
||||
dataValueLast = GetDefault();
|
||||
dataValue = ValueLerpUnclamped(v, dataValueLast, smoothing);
|
||||
} else if (nowT == currentT) {
|
||||
// Change value
|
||||
dataValue = ValueLerpUnclamped(v, dataValueLast, smoothing);
|
||||
} else {
|
||||
// Regular operation
|
||||
updateT = Time.time;
|
||||
lastT = currentT;
|
||||
currentT = nowT;
|
||||
if (gotData < 2)
|
||||
gotData++;
|
||||
dataValueLast = dataValue;
|
||||
dataValue = ValueLerpUnclamped(v, dataValueLast, smoothing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public V Interpolate() {
|
||||
lock (this) {
|
||||
interpolateT = Time.time;
|
||||
if (gotData < 2) {
|
||||
if (valueSet)
|
||||
return dataValue;
|
||||
else
|
||||
return GetDefault();
|
||||
}
|
||||
float t = Mathf.Min((interpolateT - updateT) / (float)(currentT - lastT), clamp);
|
||||
return ValueLerpUnclamped(dataValueLast, dataValue, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class InterpolatedColor : InterpolatedValue<Color> {
|
||||
public override Color ValueLerpUnclamped(Color a, Color b, float t) {
|
||||
return Color.LerpUnclamped(a, b, t);
|
||||
}
|
||||
public override Color GetBuiltinDefault() {
|
||||
return Color.clear;
|
||||
}
|
||||
}
|
||||
|
||||
public class InterpolatedQuaternion : InterpolatedValue<Quaternion> {
|
||||
public override Quaternion ValueLerpUnclamped(Quaternion a, Quaternion b, float t) {
|
||||
return Quaternion.SlerpUnclamped(a, b, t);
|
||||
}
|
||||
public override Quaternion GetBuiltinDefault() {
|
||||
return Quaternion.identity;
|
||||
}
|
||||
}
|
||||
|
||||
public class InterpolatedVector2 : InterpolatedValue<Vector2> {
|
||||
public override Vector2 ValueLerpUnclamped(Vector2 a, Vector2 b, float t) {
|
||||
return Vector2.LerpUnclamped(a, b, t);
|
||||
}
|
||||
public override Vector2 GetBuiltinDefault() {
|
||||
return Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public class InterpolatedVector3 : InterpolatedValue<Vector3> {
|
||||
public override Vector3 ValueLerpUnclamped(Vector3 a, Vector3 b, float t) {
|
||||
return Vector3.LerpUnclamped(a, b, t);
|
||||
}
|
||||
public override Vector3 GetBuiltinDefault() {
|
||||
return Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public class InterpolatedVector4 : InterpolatedValue<Vector4> {
|
||||
public override Vector4 ValueLerpUnclamped(Vector4 a, Vector4 b, float t) {
|
||||
return Vector4.LerpUnclamped(a, b, t);
|
||||
}
|
||||
public override Vector4 GetBuiltinDefault() {
|
||||
return Vector4.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public class InterpolatedFloat : InterpolatedValue<float> {
|
||||
public override float ValueLerpUnclamped(float a, float b, float t) {
|
||||
return Mathf.LerpUnclamped(a, b, t);
|
||||
}
|
||||
public override float GetBuiltinDefault() {
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d61e11f1b6900a7419383be1377f191b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// From: https://stackoverflow.com/a/9164742
|
||||
// Is this better? https://stackoverflow.com/a/36648201
|
||||
namespace OpenSee
|
||||
{
|
||||
public class Win32 {
|
||||
[DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool CloseHandle(IntPtr hObject);
|
||||
}
|
||||
|
||||
public class Job : IDisposable
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
static extern IntPtr CreateJobObject(IntPtr a, string lpName);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
private IntPtr handle;
|
||||
private bool disposed;
|
||||
|
||||
public Job()
|
||||
{
|
||||
handle = CreateJobObject(IntPtr.Zero, null);
|
||||
|
||||
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
{
|
||||
LimitFlags = 0x2000
|
||||
};
|
||||
|
||||
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
{
|
||||
BasicLimitInformation = info
|
||||
};
|
||||
|
||||
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
||||
IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
||||
|
||||
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
|
||||
throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error()));
|
||||
|
||||
Marshal.FreeHGlobal(extendedInfoPtr);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing) { }
|
||||
|
||||
Close();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseHandle(handle);
|
||||
handle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public bool AddProcess(IntPtr processHandle)
|
||||
{
|
||||
return AssignProcessToJobObject(handle, processHandle);
|
||||
}
|
||||
|
||||
public bool AddProcess(int processId)
|
||||
{
|
||||
return AddProcess(Process.GetProcessById(processId).Handle);
|
||||
}
|
||||
}
|
||||
|
||||
#region Helper classes
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct IO_COUNTERS
|
||||
{
|
||||
public UInt64 ReadOperationCount;
|
||||
public UInt64 WriteOperationCount;
|
||||
public UInt64 OtherOperationCount;
|
||||
public UInt64 ReadTransferCount;
|
||||
public UInt64 WriteTransferCount;
|
||||
public UInt64 OtherTransferCount;
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
{
|
||||
public Int64 PerProcessUserTimeLimit;
|
||||
public Int64 PerJobUserTimeLimit;
|
||||
public UInt32 LimitFlags;
|
||||
public UIntPtr MinimumWorkingSetSize;
|
||||
public UIntPtr MaximumWorkingSetSize;
|
||||
public UInt32 ActiveProcessLimit;
|
||||
public UIntPtr Affinity;
|
||||
public UInt32 PriorityClass;
|
||||
public UInt32 SchedulingClass;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public UInt32 nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public Int32 bInheritHandle;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
{
|
||||
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
|
||||
public IO_COUNTERS IoInfo;
|
||||
public UIntPtr ProcessMemoryLimit;
|
||||
public UIntPtr JobMemoryLimit;
|
||||
public UIntPtr PeakProcessMemoryUsed;
|
||||
public UIntPtr PeakJobMemoryUsed;
|
||||
}
|
||||
|
||||
public enum JobObjectInfoType
|
||||
{
|
||||
AssociateCompletionPortInformation = 7,
|
||||
BasicLimitInformation = 2,
|
||||
BasicUIRestrictions = 4,
|
||||
EndOfJobTimeInformation = 6,
|
||||
ExtendedLimitInformation = 9,
|
||||
SecurityLimitInformation = 5,
|
||||
GroupInformation = 11
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3251308a085a78545a82e7f34ebc2d34
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class OpenSeeKinematicInterpolation : MonoBehaviour
|
||||
{
|
||||
public float interpolateClamp = 1f;
|
||||
|
||||
private Vector3 lastPos;
|
||||
private Vector3 lastPosV;
|
||||
private double lastT;
|
||||
|
||||
private Vector3 currentPos;
|
||||
private Vector3 currentPosV;
|
||||
private double currentT = 0f;
|
||||
|
||||
private Vector3 lastRot = Vector3.zero;
|
||||
private Vector3 lastRotV;
|
||||
|
||||
private Vector3 currentRot;
|
||||
private Vector3 currentRotV;
|
||||
|
||||
private float dT;
|
||||
private Vector3 coeffPosA;
|
||||
private Vector3 coeffPosB;
|
||||
private Vector3 coeffRotA;
|
||||
private Vector3 coeffRotB;
|
||||
|
||||
private double updateT;
|
||||
private double interpolateT;
|
||||
private int ready = 0;
|
||||
|
||||
public void UpdateKI(double newT, Vector3 newPos, Quaternion newRot) {
|
||||
Interpolate();
|
||||
if (newT - currentT > 3f)
|
||||
ready = 0;
|
||||
|
||||
currentPos = transform.localPosition;
|
||||
currentRot = OpenSeeIKTarget.PrettyEuler(currentRot, transform.localRotation.eulerAngles);
|
||||
float dTNow = (float)(interpolateT - updateT);
|
||||
if (interpolateT - updateT > 0 || ready < 1) {
|
||||
currentPosV = (currentPos - lastPos) / dT;
|
||||
currentRotV = (currentRot - lastRot) / dT;
|
||||
} else {
|
||||
currentPosV = Vector3.zero;
|
||||
currentRotV = Vector3.zero;
|
||||
}
|
||||
|
||||
lastPos = currentPos;
|
||||
lastPosV = currentPosV;
|
||||
lastRot = currentRot;
|
||||
lastRotV = currentRotV;
|
||||
lastT = currentT;
|
||||
|
||||
currentPos = newPos;
|
||||
currentRot = OpenSeeIKTarget.PrettyEuler(lastRot, newRot.eulerAngles);
|
||||
currentT = newT;
|
||||
dT = (float)(currentT - lastT);
|
||||
currentPosV = (currentPos - lastPos) / dT;
|
||||
currentRotV = (currentRot - lastRot) / dT;
|
||||
|
||||
float dTPow2 = Mathf.Pow(dT, 2);
|
||||
float dTPow3 = Mathf.Pow(dT, 3);
|
||||
coeffPosA = (- (2f*(currentPosV * dT + 2f * dT * lastPosV - 3f * currentPos + 3f * lastPos)) / dTPow2) / 2f;
|
||||
coeffPosB = ((6f*(currentPosV * dT + dT * lastPosV - 2f * currentPos + 2f * lastPos)) / dTPow3)/6f;
|
||||
coeffRotA = (- (2f*(currentRotV * dT + 2f * dT * lastRotV - 3f * currentRot + 3f * lastRot)) / dTPow2) / 2f;
|
||||
coeffRotB = ((6f*(currentRotV * dT + dT * lastRotV - 2f * currentRot + 2f * lastRot)) / dTPow3)/6f;
|
||||
|
||||
updateT = Time.time;
|
||||
if (ready <= 2)
|
||||
ready++;
|
||||
}
|
||||
|
||||
float InterpolateValue(float dTNow, float dTNowPow2, float dTNowPow3, float a, float b, float x1, float x2, float v1, float v2) {
|
||||
return x1 + v1 * dTNow + dTNowPow2 * a + dTNowPow3 * b;
|
||||
}
|
||||
|
||||
void Interpolate() {
|
||||
interpolateT = Time.time;
|
||||
if (interpolateT == updateT || ready <= 2)
|
||||
return;
|
||||
float dTNow = (float)(interpolateT - updateT);
|
||||
if (dTNow >= dT) {
|
||||
transform.localPosition = Vector3.LerpUnclamped(lastPos, currentPos, Mathf.Min((float)(dTNow / dT), interpolateClamp));
|
||||
transform.localEulerAngles = Vector3.LerpUnclamped(lastRot, currentRot, Mathf.Min((float)(dTNow / dT), interpolateClamp));
|
||||
} else {
|
||||
float dTNowPow2 = Mathf.Pow(dTNow, 2);
|
||||
float dTNowPow3 = Mathf.Pow(dTNow, 3);
|
||||
float x = InterpolateValue(dTNow, dTNowPow2, dTNowPow3, coeffPosA.x, coeffPosB.x, lastPos.x, currentPos.x, lastPosV.x, currentPosV.x);
|
||||
float y = InterpolateValue(dTNow, dTNowPow2, dTNowPow3, coeffPosA.y, coeffPosB.y, lastPos.y, currentPos.y, lastPosV.y, currentPosV.y);
|
||||
float z = InterpolateValue(dTNow, dTNowPow2, dTNowPow3, coeffPosA.z, coeffPosB.z, lastPos.z, currentPos.z, lastPosV.z, currentPosV.z);
|
||||
transform.localPosition = new Vector3(x, y, z);
|
||||
x = InterpolateValue(dTNow, dTNowPow2, dTNowPow3, coeffRotA.x, coeffRotB.x, lastRot.x, currentRot.x, lastRotV.x, currentRotV.x);
|
||||
y = InterpolateValue(dTNow, dTNowPow2, dTNowPow3, coeffRotA.y, coeffRotB.y, lastRot.y, currentRot.y, lastRotV.y, currentRotV.y);
|
||||
z = InterpolateValue(dTNow, dTNowPow2, dTNowPow3, coeffRotA.z, coeffRotB.z, lastRot.z, currentRot.z, lastRotV.z, currentRotV.z);
|
||||
transform.localEulerAngles = new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
Interpolate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a4112b4be90fc9e4baee9eade65378c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -1
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,474 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class OpenSeeLauncher : MonoBehaviour {
|
||||
#region DllImport
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_capture")]
|
||||
private static extern System.IntPtr create_capture_x64();
|
||||
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_devices")]
|
||||
private static extern int get_devices_x64(System.IntPtr cap);
|
||||
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_device")]
|
||||
private static extern void get_device_x64(System.IntPtr cap, int deviceno, [Out] StringBuilder namebuffer, int bufferlength);
|
||||
|
||||
[DllImport("dshowcapture_x64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "destroy_capture")]
|
||||
private static extern void destroy_capture_x64(System.IntPtr cap);
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_capture")]
|
||||
private static extern System.IntPtr create_capture_x86();
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_devices")]
|
||||
private static extern int get_devices_x86(System.IntPtr cap);
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_device")]
|
||||
private static extern void get_device_x86(System.IntPtr cap, int deviceno, [Out] StringBuilder namebuffer, int bufferlength);
|
||||
|
||||
[DllImport("dshowcapture_x86", CallingConvention = CallingConvention.Cdecl, EntryPoint = "destroy_capture")]
|
||||
private static extern void destroy_capture_x86(System.IntPtr cap);
|
||||
#endregion
|
||||
|
||||
[Header("Settings")]
|
||||
[Tooltip("If this is left empty, it will default to the OpenSee component on the same game object.")]
|
||||
public OpenSee openSeeTarget;
|
||||
[Tooltip("The numeric index of the camera as in the list returned by the ListCameras function.")]
|
||||
public int cameraIndex = -1;
|
||||
[Tooltip("The path to a video file that should be used as input for the tracker instead of a camera")]
|
||||
public string videoPath = "";
|
||||
[Tooltip("Should facetracker.exe be started automatically with the scene?")]
|
||||
public bool autoStart = false;
|
||||
[Tooltip("If this is enabled, available cameras are automatically printed to the debug console.")]
|
||||
public bool autoListCameras = false;
|
||||
[Tooltip("If this is enabled, no output from the tracker will be printed to the debug console. This cannot be changed while the tracker is running.")]
|
||||
public bool dontPrint = true;
|
||||
[Tooltip("If enabled, the next available port within 500 starting after the one given on the OpenSee component will be used.")]
|
||||
public bool dynamicPort = false;
|
||||
[Tooltip("The path to \"facetracker.exe\".")]
|
||||
public string exePath = "facetracker.exe";
|
||||
[Tooltip("The path, where the .onnx model files can be found")]
|
||||
public string modelPath = "models\\";
|
||||
[Tooltip("Additional options that should be passed to the face tracker.")]
|
||||
public List<string> commandlineOptions = new List<string>(new string[] { "--silent", "1", "--max-threads", "1" });
|
||||
[Tooltip("This string will be appended at the end of the tracker options without quoting.")]
|
||||
public string extraOptions = "";
|
||||
[Tooltip("If enabled, the tracker commandline option string will be logged with Debug.Log.")]
|
||||
public bool logCommandline = false;
|
||||
[Tooltip("IL2CPP doesn't support Proocess.Start. When this is enabled, OpenSeeLauncher will create the tracking process by calling the necessary API functions directly through the DLLs. In this case, reading the standard output from the process will not be supported and it will instead be send to the logfiles set in pinvokeStdOut and pinvokeStdErr in the persistent data directory. It will also retrieve the camera list directly through the libdshowcapture DLLs, so make sure it is part of your Unity project.")]
|
||||
public bool usePinvoke = false;
|
||||
[Tooltip("When this is enabled, even if usePinvoke is disabled, the camera list will be retrieved through libdshowcapture DLLs directly, which can be faster. Make sure the DLLs are in your Unity project.")]
|
||||
public bool usePinvokeListCameras = false;
|
||||
[Tooltip("This is the standard output log file's name in the persistent data directory.")]
|
||||
public string pinvokeStdOut = "output.txt";
|
||||
[Tooltip("This is the standard error log file's name in the persistent data directory.")]
|
||||
public string pinvokeStdErr = "error.txt";
|
||||
[Header("Runtime information")]
|
||||
[Tooltip("This field shows if the tracker is currently alive.")]
|
||||
public bool trackerAlive = false;
|
||||
|
||||
private string ip;
|
||||
private Process trackerProcess = null;
|
||||
private StringBuilder trackerSB = null;
|
||||
private bool dontPrintNow = false;
|
||||
private System.IntPtr processHandle = System.IntPtr.Zero;
|
||||
private System.IntPtr processStdOut = System.IntPtr.Zero;
|
||||
private System.IntPtr processStdErr = System.IntPtr.Zero;
|
||||
private Job job = null;
|
||||
|
||||
public void UnsetOption(string name, bool hasArgument) {
|
||||
int count = 1;
|
||||
if (hasArgument)
|
||||
count++;
|
||||
int found = -1;
|
||||
int i = 0;
|
||||
foreach (string arg in commandlineOptions) {
|
||||
if (arg == name) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (found > -1)
|
||||
commandlineOptions.RemoveRange(found, count);
|
||||
}
|
||||
|
||||
public void SetOption(string name, string argument) {
|
||||
UnsetOption(name, argument != null);
|
||||
commandlineOptions.Add(name);
|
||||
if (argument != null)
|
||||
commandlineOptions.Add(argument);
|
||||
}
|
||||
|
||||
private bool CheckSetup(bool requireTarget) {
|
||||
if (openSeeTarget == null)
|
||||
openSeeTarget = GetComponent<OpenSee>();
|
||||
if (openSeeTarget == null) {
|
||||
UnityEngine.Debug.LogError("No openSeeTarget is set.");
|
||||
return false;
|
||||
}
|
||||
IPAddress address;
|
||||
if (!File.Exists(exePath)) {
|
||||
UnityEngine.Debug.LogError("Facetracker executable cannot be found.");
|
||||
return false;
|
||||
}
|
||||
/*if (!Directory.Exists(modelPath)) {
|
||||
UnityEngine.Debug.LogError("Model directory cannot be found.");
|
||||
return false;
|
||||
}*/
|
||||
if (requireTarget) {
|
||||
ip = openSeeTarget.listenAddress;
|
||||
if (!IPAddress.TryParse(ip, out address)) {
|
||||
UnityEngine.Debug.LogError("No valid IP address was given in the OpenSee component.");
|
||||
return false;
|
||||
}
|
||||
if (openSeeTarget.listenPort < 1 || openSeeTarget.listenPort > 65535) {
|
||||
UnityEngine.Debug.LogError("No valid port was given in the OpenSee component.");
|
||||
return false;
|
||||
}
|
||||
if (cameraIndex >= 0 && videoPath != "") {
|
||||
UnityEngine.Debug.LogError("Entering both a camera index and a video filename is not valid.");
|
||||
return false;
|
||||
}
|
||||
if (cameraIndex < 0 && !File.Exists(videoPath)) {
|
||||
if (videoPath != "") {
|
||||
UnityEngine.Debug.LogError("The given video file does not exist.");
|
||||
} else {
|
||||
UnityEngine.Debug.LogError("No camera or video file was given.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (job == null) {
|
||||
job = new Job();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private string[] ListCameras_x64() {
|
||||
List<string> cameras = new List<string>();
|
||||
System.IntPtr cap = create_capture_x64();
|
||||
int count = get_devices_x64(cap);
|
||||
for (int i = 0; i < count; i++) {
|
||||
StringBuilder namebuffer = new StringBuilder(2048);
|
||||
get_device_x64(cap, i, namebuffer, 2048);
|
||||
cameras.Add(namebuffer.ToString());
|
||||
}
|
||||
destroy_capture_x64(cap);
|
||||
return cameras.ToArray();
|
||||
}
|
||||
|
||||
private string[] ListCameras_x86() {
|
||||
List<string> cameras = new List<string>();
|
||||
System.IntPtr cap = create_capture_x86();
|
||||
int count = get_devices_x86(cap);
|
||||
for (int i = 0; i < count; i++) {
|
||||
StringBuilder namebuffer = new StringBuilder(2048);
|
||||
get_device_x86(cap, i, namebuffer, 2048);
|
||||
cameras.Add(namebuffer.ToString());
|
||||
}
|
||||
destroy_capture_x86(cap);
|
||||
return cameras.ToArray();
|
||||
}
|
||||
|
||||
public string[] ListCameras() {
|
||||
if (usePinvoke || usePinvokeListCameras) {
|
||||
if (Environment.Is64BitProcess)
|
||||
return ListCameras_x64();
|
||||
else
|
||||
return ListCameras_x86();
|
||||
}
|
||||
|
||||
if (!CheckSetup(false))
|
||||
return null;
|
||||
|
||||
StringBuilder stringBuilder;
|
||||
ProcessStartInfo processStartInfo;
|
||||
Process process;
|
||||
|
||||
stringBuilder = new StringBuilder();
|
||||
|
||||
processStartInfo = new ProcessStartInfo();
|
||||
processStartInfo.CreateNoWindow = true;
|
||||
processStartInfo.RedirectStandardOutput = true;
|
||||
processStartInfo.RedirectStandardInput = true;
|
||||
processStartInfo.RedirectStandardError = true;
|
||||
processStartInfo.UseShellExecute = false;
|
||||
processStartInfo.FileName = exePath;
|
||||
processStartInfo.Arguments = "--list-cameras 2";
|
||||
|
||||
process = new Process();
|
||||
process.StartInfo = processStartInfo;
|
||||
process.EnableRaisingEvents = true;
|
||||
process.OutputDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e) {
|
||||
stringBuilder.Append(e.Data);
|
||||
stringBuilder.Append("\n");
|
||||
});
|
||||
process.Start();
|
||||
job.AddProcess(process.Handle);
|
||||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
process.CancelOutputRead();
|
||||
|
||||
string cameraInfo = stringBuilder.ToString();
|
||||
string[] cameras = cameraInfo.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return cameras;
|
||||
}
|
||||
|
||||
public float[] Benchmark(int threads) {
|
||||
if (!CheckSetup(false))
|
||||
return null;
|
||||
|
||||
StringBuilder stringBuilder;
|
||||
ProcessStartInfo processStartInfo;
|
||||
Process process;
|
||||
|
||||
stringBuilder = new StringBuilder();
|
||||
|
||||
processStartInfo = new ProcessStartInfo();
|
||||
processStartInfo.CreateNoWindow = true;
|
||||
processStartInfo.RedirectStandardOutput = true;
|
||||
processStartInfo.RedirectStandardInput = true;
|
||||
processStartInfo.RedirectStandardError = true;
|
||||
processStartInfo.UseShellExecute = false;
|
||||
processStartInfo.FileName = exePath;
|
||||
processStartInfo.Arguments = "--benchmark 1 --priority 4 --max-threads " + threads.ToString();
|
||||
|
||||
process = new Process();
|
||||
process.StartInfo = processStartInfo;
|
||||
process.EnableRaisingEvents = true;
|
||||
process.OutputDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e) {
|
||||
stringBuilder.Append(e.Data);
|
||||
stringBuilder.Append("\n");
|
||||
});
|
||||
process.Start();
|
||||
job.AddProcess(process.Handle);
|
||||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
process.CancelOutputRead();
|
||||
|
||||
string[] lines = stringBuilder.ToString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float[] results = new float[lines.Length];
|
||||
|
||||
for (int i = 0; i < lines.Length; i++) {
|
||||
results[i] = Single.Parse(lines[i]);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// From: http://csharptest.net/529/how-to-correctly-escape-command-line-arguments-in-c/index.html
|
||||
private static string EscapeArguments(params string[] args)
|
||||
{
|
||||
StringBuilder arguments = new StringBuilder();
|
||||
Regex invalidChar = new Regex("[\x00\x0a\x0d]");// these can not be escaped
|
||||
Regex needsQuotes = new Regex(@"\s|""");// contains whitespace or two quote characters
|
||||
Regex escapeQuote = new Regex(@"(\\*)(""|$)");// one or more '\' followed with a quote or end of string
|
||||
for (int carg = 0; args != null && carg < args.Length; carg++)
|
||||
{
|
||||
if (args[carg] == null) { throw new ArgumentNullException("args[" + carg + "]"); }
|
||||
if (invalidChar.IsMatch(args[carg])) { throw new ArgumentOutOfRangeException("args[" + carg + "]"); }
|
||||
if (args[carg] == String.Empty) { arguments.Append("\"\""); }
|
||||
else if (!needsQuotes.IsMatch(args[carg])) { arguments.Append(args[carg]); }
|
||||
else
|
||||
{
|
||||
arguments.Append('"');
|
||||
arguments.Append(escapeQuote.Replace(args[carg], m => m.Groups[1].Value + m.Groups[1].Value + (m.Groups[2].Value == "\"" ? "\\\"" : "")));
|
||||
arguments.Append('"');
|
||||
}
|
||||
if (carg + 1 < args.Length)
|
||||
arguments.Append(' ');
|
||||
}
|
||||
return arguments.ToString();
|
||||
}
|
||||
|
||||
public bool StartTracker() {
|
||||
if (!CheckSetup(true))
|
||||
return false;
|
||||
|
||||
List<string> arguments = new List<string>();
|
||||
arguments.Add("--ip");
|
||||
arguments.Add(ip);
|
||||
|
||||
if (dynamicPort && !openSeeTarget.listening) {
|
||||
System.Net.IPEndPoint[] inUse = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
|
||||
Array.Sort(inUse, delegate(System.Net.IPEndPoint a, System.Net.IPEndPoint b) { return a.Port.CompareTo(b.Port); });
|
||||
int port = openSeeTarget.listenPort + 1;
|
||||
for (int i = 0; i < inUse.Length; i++) {
|
||||
if (inUse[i].Port < port)
|
||||
continue;
|
||||
if (inUse[i].Port == port) {
|
||||
if (port <= openSeeTarget.listenPort + 500 && port <= IPEndPoint.MaxPort) {
|
||||
port++;
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
openSeeTarget.listenPort = port;
|
||||
}
|
||||
|
||||
arguments.Add("--port");
|
||||
arguments.Add(openSeeTarget.listenPort.ToString());
|
||||
arguments.Add("--model-dir");
|
||||
arguments.Add(modelPath);
|
||||
|
||||
arguments.Add("--capture");
|
||||
if (videoPath != "" && File.Exists(videoPath))
|
||||
arguments.Add(videoPath);
|
||||
else
|
||||
arguments.Add(cameraIndex.ToString());
|
||||
foreach (string argument in commandlineOptions) {
|
||||
arguments.Add(argument);
|
||||
}
|
||||
string argumentString = EscapeArguments(arguments.ToArray());
|
||||
if (extraOptions != "")
|
||||
argumentString = argumentString + " " + extraOptions;
|
||||
|
||||
if (logCommandline)
|
||||
UnityEngine.Debug.Log("Starting tracker: " + argumentString);
|
||||
|
||||
StopTracker();
|
||||
|
||||
if (!usePinvoke) {
|
||||
ProcessStartInfo processStartInfo;
|
||||
processStartInfo = new ProcessStartInfo();
|
||||
processStartInfo.CreateNoWindow = true;
|
||||
processStartInfo.RedirectStandardOutput = true;
|
||||
processStartInfo.RedirectStandardInput = true;
|
||||
processStartInfo.RedirectStandardError = true;
|
||||
processStartInfo.UseShellExecute = false;
|
||||
processStartInfo.FileName = exePath;
|
||||
processStartInfo.Arguments = argumentString;
|
||||
|
||||
trackerSB = new StringBuilder();
|
||||
trackerProcess = new Process();
|
||||
trackerProcess.StartInfo = processStartInfo;
|
||||
dontPrintNow = dontPrint;
|
||||
if (!dontPrintNow) {
|
||||
trackerProcess.EnableRaisingEvents = true;
|
||||
trackerProcess.OutputDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e) {
|
||||
trackerSB.Append(e.Data);
|
||||
trackerSB.Append("\n");
|
||||
});
|
||||
trackerProcess.ErrorDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e) {
|
||||
trackerSB.Append(e.Data);
|
||||
trackerSB.Append("\n");
|
||||
});
|
||||
}
|
||||
trackerProcess.Start();
|
||||
job.AddProcess(trackerProcess.Handle);
|
||||
if (!dontPrintNow) {
|
||||
trackerProcess.BeginOutputReadLine();
|
||||
trackerProcess.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
return !trackerProcess.HasExited;
|
||||
} else {
|
||||
string dir = Path.GetDirectoryName(exePath);
|
||||
string outputLog = Application.persistentDataPath + "/" + pinvokeStdOut;
|
||||
string errorLog = Application.persistentDataPath + "/" + pinvokeStdErr;
|
||||
OpenSeeProcessInterface.Start(exePath, "facetracker " + argumentString, dir, true, outputLog, errorLog, out processHandle, out processStdOut, out processStdErr);
|
||||
if (processHandle != System.IntPtr.Zero) {
|
||||
job.AddProcess(processHandle);
|
||||
return OpenSeeProcessInterface.Alive(processHandle);
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopTracker() {
|
||||
if (processHandle != System.IntPtr.Zero) {
|
||||
if (OpenSeeProcessInterface.Alive(processHandle))
|
||||
OpenSeeProcessInterface.TerminateProcess(processHandle, 0);
|
||||
OpenSeeProcessInterface.CloseHandle(processHandle);
|
||||
processHandle = System.IntPtr.Zero;
|
||||
}
|
||||
if (processStdOut != System.IntPtr.Zero) {
|
||||
OpenSeeProcessInterface.CloseHandle(processStdOut);
|
||||
processStdOut = System.IntPtr.Zero;
|
||||
}
|
||||
if (processStdErr != System.IntPtr.Zero) {
|
||||
OpenSeeProcessInterface.CloseHandle(processStdErr);
|
||||
processStdErr = System.IntPtr.Zero;
|
||||
}
|
||||
if (trackerProcess != null && !trackerProcess.HasExited) {
|
||||
trackerProcess.CloseMainWindow();
|
||||
trackerProcess.Close();
|
||||
if (!dontPrintNow) {
|
||||
try {
|
||||
trackerProcess.CancelOutputRead();
|
||||
trackerProcess.CancelErrorRead();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
trackerProcess = null;
|
||||
}
|
||||
|
||||
public void Start() {
|
||||
if (autoStart)
|
||||
StartTracker();
|
||||
if (autoListCameras) {
|
||||
string[] cameras = ListCameras();
|
||||
if (cameras == null || cameras.Length < 1) {
|
||||
UnityEngine.Debug.Log("No cameras found.");
|
||||
return;
|
||||
}
|
||||
UnityEngine.Debug.Log("Cameras:");
|
||||
for (int i = 0; i < cameras.Length; i++) {
|
||||
UnityEngine.Debug.Log(i + " = " + cameras[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
if (processHandle != System.IntPtr.Zero) {
|
||||
trackerAlive = OpenSeeProcessInterface.Alive(processHandle);
|
||||
return;
|
||||
} else {
|
||||
if (trackerProcess != null)
|
||||
trackerAlive = !trackerProcess.HasExited;
|
||||
else {
|
||||
trackerAlive = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dontPrintNow || trackerSB == null)
|
||||
return;
|
||||
int len = trackerSB.Length;
|
||||
if (len > 0) {
|
||||
string output = trackerSB.ToString();
|
||||
UnityEngine.Debug.Log("Tracker: " + output);
|
||||
trackerSB.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanJob() {
|
||||
if (job != null) {
|
||||
job.Dispose();
|
||||
job = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDestroy() {
|
||||
StopTracker();
|
||||
CleanJob();
|
||||
}
|
||||
|
||||
public void OnApplicationQuit() {
|
||||
StopTracker();
|
||||
CleanJob();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: beae834477f91834691fb7248fddacf6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,364 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
// Based on https://github.com/josh4364/IL2cppStartProcess/blob/master/StartExternalProcess.cs
|
||||
|
||||
public class OpenSeeProcessInterface {
|
||||
#region DllImport
|
||||
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
|
||||
private static extern IntPtr CreateFile(
|
||||
string lpFileName,
|
||||
EFileAccess dwDesiredAccess,
|
||||
EFileShare dwShareMode,
|
||||
SECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
ECreationDisposition dwCreationDisposition,
|
||||
EFileAttributes dwFlagsAndAttributes,
|
||||
IntPtr hTemplateFile
|
||||
);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct SECURITY_ATTRIBUTES
|
||||
{
|
||||
public int nLength;
|
||||
public IntPtr lpSecurityDescriptor;
|
||||
public int bInheritHandle;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum EFileAccess : uint {
|
||||
//
|
||||
// Standart Section
|
||||
//
|
||||
|
||||
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
|
||||
MaximumAllowed = 0x2000000, // MaximumAllowed access type
|
||||
|
||||
Delete = 0x10000,
|
||||
ReadControl = 0x20000,
|
||||
WriteDAC = 0x40000,
|
||||
WriteOwner = 0x80000,
|
||||
Synchronize = 0x100000,
|
||||
|
||||
StandardRightsRequired = 0xF0000,
|
||||
StandardRightsRead = ReadControl,
|
||||
StandardRightsWrite = ReadControl,
|
||||
StandardRightsExecute = ReadControl,
|
||||
StandardRightsAll = 0x1F0000,
|
||||
SpecificRightsAll = 0xFFFF,
|
||||
|
||||
FILE_READ_DATA = 0x0001, // file & pipe
|
||||
FILE_LIST_DIRECTORY = 0x0001, // directory
|
||||
FILE_WRITE_DATA = 0x0002, // file & pipe
|
||||
FILE_ADD_FILE = 0x0002, // directory
|
||||
FILE_APPEND_DATA = 0x0004, // file
|
||||
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
|
||||
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
|
||||
FILE_READ_EA = 0x0008, // file & directory
|
||||
FILE_WRITE_EA = 0x0010, // file & directory
|
||||
FILE_EXECUTE = 0x0020, // file
|
||||
FILE_TRAVERSE = 0x0020, // directory
|
||||
FILE_DELETE_CHILD = 0x0040, // directory
|
||||
FILE_READ_ATTRIBUTES = 0x0080, // all
|
||||
FILE_WRITE_ATTRIBUTES = 0x0100, // all
|
||||
|
||||
//
|
||||
// Generic Section
|
||||
//
|
||||
|
||||
GenericRead = 0x80000000,
|
||||
GenericWrite = 0x40000000,
|
||||
GenericExecute = 0x20000000,
|
||||
GenericAll = 0x10000000,
|
||||
|
||||
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
|
||||
FILE_ALL_ACCESS =
|
||||
StandardRightsRequired |
|
||||
Synchronize |
|
||||
0x1FF,
|
||||
|
||||
FILE_GENERIC_READ =
|
||||
StandardRightsRead |
|
||||
FILE_READ_DATA |
|
||||
FILE_READ_ATTRIBUTES |
|
||||
FILE_READ_EA |
|
||||
Synchronize,
|
||||
|
||||
FILE_GENERIC_WRITE =
|
||||
StandardRightsWrite |
|
||||
FILE_WRITE_DATA |
|
||||
FILE_WRITE_ATTRIBUTES |
|
||||
FILE_WRITE_EA |
|
||||
FILE_APPEND_DATA |
|
||||
Synchronize,
|
||||
|
||||
FILE_GENERIC_EXECUTE =
|
||||
StandardRightsExecute |
|
||||
FILE_READ_ATTRIBUTES |
|
||||
FILE_EXECUTE |
|
||||
Synchronize
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum EFileShare : uint
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
None = 0x00000000,
|
||||
/// <summary>
|
||||
/// Enables subsequent open operations on an object to request read access.
|
||||
/// Otherwise, other processes cannot open the object if they request read access.
|
||||
/// If this flag is not specified, but the object has been opened for read access, the function fails.
|
||||
/// </summary>
|
||||
Read = 0x00000001,
|
||||
/// <summary>
|
||||
/// Enables subsequent open operations on an object to request write access.
|
||||
/// Otherwise, other processes cannot open the object if they request write access.
|
||||
/// If this flag is not specified, but the object has been opened for write access, the function fails.
|
||||
/// </summary>
|
||||
Write = 0x00000002,
|
||||
/// <summary>
|
||||
/// Enables subsequent open operations on an object to request delete access.
|
||||
/// Otherwise, other processes cannot open the object if they request delete access.
|
||||
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
|
||||
/// </summary>
|
||||
Delete = 0x00000004
|
||||
}
|
||||
|
||||
public enum ECreationDisposition : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new file. The function fails if a specified file exists.
|
||||
/// </summary>
|
||||
New = 1,
|
||||
/// <summary>
|
||||
/// Creates a new file, always.
|
||||
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
|
||||
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
|
||||
/// </summary>
|
||||
CreateAlways = 2,
|
||||
/// <summary>
|
||||
/// Opens a file. The function fails if the file does not exist.
|
||||
/// </summary>
|
||||
OpenExisting = 3,
|
||||
/// <summary>
|
||||
/// Opens a file, always.
|
||||
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
|
||||
/// </summary>
|
||||
OpenAlways = 4,
|
||||
/// <summary>
|
||||
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
|
||||
/// The calling process must open the file with the GENERIC_WRITE access right.
|
||||
/// </summary>
|
||||
TruncateExisting = 5
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum EFileAttributes : uint
|
||||
{
|
||||
Readonly = 0x00000001,
|
||||
Hidden = 0x00000002,
|
||||
System = 0x00000004,
|
||||
Directory = 0x00000010,
|
||||
Archive = 0x00000020,
|
||||
Device = 0x00000040,
|
||||
Normal = 0x00000080,
|
||||
Temporary = 0x00000100,
|
||||
SparseFile = 0x00000200,
|
||||
ReparsePoint = 0x00000400,
|
||||
Compressed = 0x00000800,
|
||||
Offline = 0x00001000,
|
||||
NotContentIndexed= 0x00002000,
|
||||
Encrypted = 0x00004000,
|
||||
Write_Through = 0x80000000,
|
||||
Overlapped = 0x40000000,
|
||||
NoBuffering = 0x20000000,
|
||||
RandomAccess = 0x10000000,
|
||||
SequentialScan = 0x08000000,
|
||||
DeleteOnClose = 0x04000000,
|
||||
BackupSemantics = 0x02000000,
|
||||
PosixSemantics = 0x01000000,
|
||||
OpenReparsePoint = 0x00200000,
|
||||
OpenNoRecall = 0x00100000,
|
||||
FirstPipeInstance= 0x00080000
|
||||
}
|
||||
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
|
||||
const UInt32 INFINITE = 0xFFFFFFFF;
|
||||
const UInt32 WAIT_ABANDONED = 0x00000080;
|
||||
const UInt32 WAIT_OBJECT_0 = 0x00000000;
|
||||
const UInt32 WAIT_TIMEOUT = 0x00000102;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CreateProcessW(
|
||||
string lpApplicationName,
|
||||
[In] string lpCommandLine,
|
||||
IntPtr procSecAttrs,
|
||||
IntPtr threadSecAttrs,
|
||||
bool bInheritHandles,
|
||||
ProcessCreationFlags dwCreationFlags,
|
||||
IntPtr lpEnvironment,
|
||||
string lpCurrentDirectory,
|
||||
ref STARTUPINFO lpStartupInfo,
|
||||
ref PROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool TerminateProcess(IntPtr processHandle, uint exitCode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr OpenProcess(ProcessAccessRights access, bool inherit, uint processId);
|
||||
|
||||
[Flags]
|
||||
private enum ProcessAccessRights : uint
|
||||
{
|
||||
PROCESS_CREATE_PROCESS = 0x0080, // Required to create a process.
|
||||
PROCESS_CREATE_THREAD = 0x0002, // Required to create a thread.
|
||||
PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
|
||||
PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
|
||||
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000: This access right is not supported.
|
||||
PROCESS_SET_INFORMATION = 0x0200, // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
|
||||
PROCESS_SET_QUOTA = 0x0100, // Required to set memory limits using SetProcessWorkingSetSize.
|
||||
PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
|
||||
PROCESS_TERMINATE = 0x0001, // Required to terminate a process using TerminateProcess.
|
||||
PROCESS_VM_OPERATION = 0x0008, // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
|
||||
PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
|
||||
PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
|
||||
DELETE = 0x00010000, // Required to delete the object.
|
||||
READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
|
||||
SYNCHRONIZE = 0x00100000, // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
|
||||
WRITE_DAC = 0x00040000, // Required to modify the DACL in the security descriptor for the object.
|
||||
WRITE_OWNER = 0x00080000, // Required to change the owner in the security descriptor for the object.
|
||||
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
|
||||
PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF // All possible access rights for a process object.
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct PROCESS_INFORMATION
|
||||
{
|
||||
internal IntPtr hProcess;
|
||||
internal IntPtr hThread;
|
||||
internal uint dwProcessId;
|
||||
internal uint dwThreadId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct STARTUPINFO
|
||||
{
|
||||
internal uint cb;
|
||||
internal IntPtr lpReserved;
|
||||
internal IntPtr lpDesktop;
|
||||
internal IntPtr lpTitle;
|
||||
internal uint dwX;
|
||||
internal uint dwY;
|
||||
internal uint dwXSize;
|
||||
internal uint dwYSize;
|
||||
internal uint dwXCountChars;
|
||||
internal uint dwYCountChars;
|
||||
internal uint dwFillAttribute;
|
||||
internal uint dwFlags;
|
||||
internal ushort wShowWindow;
|
||||
internal ushort cbReserved2;
|
||||
internal IntPtr lpReserved2;
|
||||
internal IntPtr hStdInput;
|
||||
internal IntPtr hStdOutput;
|
||||
internal IntPtr hStdError;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum ProcessCreationFlags : uint
|
||||
{
|
||||
NONE = 0,
|
||||
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
|
||||
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
|
||||
CREATE_NEW_CONSOLE = 0x00000010,
|
||||
CREATE_NEW_PROCESS_GROUP = 0x00000200,
|
||||
CREATE_NO_WINDOW = 0x08000000,
|
||||
CREATE_PROTECTED_PROCESS = 0x00040000,
|
||||
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
|
||||
CREATE_SECURE_PROCESS = 0x00400000,
|
||||
CREATE_SEPARATE_WOW_VDM = 0x00000800,
|
||||
CREATE_SHARED_WOW_VDM = 0x00001000,
|
||||
CREATE_SUSPENDED = 0x00000004,
|
||||
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
|
||||
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
|
||||
DEBUG_PROCESS = 0x00000001,
|
||||
DETACHED_PROCESS = 0x00000008,
|
||||
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
|
||||
INHERIT_PARENT_AFFINITY = 0x00010000
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static uint Start(string exePath, string commandline, string dir, bool hidden, string outputLog, string errorLog, out IntPtr handle, out IntPtr handleStdOut, out IntPtr handleStdErr)
|
||||
{
|
||||
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
|
||||
sa.nLength = Marshal.SizeOf<SECURITY_ATTRIBUTES>();
|
||||
sa.lpSecurityDescriptor = IntPtr.Zero;
|
||||
sa.bInheritHandle = 1;
|
||||
|
||||
handle = IntPtr.Zero;
|
||||
handleStdOut = IntPtr.Zero;
|
||||
handleStdErr = IntPtr.Zero;
|
||||
if (outputLog != null)
|
||||
handleStdOut = CreateFile(outputLog, EFileAccess.FILE_WRITE_DATA, EFileShare.Read | EFileShare.Write, sa, ECreationDisposition.CreateAlways, EFileAttributes.Normal, IntPtr.Zero);
|
||||
if (errorLog != null)
|
||||
handleStdErr = CreateFile(errorLog, EFileAccess.FILE_WRITE_DATA, EFileShare.Read | EFileShare.Write, sa, ECreationDisposition.CreateAlways, EFileAttributes.Normal, IntPtr.Zero);
|
||||
|
||||
ProcessCreationFlags flags = hidden ? ProcessCreationFlags.CREATE_NO_WINDOW : ProcessCreationFlags.NONE;
|
||||
STARTUPINFO startupinfo = new STARTUPINFO
|
||||
{
|
||||
cb = (uint)Marshal.SizeOf<STARTUPINFO>(),
|
||||
dwFlags = 0x00000100,
|
||||
hStdInput = IntPtr.Zero,
|
||||
hStdOutput = handleStdOut,
|
||||
hStdError = handleStdErr
|
||||
};
|
||||
PROCESS_INFORMATION processinfo = new PROCESS_INFORMATION();
|
||||
if (!CreateProcessW(exePath, commandline, IntPtr.Zero, IntPtr.Zero, true, flags, IntPtr.Zero, dir, ref startupinfo, ref processinfo))
|
||||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
handle = OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, processinfo.dwProcessId);
|
||||
|
||||
return processinfo.dwProcessId;
|
||||
}
|
||||
|
||||
public static bool Alive(IntPtr handle) {
|
||||
uint ret = WaitForSingleObject(handle, 0);
|
||||
return ret == WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
public static int KillProcess(uint pid)
|
||||
{
|
||||
IntPtr handle = OpenProcess(ProcessAccessRights.PROCESS_ALL_ACCESS, false, pid);
|
||||
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!TerminateProcess(handle, 0))
|
||||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
if (!CloseHandle(handle))
|
||||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8130804d2fedd6d47a6cf0a447058d08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class OpenSeeShowPoints : MonoBehaviour {
|
||||
public OpenSee openSee = null;
|
||||
public int faceId = 0;
|
||||
public bool only30Points = false;
|
||||
public bool show3DPoints = true;
|
||||
public bool applyTranslation = false;
|
||||
public bool applyRotation = false;
|
||||
[Range(0, 1)]
|
||||
public float minConfidence = 0.20f;
|
||||
public bool showGaze = true;
|
||||
public Material material;
|
||||
public bool showLines = false;
|
||||
public float lineWidth = 0.01f;
|
||||
public Material lineMaterial;
|
||||
public bool receiveShadows = false;
|
||||
|
||||
private OpenSee.OpenSeeData openSeeData;
|
||||
private GameObject[] gameObjects;
|
||||
private LineRenderer[] lineRenderers;
|
||||
private GameObject centerBall;
|
||||
private double updated = 0.0;
|
||||
private int total = 70;
|
||||
|
||||
private int[] lines = new int[]{/* Contour */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, /* Eyebrows */ 18, 19, 20, 21, -1, 23, 24, 25, 26, -1, /* Nose */ 28, 29, 30, 33, 32, 33, 34, 35, -1, /* Eye */ 37, 38, 39, 40, 41, 36, /* Eye */ 43, 44, 45, 46, 47, 42, /* Mouth */ 49, 50, 51, 52, 62, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 58, 58, 62};
|
||||
|
||||
private HashSet<int> pt30Set = new HashSet<int> { 0, 2, 5, 8, 11, 14, 16, 17, 19, 21, 22, 24, 26, 27, 30, 33, 36, 37, 39, 40, 42, 43, 45, 46, 50, 55, 58, 60, 62, 64 };
|
||||
private int[] linesPt30Set = new int[]{/* Contour */ 2, -1, 5, -1, -1, 8, -1, -1, 11, -1, -1, 14, -1, -1, 16, -1, -1, /* Eyebrows */ 19, -1, 21, -1, -1, 24, -1, 26, -1, -1, /* Nose */ 30, -1, -1, 33, -1, -1, -1, -1, -1, /* Eye */ 37, 39, -1, 40, 36, -1, /* Eye */ 43, 45, -1, 46, 42, -1, /* Mouth */ -1, -1, 62, -1, -1, -1, -1, 58, -1, -1, 60, -1, 62, -1, 64, -1, 58, -1, 58, 62};
|
||||
|
||||
void Start () {
|
||||
if (openSee == null) {
|
||||
openSee = GetComponent<OpenSee>();
|
||||
}
|
||||
gameObjects = new GameObject[70];
|
||||
lineRenderers = new LineRenderer[68];
|
||||
if (lineMaterial == null)
|
||||
showLines = false;
|
||||
if (!showGaze)
|
||||
total = 66;
|
||||
for (int i = 0; i < total; i++) {
|
||||
gameObjects[i] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
gameObjects[i].name = "Point " + (i + 1);
|
||||
if (material != null)
|
||||
gameObjects[i].GetComponent<Renderer>().material = material;
|
||||
gameObjects[i].GetComponent<Renderer>().receiveShadows = receiveShadows;
|
||||
gameObjects[i].layer = gameObject.layer;
|
||||
gameObjects[i].transform.SetParent(transform);
|
||||
gameObjects[i].transform.localScale = new Vector3(0.025f, 0.025f, 0.025f);
|
||||
if (i >= 68) {
|
||||
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||
if (material != null)
|
||||
cylinder.GetComponent<Renderer>().material = material;
|
||||
cylinder.GetComponent<Renderer>().receiveShadows = receiveShadows;
|
||||
cylinder.layer = gameObject.layer;
|
||||
cylinder.transform.SetParent(gameObjects[i].transform);
|
||||
cylinder.transform.localEulerAngles = new Vector3(90f, 0f, 0f);
|
||||
cylinder.transform.localPosition = new Vector3(0f, 0f, -4f);
|
||||
cylinder.transform.localScale = new Vector3(1f, 4f, 1f);
|
||||
}
|
||||
if (only30Points && i < 66 && !pt30Set.Contains(i))
|
||||
gameObjects[i].SetActive(false);
|
||||
}
|
||||
for (int i = 0; i < 68; i++) {
|
||||
if (i == 66) {
|
||||
GameObject lineGameObject = new GameObject("LineGameObject");
|
||||
if (only30Points)
|
||||
lineGameObject.transform.SetParent(gameObjects[50].transform);
|
||||
else
|
||||
lineGameObject.transform.SetParent(gameObjects[48].transform);
|
||||
lineGameObject.layer = gameObject.layer;
|
||||
lineRenderers[i] = lineGameObject.AddComponent(typeof(LineRenderer)) as LineRenderer;
|
||||
} else if (i == 67) {
|
||||
GameObject lineGameObject = new GameObject("LineGameObject");
|
||||
if (only30Points)
|
||||
lineGameObject.transform.SetParent(gameObjects[55].transform);
|
||||
else
|
||||
lineGameObject.transform.SetParent(gameObjects[53].transform);
|
||||
lineGameObject.layer = gameObject.layer;
|
||||
lineRenderers[i] = lineGameObject.AddComponent(typeof(LineRenderer)) as LineRenderer;
|
||||
}
|
||||
else
|
||||
lineRenderers[i] = gameObjects[i].AddComponent(typeof(LineRenderer)) as LineRenderer;
|
||||
lineRenderers[i].useWorldSpace = true;
|
||||
lineRenderers[i].generateLightingData = true;
|
||||
lineRenderers[i].material = lineMaterial;
|
||||
lineRenderers[i].receiveShadows = receiveShadows;
|
||||
lineRenderers[i].widthMultiplier = lineWidth;
|
||||
lineRenderers[i].positionCount = 2;
|
||||
}
|
||||
centerBall = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
centerBall.name = "Center";
|
||||
centerBall.transform.SetParent(transform);
|
||||
centerBall.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (!openSee)
|
||||
return;
|
||||
/*openSeeData = openSee.trackingData;
|
||||
if (openSeeData == null || openSeeData.Length < 1)
|
||||
return;*/
|
||||
openSeeData = openSee.GetOpenSeeData(faceId);
|
||||
if (openSeeData == null || (show3DPoints && openSeeData.fit3DError > openSee.maxFit3DError))
|
||||
return;
|
||||
if (openSeeData.time > updated) {
|
||||
updated = openSeeData.time;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (show3DPoints) {
|
||||
centerBall.gameObject.SetActive(false);
|
||||
for (int i = 0; i < total; i++) {
|
||||
if (openSeeData.got3DPoints && (i >= 68 || openSeeData.confidence[i] > minConfidence)) {
|
||||
Renderer renderer = gameObjects[i].GetComponent<Renderer>();
|
||||
Vector3 pt = openSeeData.points3D[i];
|
||||
pt.x = -pt.x;
|
||||
gameObjects[i].transform.localPosition = pt;
|
||||
if (i < 68)
|
||||
renderer.material.SetColor("_Color", Color.Lerp(Color.red, Color.green, openSeeData.confidence[i]));
|
||||
else {
|
||||
if (i == 68)
|
||||
gameObjects[i].transform.localRotation = openSeeData.rightGaze;
|
||||
else
|
||||
gameObjects[i].transform.localRotation = openSeeData.leftGaze;
|
||||
}
|
||||
} else {
|
||||
Renderer renderer = gameObjects[i].GetComponent<Renderer>();
|
||||
renderer.material.SetColor("_Color", Color.cyan);
|
||||
}
|
||||
}
|
||||
if (applyTranslation) {
|
||||
Vector3 v = openSeeData.translation;
|
||||
v.x = -v.x;
|
||||
v.z = -v.z;
|
||||
transform.localPosition = v;
|
||||
}
|
||||
if (applyRotation) {
|
||||
Quaternion offset = Quaternion.Euler(0f, 0f, -90f);
|
||||
Quaternion convertedQuaternion = new Quaternion(-openSeeData.rawQuaternion.y, -openSeeData.rawQuaternion.x, openSeeData.rawQuaternion.z, openSeeData.rawQuaternion.w) * offset;
|
||||
transform.localRotation = convertedQuaternion;
|
||||
}
|
||||
} else {
|
||||
centerBall.gameObject.SetActive(false);
|
||||
Vector3 center = new Vector3(0.0f, 0.0f, 0.0f);
|
||||
float minX = 10000.0f;
|
||||
float minY = 10000.0f;
|
||||
float maxX = -1.0f;
|
||||
float maxY = -1.0f;
|
||||
for (int i = 0; i < 66; i++) {
|
||||
float x = openSeeData.points[i].x;
|
||||
float y = -openSeeData.points[i].y;
|
||||
if (minX > x) {
|
||||
minX = x;
|
||||
}
|
||||
if (minY > y) {
|
||||
minY = y;
|
||||
}
|
||||
if (maxX < x) {
|
||||
maxX = x;
|
||||
}
|
||||
if (maxY < y) {
|
||||
maxY = y;
|
||||
}
|
||||
center += new Vector3(x, y, 0.0f);
|
||||
}
|
||||
center = center / 66;
|
||||
center = center - new Vector3(minX, minY, 0.0f);
|
||||
center.x = center.x / (maxX - minX);
|
||||
center.y = center.y / (maxX - minX);
|
||||
center.z = 0.5f;
|
||||
centerBall.transform.localPosition = center;
|
||||
|
||||
for (int i = 0; i < 66; i++) {
|
||||
Renderer renderer = gameObjects[i].GetComponent<Renderer>();
|
||||
renderer.receiveShadows = receiveShadows;
|
||||
renderer.material.SetColor("_Color", Color.Lerp(Color.red, Color.green, openSeeData.confidence[i]));
|
||||
float x = openSeeData.points[i].x;
|
||||
float y = -openSeeData.points[i].y;
|
||||
Vector3 position = new Vector3(x, y, 0.0f);
|
||||
position = position - new Vector3(minX, minY, 0.0f);
|
||||
position.x = position.x / (maxX - minX);
|
||||
position.y = position.y / (maxX - minX);
|
||||
gameObjects[i].transform.localPosition = position;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 68; i++) {
|
||||
if ((!only30Points && lines[i] == -1) || (only30Points && linesPt30Set[i] == -1))
|
||||
continue;
|
||||
if (!showLines || lineMaterial == null) {
|
||||
lineRenderers[i].enabled = false;
|
||||
} else {
|
||||
int a = i;
|
||||
int b = lines[i];
|
||||
if (only30Points) {
|
||||
b = linesPt30Set[i];
|
||||
if (i == 66)
|
||||
a = 50;
|
||||
if (i == 67)
|
||||
a = 55;
|
||||
} else {
|
||||
if (i == 66)
|
||||
a = 48;
|
||||
if (i == 67)
|
||||
a = 53;
|
||||
}
|
||||
Color color = Color.Lerp(Color.red, Color.green, Mathf.Lerp(0.5f, openSeeData.confidence[a], openSeeData.confidence[b]));
|
||||
lineRenderers[i].enabled = true;
|
||||
lineRenderers[i].widthMultiplier = lineWidth;
|
||||
lineRenderers[i].receiveShadows = receiveShadows;
|
||||
lineRenderers[i].material.SetColor("_Color", color);
|
||||
lineRenderers[i].startColor = color;
|
||||
lineRenderers[i].endColor = color;
|
||||
lineRenderers[i].SetPosition(0, gameObjects[a].transform.position);
|
||||
lineRenderers[i].SetPosition(1, gameObjects[b].transform.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c0b8bb971ed0e14288631f4274a3807
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f1dfc5f1c2e6f294e8191828595882b1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OpenSee {
|
||||
|
||||
public class SVMModel {
|
||||
#region DllImport
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, EntryPoint = "trainModel")]
|
||||
private static extern System.IntPtr trainModelDll(float[] features, float[] labels, float[] weights, int rows, int cols, int classes, int probability, float C);
|
||||
protected virtual System.IntPtr trainModel(float[] features, float[] labels, float[] weights, int rows, int cols, int classes, int probability, float C) {
|
||||
Debug.Log("Training LibSVM");
|
||||
return trainModelDll(features, labels, weights, rows, cols, classes, probability, C);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, EntryPoint = "predict")]
|
||||
private static extern void predictDll(System.IntPtr model, float[] features, [Out] float[] predictions, [Out] double[] probabilities, int rows);
|
||||
protected virtual void predict(System.IntPtr model, float[] features, [Out] float[] predictions, [Out] double[] probabilities, int rows) {
|
||||
predictDll(model, features, predictions, probabilities, rows);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "loadModel")]
|
||||
private static extern System.IntPtr loadModelDll([MarshalAs(UnmanagedType.LPStr)]string filename, int cols, int classes, double[] means, double[] sdevs);
|
||||
protected virtual System.IntPtr loadModel([MarshalAs(UnmanagedType.LPStr)]string filename, int cols, int classes, double[] means, double[] sdevs) {
|
||||
return loadModelDll(filename, cols, classes, means, sdevs);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "loadModelString")]
|
||||
private static extern System.IntPtr loadModelStringDll([MarshalAs(UnmanagedType.LPStr)]string modelString, int cols, int classes, double[] means, double[] sdevs);
|
||||
protected virtual System.IntPtr loadModelString([MarshalAs(UnmanagedType.LPStr)]string modelString, int cols, int classes, double[] means, double[] sdevs) {
|
||||
Debug.Log("Loading LibSVM");
|
||||
return loadModelStringDll(modelString, cols, classes, means, sdevs);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, EntryPoint = "getScales")]
|
||||
private static extern void getScalesDll(System.IntPtr model, [Out] double[] means, [Out] double[] sdevs);
|
||||
protected virtual void getScales(System.IntPtr model, [Out] double[] means, [Out] double[] sdevs) {
|
||||
getScalesDll(model, means, sdevs);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "saveModel")]
|
||||
private static extern int saveModelDll(System.IntPtr model, [MarshalAs(UnmanagedType.LPStr)]string filename);
|
||||
protected virtual int saveModel(System.IntPtr model, [MarshalAs(UnmanagedType.LPStr)]string filename) {
|
||||
return saveModelDll(model, filename);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "saveModelString")]
|
||||
private static extern string saveModelStringDll(System.IntPtr model);
|
||||
protected virtual string saveModelString(System.IntPtr model) {
|
||||
return saveModelStringDll(model);
|
||||
}
|
||||
|
||||
[DllImport("SVMModel", CallingConvention = CallingConvention.Cdecl, EntryPoint = "destroyModel")]
|
||||
private static extern void destroyModelDll(System.IntPtr model);
|
||||
protected virtual void destroyModel(System.IntPtr model) {
|
||||
destroyModelDll(model);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool haveModel = false;
|
||||
private System.IntPtr model;
|
||||
private int cols;
|
||||
private int maxClasses;
|
||||
|
||||
[Serializable]
|
||||
private class SVMModelRepresentation {
|
||||
public string modelString;
|
||||
public int cols;
|
||||
public int maxClasses;
|
||||
public double[] means;
|
||||
public double[] sdevs;
|
||||
}
|
||||
|
||||
public System.IntPtr LoadSerialized(byte[] modelBytes, out int cols, out int maxClasses, bool compress) {
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
MemoryStream memoryStream = new MemoryStream(modelBytes);
|
||||
memoryStream.Position = 0;
|
||||
SVMModelRepresentation smr;
|
||||
if (compress) {
|
||||
using (GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) {
|
||||
smr = formatter.Deserialize(gzipStream) as SVMModelRepresentation;
|
||||
}
|
||||
} else {
|
||||
smr = formatter.Deserialize(memoryStream) as SVMModelRepresentation;
|
||||
}
|
||||
cols = smr.cols;
|
||||
maxClasses = smr.maxClasses;
|
||||
System.IntPtr model = loadModelString(smr.modelString, smr.cols, smr.maxClasses, smr.means, smr.sdevs);
|
||||
return model;
|
||||
}
|
||||
|
||||
public byte[] ToSerialized(System.IntPtr model, int cols, int maxClasses, bool compress) {
|
||||
SVMModelRepresentation smr = new SVMModelRepresentation();
|
||||
smr.cols = cols;
|
||||
smr.maxClasses = maxClasses;
|
||||
smr.means = new double[cols];
|
||||
smr.sdevs = new double[cols];
|
||||
getScales(model, smr.means, smr.sdevs);
|
||||
smr.modelString = saveModelString(model);
|
||||
|
||||
IFormatter formatter = new BinaryFormatter();
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
if (compress) {
|
||||
using (GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress)) {
|
||||
formatter.Serialize(gzipStream, smr);
|
||||
gzipStream.Flush();
|
||||
}
|
||||
} else {
|
||||
formatter.Serialize(memoryStream, smr);
|
||||
}
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
public void DestroyModel() {
|
||||
if (haveModel) {
|
||||
destroyModel(model);
|
||||
haveModel = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadModel(byte[] modelBytes, bool compress) {
|
||||
DestroyModel();
|
||||
model = LoadSerialized(modelBytes, out cols, out maxClasses, compress);
|
||||
haveModel = true;
|
||||
}
|
||||
|
||||
public void LoadModel(byte[] modelBytes) {
|
||||
LoadModel(modelBytes, true);
|
||||
}
|
||||
|
||||
private byte[] SaveModel(bool compress) {
|
||||
if (!haveModel)
|
||||
return null;
|
||||
return ToSerialized(model, cols, maxClasses, compress);
|
||||
}
|
||||
|
||||
public byte[] SaveModel() {
|
||||
return SaveModel(true);
|
||||
}
|
||||
|
||||
// features is a rows*cols long float array with rows written one after another. labels is a rows long float array with the corresponding classes as integral numbers from 0 to num_classes-1, where all classes have to exist. Training on more than 10000 rows is disabled.
|
||||
public bool TrainModel(float[] features, float[] labels, float[] weights, int rows, int cols, int probability, float C) {
|
||||
DestroyModel();
|
||||
//if (rows > 10000)
|
||||
// return false;
|
||||
if (cols < 1 || rows < 1 || features.Length < rows * cols || labels.Length < rows)
|
||||
return false;
|
||||
|
||||
int max = -1;
|
||||
HashSet<int> knownClasses = new HashSet<int>();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
labels[i] = (float)Math.Round(labels[i]);
|
||||
int l = (int)labels[i];
|
||||
if (l > max)
|
||||
max = l;
|
||||
if (l < 0)
|
||||
return false;
|
||||
knownClasses.Add(l);
|
||||
}
|
||||
for (int i = 0; i <= max; i++) {
|
||||
if (!knownClasses.Contains(i))
|
||||
return false;
|
||||
}
|
||||
|
||||
maxClasses = max + 1;
|
||||
this.cols = cols;
|
||||
float start = Time.realtimeSinceStartup;
|
||||
model = trainModel(features, labels, weights, rows, cols, maxClasses, probability, C);
|
||||
Debug.Log("Training took: " + (Time.realtimeSinceStartup - start).ToString());
|
||||
haveModel = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function takes the same arguments as train, except for cols, which is not necessary. The diagonal are correctly classified entries. The first index is labels, the second index is classifications.
|
||||
public int[,] ConfusionMatrix(float[] features, float[] labels, int rows, out float accuracy) {
|
||||
accuracy = 0f;
|
||||
if (!haveModel)
|
||||
return null;
|
||||
if (rows < 1 || features.Length < rows * cols || labels.Length < rows)
|
||||
return null;
|
||||
for (int i = 0; i < rows; i++) {
|
||||
labels[i] = (float)Math.Round(labels[i]);
|
||||
if (labels[i] < 0 || labels[i] >= maxClasses)
|
||||
return null;
|
||||
}
|
||||
int[,] confusionMatrix = new int[maxClasses, maxClasses];
|
||||
float[] predictions = new float[rows];
|
||||
double[] probabilities = new double[rows * maxClasses];
|
||||
predict(model, features, predictions, probabilities, rows);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
predictions[i] = (float)Math.Round(predictions[i]);
|
||||
if (predictions[i] >= 0 && predictions[i] < maxClasses) {
|
||||
confusionMatrix[(int)labels[i], (int)predictions[i]]++;
|
||||
if ((int)labels[i] == (int)predictions[i])
|
||||
accuracy++;
|
||||
}
|
||||
}
|
||||
accuracy /= (float)rows;
|
||||
return confusionMatrix;
|
||||
}
|
||||
|
||||
static public string FormatMatrix(int[,] matrix) {
|
||||
return FormatMatrix(matrix, null);
|
||||
}
|
||||
|
||||
static public string FormatMatrix(int[,] matrix, string[] labels) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int rowLength = matrix.GetLength(0);
|
||||
int colLength = matrix.GetLength(1);
|
||||
int maxLableLen = 5;
|
||||
|
||||
if (labels != null) {
|
||||
if (labels.Length != rowLength || labels.Length != colLength)
|
||||
labels = null;
|
||||
else {
|
||||
foreach (string label in labels)
|
||||
if (label.Length > maxLableLen)
|
||||
maxLableLen = label.Length;
|
||||
sb.Append("pred:".PadLeft(maxLableLen));
|
||||
sb.Append(" ");
|
||||
foreach (string label in labels) {
|
||||
sb.Append(label.PadLeft(maxLableLen));
|
||||
sb.Append(" ");
|
||||
}
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
for (int r = 0; r < rowLength; r++) {
|
||||
if (labels != null) {
|
||||
sb.Append(labels[r].PadLeft(maxLableLen));
|
||||
sb.Append(" ");
|
||||
}
|
||||
for (int c = 0; c < colLength; c++) {
|
||||
string n = matrix[r,c].ToString();
|
||||
sb.Append(n.PadLeft(maxLableLen));
|
||||
sb.Append(" ");
|
||||
}
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// features is a rows*cols long float array with rows written one after another.
|
||||
public float[] Predict(float[] features, out double[] probabilities, int rows) {
|
||||
probabilities = null;
|
||||
if (!haveModel)
|
||||
return null;
|
||||
if (rows < 1 || features.Length != rows * cols)
|
||||
return null;
|
||||
float[] predictions = new float[rows];
|
||||
probabilities = new double[rows * maxClasses];
|
||||
predict(model, features, predictions, probabilities, rows);
|
||||
return predictions;
|
||||
}
|
||||
|
||||
public float[] Predict(float[] features, int rows) {
|
||||
double[] probabilities;
|
||||
return Predict(features, out probabilities, rows);
|
||||
}
|
||||
|
||||
public bool Ready() {
|
||||
return haveModel;
|
||||
}
|
||||
|
||||
public SVMModel() {
|
||||
}
|
||||
|
||||
public SVMModel(byte[] modelBytes) {
|
||||
LoadModel(modelBytes);
|
||||
}
|
||||
|
||||
~SVMModel() {
|
||||
DestroyModel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a95b57bbe200b004dab9b21f0854082e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 930906668f1b67a42b2bb844bd098a26
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
'': Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux: 0
|
||||
Exclude Linux64: 0
|
||||
Exclude LinuxUniversal: 0
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude WebGL: 0
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Facebook: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Facebook: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Linux
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: LinuxUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
WebGL: WebGL
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using OpenSee;
|
||||
using UnityEngine;
|
||||
|
||||
public class ThunderSVMModel : SVMModel {
|
||||
#region DllImport
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, EntryPoint = "trainModel")]
|
||||
private static extern System.IntPtr trainModelDll(float[] features, float[] labels, float[] weights, int rows, int cols, int classes, int probability, float C);
|
||||
protected override System.IntPtr trainModel(float[] features, float[] labels, float[] weights, int rows, int cols, int classes, int probability, float C) {
|
||||
Debug.Log("Training ThunderSVM");
|
||||
return trainModelDll(features, labels, weights, rows, cols, classes, probability, C);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, EntryPoint = "predict")]
|
||||
private static extern void predictDll(System.IntPtr model, float[] features, [Out] float[] predictions, [Out] double[] probabilities, int rows);
|
||||
protected override void predict(System.IntPtr model, float[] features, [Out] float[] predictions, [Out] double[] probabilities, int rows) {
|
||||
predictDll(model, features, predictions, probabilities, rows);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "loadModel")]
|
||||
private static extern System.IntPtr loadModelDll([MarshalAs(UnmanagedType.LPStr)]string filename, int cols, int classes, double[] means, double[] sdevs);
|
||||
protected override System.IntPtr loadModel([MarshalAs(UnmanagedType.LPStr)]string filename, int cols, int classes, double[] means, double[] sdevs) {
|
||||
return loadModelDll(filename, cols, classes, means, sdevs);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "loadModelString")]
|
||||
private static extern System.IntPtr loadModelStringDll([MarshalAs(UnmanagedType.LPStr)]string modelString, int cols, int classes, double[] means, double[] sdevs);
|
||||
protected override System.IntPtr loadModelString([MarshalAs(UnmanagedType.LPStr)]string modelString, int cols, int classes, double[] means, double[] sdevs) {
|
||||
Debug.Log("Loading ThunderSVM");
|
||||
return loadModelStringDll(modelString, cols, classes, means, sdevs);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, EntryPoint = "getScales")]
|
||||
private static extern void getScalesDll(System.IntPtr model, [Out] double[] means, [Out] double[] sdevs);
|
||||
protected override void getScales(System.IntPtr model, [Out] double[] means, [Out] double[] sdevs) {
|
||||
getScalesDll(model, means, sdevs);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "saveModel")]
|
||||
private static extern int saveModelDll(System.IntPtr model, [MarshalAs(UnmanagedType.LPStr)]string filename);
|
||||
protected override int saveModel(System.IntPtr model, [MarshalAs(UnmanagedType.LPStr)]string filename) {
|
||||
return saveModelDll(model, filename);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "saveModelString")]
|
||||
private static extern string saveModelStringDll(System.IntPtr model);
|
||||
protected override string saveModelString(System.IntPtr model) {
|
||||
return saveModelStringDll(model);
|
||||
}
|
||||
|
||||
[DllImport("ThunderSVMWrapper", CallingConvention = CallingConvention.Cdecl, EntryPoint = "destroyModel")]
|
||||
private static extern void destroyModelDll(System.IntPtr model);
|
||||
protected override void destroyModel(System.IntPtr model) {
|
||||
destroyModelDll(model);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public ThunderSVMModel() {
|
||||
}
|
||||
|
||||
public ThunderSVMModel(byte[] modelBytes) {
|
||||
LoadModel(modelBytes);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c58dd9d1f0cf3140bd004d536712abb
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 0
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dd0c04eb3445c3048bfaa964c6bcb16f
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a504a0f747083b54c99d6e91731ccecc
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dd0c04eb3445c3048bfaa964c6bcb16f
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a504a0f747083b54c99d6e91731ccecc
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 51dc8f0c65df30d4daa188cdfd1dd220
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fcfc753ca92169e48aa69c1ecc1b0d64
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e549e6cbc6a547748abc7f1b05bfa7b4
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 0
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: AnyOS
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
make_exe.bat
|
|
@ -1,31 +0,0 @@
|
|||
echo "Started"
|
||||
|
||||
:: A venv is used so it's easy to add the onnxruntime dll to the binary
|
||||
|
||||
echo "Creating venv"
|
||||
python -m venv venv
|
||||
|
||||
echo "Activating venv"
|
||||
call venv\Scripts\activate.bat
|
||||
|
||||
echo "Installing dependencies"
|
||||
pip install wheel
|
||||
|
||||
pip install onnxruntime opencv-python==4.5.4.60 pillow numpy==1.23.0 pyinstaller
|
||||
|
||||
echo "Running pyinstaller"
|
||||
pyinstaller facetracker.py --clean ^
|
||||
--onedir ^
|
||||
--add-binary dshowcapture/*.dll;. ^
|
||||
--add-binary escapi/*.dll;. ^
|
||||
--add-binary venv/lib/site-packages/onnxruntime/capi/*.dll;onnxruntime\capi ^
|
||||
--add-binary msvcp140.dll;. ^
|
||||
--add-binary vcomp140.dll;. ^
|
||||
--add-binary concrt140.dll;. ^
|
||||
--add-binary vccorlib140.dll;. ^
|
||||
--add-binary run.bat;.
|
||||
|
||||
echo "Deleting opencv dll"
|
||||
del dist\facetracker\cv2\opencv_videoio_*
|
||||
|
||||
echo "Finished"
|
||||
32
make_exe.sh
|
|
@ -1,32 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PYTHON_BINARY=$(which python3)
|
||||
|
||||
if [[ -z "$PYTHON_BINARY" ]]; then
|
||||
echo "python3 not found. Searching for python instead."
|
||||
PYTHON_BINARY=$(which python)
|
||||
if [[ -z "$PYTHON_BINARY" ]]; then
|
||||
echo "Python 3 must be available on your path. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Creating venv"
|
||||
"$PYTHON_BINARY" -m venv venv
|
||||
|
||||
source venv/bin/activate
|
||||
|
||||
echo "Installing packages"
|
||||
pip install wheel # Make sure this is installed beforehand
|
||||
|
||||
pip install onnxruntime opencv-python==4.5.4.60 pillow numpy==1.23.0 pyinstaller
|
||||
|
||||
echo "Creating binary"
|
||||
pyinstaller --onedir --clean facetracker.py \
|
||||
--add-binary venv/lib/python3.*/site-packages/onnxruntime/capi/*.so:onnxruntime/capi
|
||||
|
||||
echo "Files should be available in the dist/ folder"
|
||||
echo "Done!"
|
||||
|
||||
16
run.bat
|
|
@ -1,16 +0,0 @@
|
|||
%ECHO OFF
|
||||
|
||||
facetracker -l 1
|
||||
|
||||
echo Make sure that nothing is accessing your camera before you proceed.
|
||||
|
||||
set /p cameraNum=Select your camera from the list above and enter the corresponding number:
|
||||
|
||||
facetracker -a %cameraNum%
|
||||
|
||||
set /p dcaps=Select your camera mode or -1 for default settings:
|
||||
set /p fps=Select the FPS:
|
||||
|
||||
facetracker -c %cameraNum% -F %fps% -D %dcaps% -v 3 -P 1 --discard-after 0 --scan-every 0 --no-3d-adapt 1 --max-feature-updates 900
|
||||
|
||||
pause
|
||||