init assets and filemgt

This commit is contained in:
2022-03-07 18:33:30 +02:00
parent 62585ef143
commit 813cd0c451
1274 changed files with 346654 additions and 249 deletions

View File

@@ -0,0 +1,131 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using PDollarGestureRecognizer;
public class Demo : MonoBehaviour {
public Transform gestureOnScreenPrefab;
private List<Gesture> trainingSet = new List<Gesture>();
private List<Point> points = new List<Point>();
private int strokeId = -1;
private Vector3 virtualKeyPosition = Vector2.zero;
private Rect drawArea;
private RuntimePlatform platform;
private int vertexCount = 0;
private List<LineRenderer> gestureLinesRenderer = new List<LineRenderer>();
private LineRenderer currentGestureLineRenderer;
//GUI
private string message;
private bool recognized;
private string newGestureName = "";
void Start () {
platform = Application.platform;
drawArea = new Rect(0, 0, Screen.width - Screen.width / 3, Screen.height);
//Load pre-made gestures
TextAsset[] gesturesXml = Resources.LoadAll<TextAsset>("GestureSet/10-stylus-MEDIUM/");
foreach (TextAsset gestureXml in gesturesXml)
trainingSet.Add(GestureIO.ReadGestureFromXML(gestureXml.text));
//Load user custom gestures
string[] filePaths = Directory.GetFiles(Application.persistentDataPath, "*.xml");
foreach (string filePath in filePaths)
trainingSet.Add(GestureIO.ReadGestureFromFile(filePath));
}
void Update () {
if (platform == RuntimePlatform.Android || platform == RuntimePlatform.IPhonePlayer) {
if (Input.touchCount > 0) {
virtualKeyPosition = new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y);
}
} else {
if (Input.GetMouseButton(0)) {
virtualKeyPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y);
}
}
if (drawArea.Contains(virtualKeyPosition)) {
if (Input.GetMouseButtonDown(0)) {
if (recognized) {
recognized = false;
strokeId = -1;
points.Clear();
foreach (LineRenderer lineRenderer in gestureLinesRenderer) {
lineRenderer.SetVertexCount(0);
Destroy(lineRenderer.gameObject);
}
gestureLinesRenderer.Clear();
}
++strokeId;
Transform tmpGesture = Instantiate(gestureOnScreenPrefab, transform.position, transform.rotation) as Transform;
currentGestureLineRenderer = tmpGesture.GetComponent<LineRenderer>();
gestureLinesRenderer.Add(currentGestureLineRenderer);
vertexCount = 0;
}
if (Input.GetMouseButton(0)) {
points.Add(new Point(virtualKeyPosition.x, -virtualKeyPosition.y, strokeId));
currentGestureLineRenderer.SetVertexCount(++vertexCount);
currentGestureLineRenderer.SetPosition(vertexCount - 1, Camera.main.ScreenToWorldPoint(new Vector3(virtualKeyPosition.x, virtualKeyPosition.y, 10)));
}
}
}
void OnGUI() {
GUI.Box(drawArea, "Draw Area");
GUI.Label(new Rect(10, Screen.height - 40, 500, 50), message);
if (GUI.Button(new Rect(Screen.width - 100, 10, 100, 30), "Recognize")) {
recognized = true;
Gesture candidate = new Gesture(points.ToArray());
Result gestureResult = PointCloudRecognizer.Classify(candidate, trainingSet.ToArray());
message = gestureResult.GestureClass + " " + gestureResult.Score;
}
GUI.Label(new Rect(Screen.width - 200, 150, 70, 30), "Add as: ");
newGestureName = GUI.TextField(new Rect(Screen.width - 150, 150, 100, 30), newGestureName);
if (GUI.Button(new Rect(Screen.width - 50, 150, 50, 30), "Add") && points.Count > 0 && newGestureName != "") {
string fileName = String.Format("{0}/{1}-{2}.xml", Application.persistentDataPath, newGestureName, DateTime.Now.ToFileTime());
#if !UNITY_WEBPLAYER
GestureIO.WriteGesture(points.ToArray(), newGestureName, fileName);
#endif
trainingSet.Add(new Gesture(points.ToArray(), newGestureName));
newGestureName = "";
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 63465958d7bcc403e89a14826c19f7bc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c92918eb0fa65af4a93fdc9056b24297
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
/**
* The $P Point-Cloud Recognizer (.NET Framework 4.0 C# version)
*
* Radu-Daniel Vatavu, Ph.D.
* University Stefan cel Mare of Suceava
* Suceava 720229, Romania
* vatavu@eed.usv.ro
*
* Lisa Anthony, Ph.D.
* UMBC
* Information Systems Department
* 1000 Hilltop Circle
* Baltimore, MD 21250
* lanthony@umbc.edu
*
* Jacob O. Wobbrock, Ph.D.
* The Information School
* University of Washington
* Seattle, WA 98195-2840
* wobbrock@uw.edu
*
* The academic publication for the $P recognizer, and what should be
* used to cite it, is:
*
* Vatavu, R.-D., Anthony, L. and Wobbrock, J.O. (2012).
* Gestures as point clouds: A $P recognizer for user interface
* prototypes. Proceedings of the ACM Int'l Conference on
* Multimodal Interfaces (ICMI '12). Santa Monica, California
* (October 22-26, 2012). New York: ACM Press, pp. 273-280.
*
* This software is distributed under the "New BSD License" agreement:
*
* Copyright (c) 2012, Radu-Daniel Vatavu, Lisa Anthony, and
* Jacob O. Wobbrock. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of the University Stefan cel Mare of Suceava,
* University of Washington, nor UMBC, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Radu-Daniel Vatavu OR Lisa Anthony
* OR Jacob O. Wobbrock BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
**/
using System;
namespace PDollarGestureRecognizer
{
public class Geometry
{
/// <summary>
/// Computes the Squared Euclidean Distance between two points in 2D
/// </summary>
public static float SqrEuclideanDistance(Point a, Point b)
{
return (a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y);
}
/// <summary>
/// Computes the Euclidean Distance between two points in 2D
/// </summary>
public static float EuclideanDistance(Point a, Point b)
{
return (float)Math.Sqrt(SqrEuclideanDistance(a, b));
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4ffb6ac588cc947cebfda99d6eb54783
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,208 @@
/**
* The $P Point-Cloud Recognizer (.NET Framework 4.0 C# version)
*
* Radu-Daniel Vatavu, Ph.D.
* University Stefan cel Mare of Suceava
* Suceava 720229, Romania
* vatavu@eed.usv.ro
*
* Lisa Anthony, Ph.D.
* UMBC
* Information Systems Department
* 1000 Hilltop Circle
* Baltimore, MD 21250
* lanthony@umbc.edu
*
* Jacob O. Wobbrock, Ph.D.
* The Information School
* University of Washington
* Seattle, WA 98195-2840
* wobbrock@uw.edu
*
* The academic publication for the $P recognizer, and what should be
* used to cite it, is:
*
* Vatavu, R.-D., Anthony, L. and Wobbrock, J.O. (2012).
* Gestures as point clouds: A $P recognizer for user interface
* prototypes. Proceedings of the ACM Int'l Conference on
* Multimodal Interfaces (ICMI '12). Santa Monica, California
* (October 22-26, 2012). New York: ACM Press, pp. 273-280.
*
* This software is distributed under the "New BSD License" agreement:
*
* Copyright (c) 2012, Radu-Daniel Vatavu, Lisa Anthony, and
* Jacob O. Wobbrock. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of the University Stefan cel Mare of Suceava,
* University of Washington, nor UMBC, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Radu-Daniel Vatavu OR Lisa Anthony
* OR Jacob O. Wobbrock BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
**/
using System;
namespace PDollarGestureRecognizer
{
/// <summary>
/// Implements a gesture as a cloud of points (i.e., an unordered set of points).
/// Gestures are normalized with respect to scale, translated to origin, and resampled into a fixed number of 32 points.
/// </summary>
public class Gesture
{
public Point[] Points = null; // gesture points (normalized)
public string Name = ""; // gesture class
private const int SAMPLING_RESOLUTION = 32;
/// <summary>
/// Constructs a gesture from an array of points
/// </summary>
/// <param name="points"></param>
public Gesture(Point[] points, string gestureName = "")
{
this.Name = gestureName;
// normalizes the array of points with respect to scale, origin, and number of points
this.Points = Scale(points);
this.Points = TranslateTo(Points, Centroid(Points));
this.Points = Resample(Points, SAMPLING_RESOLUTION);
}
#region gesture pre-processing steps: scale normalization, translation to origin, and resampling
/// <summary>
/// Performs scale normalization with shape preservation into [0..1]x[0..1]
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
private Point[] Scale(Point[] points)
{
float minx = float.MaxValue, miny = float.MaxValue, maxx = float.MinValue, maxy = float.MinValue;
for (int i = 0; i < points.Length; i++)
{
if (minx > points[i].X) minx = points[i].X;
if (miny > points[i].Y) miny = points[i].Y;
if (maxx < points[i].X) maxx = points[i].X;
if (maxy < points[i].Y) maxy = points[i].Y;
}
Point[] newPoints = new Point[points.Length];
float scale = Math.Max(maxx - minx, maxy - miny);
for (int i = 0; i < points.Length; i++)
newPoints[i] = new Point((points[i].X - minx) / scale, (points[i].Y - miny) / scale, points[i].StrokeID);
return newPoints;
}
/// <summary>
/// Translates the array of points by p
/// </summary>
/// <param name="points"></param>
/// <param name="p"></param>
/// <returns></returns>
private Point[] TranslateTo(Point[] points, Point p)
{
Point[] newPoints = new Point[points.Length];
for (int i = 0; i < points.Length; i++)
newPoints[i] = new Point(points[i].X - p.X, points[i].Y - p.Y, points[i].StrokeID);
return newPoints;
}
/// <summary>
/// Computes the centroid for an array of points
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
private Point Centroid(Point[] points)
{
float cx = 0, cy = 0;
for (int i = 0; i < points.Length; i++)
{
cx += points[i].X;
cy += points[i].Y;
}
return new Point(cx / points.Length, cy / points.Length, 0);
}
/// <summary>
/// Resamples the array of points into n equally-distanced points
/// </summary>
/// <param name="points"></param>
/// <param name="n"></param>
/// <returns></returns>
public Point[] Resample(Point[] points, int n)
{
Point[] newPoints = new Point[n];
newPoints[0] = new Point(points[0].X, points[0].Y, points[0].StrokeID);
int numPoints = 1;
float I = PathLength(points) / (n - 1); // computes interval length
float D = 0;
for (int i = 1; i < points.Length; i++)
{
if (points[i].StrokeID == points[i - 1].StrokeID)
{
float d = Geometry.EuclideanDistance(points[i - 1], points[i]);
if (D + d >= I)
{
Point firstPoint = points[i - 1];
while (D + d >= I)
{
// add interpolated point
float t = Math.Min(Math.Max((I - D) / d, 0.0f), 1.0f);
if (float.IsNaN(t)) t = 0.5f;
newPoints[numPoints++] = new Point(
(1.0f - t) * firstPoint.X + t * points[i].X,
(1.0f - t) * firstPoint.Y + t * points[i].Y,
points[i].StrokeID
);
// update partial length
d = D + d - I;
D = 0;
firstPoint = newPoints[numPoints - 1];
}
D = d;
}
else D += d;
}
}
if (numPoints == n - 1) // sometimes we fall a rounding-error short of adding the last point, so add it if so
newPoints[numPoints++] = new Point(points[points.Length - 1].X, points[points.Length - 1].Y, points[points.Length - 1].StrokeID);
return newPoints;
}
/// <summary>
/// Computes the path length for an array of points
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
private float PathLength(Point[] points)
{
float length = 0;
for (int i = 1; i < points.Length; i++)
if (points[i].StrokeID == points[i - 1].StrokeID)
length += Geometry.EuclideanDistance(points[i - 1], points[i]);
return length;
}
#endregion
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 315e77a99b01f483bac4a8055ae4bd44
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,128 @@
using System.IO;
using System.Collections.Generic;
using System.Xml;
using UnityEngine;
namespace PDollarGestureRecognizer
{
public class GestureIO
{
/// <summary>
/// Reads a multistroke gesture from an XML file
/// </summary>
/// <param name="xml"></param>
/// <returns></returns>
public static Gesture ReadGestureFromXML(string xml) {
XmlTextReader xmlReader = null;
Gesture gesture = null;
try {
xmlReader = new XmlTextReader(new StringReader(xml));
gesture = ReadGesture(xmlReader);
} finally {
if (xmlReader != null)
xmlReader.Close();
}
return gesture;
}
/// <summary>
/// Reads a multistroke gesture from an XML file
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static Gesture ReadGestureFromFile(string fileName) {
XmlTextReader xmlReader = null;
Gesture gesture = null;
try {
xmlReader = new XmlTextReader(File.OpenText(fileName));
gesture = ReadGesture(xmlReader);
} finally {
if (xmlReader != null)
xmlReader.Close();
}
return gesture;
}
private static Gesture ReadGesture(XmlTextReader xmlReader)
{
List<Point> points = new List<Point>();
int currentStrokeIndex = -1;
string gestureName = "";
try
{
while (xmlReader.Read())
{
if (xmlReader.NodeType != XmlNodeType.Element) continue;
switch (xmlReader.Name)
{
case "Gesture":
gestureName = xmlReader["Name"];
if (gestureName.Contains("~")) // '~' character is specific to the naming convention of the MMG set
gestureName = gestureName.Substring(0, gestureName.LastIndexOf('~'));
if (gestureName.Contains("_")) // '_' character is specific to the naming convention of the MMG set
gestureName = gestureName.Replace('_', ' ');
break;
case "Stroke":
currentStrokeIndex++;
break;
case "Point":
points.Add(new Point(
float.Parse(xmlReader["X"]),
float.Parse(xmlReader["Y"]),
currentStrokeIndex
));
break;
}
}
}
finally
{
if (xmlReader != null)
xmlReader.Close();
}
return new Gesture(points.ToArray(), gestureName);
}
/// <summary>
/// Writes a multistroke gesture to an XML file
/// </summary>
public static void WriteGesture(PDollarGestureRecognizer.Point[] points, string gestureName, string fileName)
{
using (StreamWriter sw = new StreamWriter(fileName))
{
sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>");
sw.WriteLine("<Gesture Name = \"{0}\">", gestureName);
int currentStroke = -1;
for (int i = 0; i < points.Length; i++)
{
if (points[i].StrokeID != currentStroke)
{
if (i > 0)
sw.WriteLine("\t</Stroke>");
sw.WriteLine("\t<Stroke>");
currentStroke = points[i].StrokeID;
}
sw.WriteLine("\t\t<Point X = \"{0}\" Y = \"{1}\" T = \"0\" Pressure = \"0\" />",
points[i].X, points[i].Y
);
}
sw.WriteLine("\t</Stroke>");
sw.WriteLine("</Gesture>");
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9d76c99c311504a75835d36abebaddee
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,80 @@
/**
* The $P Point-Cloud Recognizer (.NET Framework 4.0 C# version)
*
* Radu-Daniel Vatavu, Ph.D.
* University Stefan cel Mare of Suceava
* Suceava 720229, Romania
* vatavu@eed.usv.ro
*
* Lisa Anthony, Ph.D.
* UMBC
* Information Systems Department
* 1000 Hilltop Circle
* Baltimore, MD 21250
* lanthony@umbc.edu
*
* Jacob O. Wobbrock, Ph.D.
* The Information School
* University of Washington
* Seattle, WA 98195-2840
* wobbrock@uw.edu
*
* The academic publication for the $P recognizer, and what should be
* used to cite it, is:
*
* Vatavu, R.-D., Anthony, L. and Wobbrock, J.O. (2012).
* Gestures as point clouds: A $P recognizer for user interface
* prototypes. Proceedings of the ACM Int'l Conference on
* Multimodal Interfaces (ICMI '12). Santa Monica, California
* (October 22-26, 2012). New York: ACM Press, pp. 273-280.
*
* This software is distributed under the "New BSD License" agreement:
*
* Copyright (c) 2012, Radu-Daniel Vatavu, Lisa Anthony, and
* Jacob O. Wobbrock. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of the University Stefan cel Mare of Suceava,
* University of Washington, nor UMBC, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Radu-Daniel Vatavu OR Lisa Anthony
* OR Jacob O. Wobbrock BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
**/
using System;
namespace PDollarGestureRecognizer
{
/// <summary>
/// Implements a 2D Point that exposes X, Y, and StrokeID properties.
/// StrokeID is the stroke index the point belongs to (e.g., 0, 1, 2, ...) that is filled by counting pen down/up events.
/// </summary>
public class Point
{
public float X, Y;
public int StrokeID;
public Point(float x, float y, int strokeId)
{
this.X = x;
this.Y = y;
this.StrokeID = strokeId;
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 27aaeb3219c2e4a3ea919281a1dfb8fa
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,156 @@
/**
* The $P Point-Cloud Recognizer (.NET Framework 4.0 C# version)
*
* Radu-Daniel Vatavu, Ph.D.
* University Stefan cel Mare of Suceava
* Suceava 720229, Romania
* vatavu@eed.usv.ro
*
* Lisa Anthony, Ph.D.
* UMBC
* Information Systems Department
* 1000 Hilltop Circle
* Baltimore, MD 21250
* lanthony@umbc.edu
*
* Jacob O. Wobbrock, Ph.D.
* The Information School
* University of Washington
* Seattle, WA 98195-2840
* wobbrock@uw.edu
*
* The academic publication for the $P recognizer, and what should be
* used to cite it, is:
*
* Vatavu, R.-D., Anthony, L. and Wobbrock, J.O. (2012).
* Gestures as point clouds: A $P recognizer for user interface
* prototypes. Proceedings of the ACM Int'l Conference on
* Multimodal Interfaces (ICMI '12). Santa Monica, California
* (October 22-26, 2012). New York: ACM Press, pp. 273-280.
*
* This software is distributed under the "New BSD License" agreement:
*
* Copyright (c) 2012, Radu-Daniel Vatavu, Lisa Anthony, and
* Jacob O. Wobbrock. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of the University Stefan cel Mare of Suceava,
* University of Washington, nor UMBC, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Radu-Daniel Vatavu OR Lisa Anthony
* OR Jacob O. Wobbrock BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
**/
using System;
using System.Collections.Generic;
using UnityEngine;
namespace PDollarGestureRecognizer
{
/// <summary>
/// Implements the $P recognizer
/// </summary>
public class PointCloudRecognizer
{
/// <summary>
/// Main function of the $P recognizer.
/// Classifies a candidate gesture against a set of training samples.
/// Returns the class of the closest neighbor in the training set.
/// </summary>
/// <param name="candidate"></param>
/// <param name="trainingSet"></param>
/// <returns></returns>
public static Result Classify(Gesture candidate, Gesture[] trainingSet)
{
float minDistance = float.MaxValue;
string gestureClass = "";
foreach (Gesture template in trainingSet)
{
float dist = GreedyCloudMatch(candidate.Points, template.Points);
if (dist < minDistance)
{
minDistance = dist;
gestureClass = template.Name;
}
}
return gestureClass == "" ? new Result() {GestureClass = "No match", Score = 0.0f} : new Result() {GestureClass = gestureClass, Score = Mathf.Max((minDistance - 2.0f) / -2.0f, 0.0f)};
}
/// <summary>
/// Implements greedy search for a minimum-distance matching between two point clouds
/// </summary>
/// <param name="points1"></param>
/// <param name="points2"></param>
/// <returns></returns>
private static float GreedyCloudMatch(Point[] points1, Point[] points2)
{
int n = points1.Length; // the two clouds should have the same number of points by now
float eps = 0.5f; // controls the number of greedy search trials (eps is in [0..1])
int step = (int)Math.Floor(Math.Pow(n, 1.0f - eps));
float minDistance = float.MaxValue;
for (int i = 0; i < n; i += step)
{
float dist1 = CloudDistance(points1, points2, i); // match points1 --> points2 starting with index point i
float dist2 = CloudDistance(points2, points1, i); // match points2 --> points1 starting with index point i
minDistance = Math.Min(minDistance, Math.Min(dist1, dist2));
}
return minDistance;
}
/// <summary>
/// Computes the distance between two point clouds by performing a minimum-distance greedy matching
/// starting with point startIndex
/// </summary>
/// <param name="points1"></param>
/// <param name="points2"></param>
/// <param name="startIndex"></param>
/// <returns></returns>
private static float CloudDistance(Point[] points1, Point[] points2, int startIndex)
{
int n = points1.Length; // the two clouds should have the same number of points by now
bool[] matched = new bool[n]; // matched[i] signals whether point i from the 2nd cloud has been already matched
Array.Clear(matched, 0, n); // no points are matched at the beginning
float sum = 0; // computes the sum of distances between matched points (i.e., the distance between the two clouds)
int i = startIndex;
do
{
int index = -1;
float minDistance = float.MaxValue;
for(int j = 0; j < n; j++)
if (!matched[j])
{
float dist = Geometry.SqrEuclideanDistance(points1[i], points2[j]); // use squared Euclidean distance to save some processing time
if (dist < minDistance)
{
minDistance = dist;
index = j;
}
}
matched[index] = true; // point index from the 2nd cloud is matched to point i from the 1st cloud
float weight = 1.0f - ((i - startIndex + n) % n) / (1.0f * n);
sum += weight * minDistance; // weight each distance with a confidence coefficient that decreases from 1 to 0
i = (i + 1) % n;
} while (i != startIndex);
return sum;
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 52f14a9a2853942ce85ce3cc1b870fa7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,8 @@
namespace PDollarGestureRecognizer {
public struct Result {
public string GestureClass;
public float Score;
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 62a02aa6ce62e44d08814ea5d80f92c6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData: