Heroes_of_Hiis/Assets/PDollar/Scripts/PDollarGestureRecognizer/PointCloudRecognizer.cs

156 lines
6.9 KiB
C#

/**
* 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;
}
}
}