added DTW to app
[gsk.git] / GestureDetectionApp / GestureRecognition / src / at / mus / recognition / Recognizer2D.java
blob20216cf4738cb4bb344d22d2be36471e64d39dd6
1 package at.mus.recognition;
3 import java.util.*;
5 public class Recognizer2D {//implements IRecognizer {
6 private static final double Phi = 0.5 * (-1.0 + Math.sqrt(5.0)); // Golden Ratio
7 private static final double AngleRange = deg2Rad(45.0);
8 private static final double AnglePrecision = deg2Rad(2.0);
10 /* Step 1: Resample */
12 public List<Point> resample(List<Point> points, int n) {
13 List<Point> newPoints = new ArrayList<Point>();
14 double I = pathLength(points) / (n - 1);
15 double D = 0.0;
17 newPoints.add(points.get(0));
18 for (int i = 1; i < points.size(); i++) {
19 Point pi_1 = points.get(i - 1);
20 Point pi = points.get(i);
21 double d = pi_1.distance(pi);
22 if (D + d >= I) {
23 Point q = new Point(pi_1.x + ((I - D) / d) * (pi.x - pi_1.x),
24 pi_1.y + ((I - D) / d) * (pi.y - pi_1.y));
25 newPoints.add(q);
26 points.add(i, q);
27 D = 0.0;
28 } else
29 D += d;
31 // somtimes we fall a rounding-error short of adding the last point, so
32 // add it if so
33 if (newPoints.size() == (n - 1)) {
34 Point last = points.get(points.size() - 1);
35 newPoints.add(new Point(last.x, last.y));
37 return newPoints;
40 private double pathLength(List<Point> points) {
41 double d = 0.0;
42 for (int i = 1; i < points.size(); i++)
43 d += points.get(i - 1).distance(points.get(i));
44 return d;
47 /* Step 2: Rotate */
49 public List<Point> rotateToZero(List<Point> points) {
50 Point c = centroid(points);
51 Point p0 = points.get(0);
52 double radians = Math.atan2(c.y - p0.y, c.x - p0.x); // indicative angle
53 return rotateBy(points, c, radians);
56 private List<Point> rotateBy(List<Point> points, Point c, double radians) {
57 double cos = Math.cos(radians);
58 double sin = Math.sin(radians);
60 List<Point> newPoints = new ArrayList<Point>();
61 for (Point p : points) {
62 newPoints.add(new Point(
63 (p.x - c.x) * cos - (p.y - c.y) * sin + c.x, (p.x - c.x)
64 * sin + (p.y - c.y) * cos + c.y));
66 return newPoints;
69 private Point centroid(List<Point> points) {
70 double cx = 0.0, cy = 0.0;
71 for (Point p : points) {
72 cx += p.x;
73 cy += p.y;
75 cx /= points.size();
76 cy /= points.size();
77 return new Point(cx, cy);
80 /* Step 3: Scale */
82 public List<Point> scaleAndTranslateTo(List<Point> points, double size, Point pt) {
83 List<Point> newPoints = new ArrayList<Point>();
85 // scale
86 Rectangle rect = boundingBox(points);
87 for (Point p : points) {
88 newPoints.add(new Point(p.x * (size / rect.width), p.y
89 * (size / rect.height)));
92 // translate
93 Point c = centroid(newPoints);
94 for (Point p : newPoints) {
95 p.x = p.x + pt.x - c.x;
96 p.y = p.y + pt.y - c.y;
98 return newPoints;
101 private Rectangle boundingBox(List<Point> points) {
102 double minX = Double.MAX_VALUE, maxX = Double.MIN_VALUE;
103 double minY = Double.MAX_VALUE, maxY = Double.MIN_VALUE;
104 for (Point p : points) {
105 if (p.x < minX)
106 minX = p.x;
107 if (p.y < minY)
108 minY = p.y;
109 if (p.x > maxX)
110 maxX = p.x;
111 if (p.y > maxY)
112 maxY = p.y;
114 return new Rectangle(minX, minY, maxX - minX, maxY - minY);
117 /* Step 4: Recognition */
120 * @param rawPoints list of points to recognize
121 * @param templates pre-defined list of templates
122 * @param n number of points (resample)
123 * @param size square size (scaleAndTranslateTo)
124 * @param origin desired origin (scaleAndTranslateTo)
126 //@Override
127 public Result recognize(List<Point> rawPoints, List<Template> templates, int n, double size, Point origin) {
128 List<Point> points = scaleAndTranslateTo(rotateToZero(resample(rawPoints, n)), size, origin);
130 Point c = centroid(points);
131 double b = Double.MAX_VALUE, d;
132 Template tpl = null;
133 for (Template t : templates) {
134 d = distanceAtBestAngle(points, t, c, -AngleRange, +AngleRange, AnglePrecision);
135 if (d < b) {
136 b = d; // best (least) distance
137 tpl = t;
140 double halfDiagonal = Math.sqrt(size * size * 2) * 0.5;
141 return new Result(tpl, 1.0 - b / halfDiagonal);
144 private double distanceAtBestAngle(List<Point> points, Template t, Point c, double a, double b, double threshold) {
145 double x1 = Phi * a + (1.0 - Phi) * b;
146 double f1 = distanceAtAngle(points, t, c, x1);
147 double x2 = (1.0 - Phi) * a + Phi * b;
148 double f2 = distanceAtAngle(points, t, c, x2);
150 while (Math.abs(b - a) > threshold) {
151 if (f1 < f2) {
152 b = x2;
153 x2 = x1;
154 f2 = f1;
155 x1 = Phi * a + (1.0 - Phi) * b;
156 f1 = distanceAtAngle(points, t, c, x1);
157 } else {
158 a = x1;
159 x1 = x2;
160 f1 = f2;
161 x2 = (1.0 - Phi) * a + Phi * b;
162 f2 = distanceAtAngle(points, t, c, x2);
165 return Math.min(f1, f2);
168 private double distanceAtAngle(List<Point> points, Template t, Point c,
169 double radians) {
170 List<Point> newPoints = rotateBy(points, c, radians);
172 /* path distance */
173 double d = 0.0;
174 for (int i = 0; i < newPoints.size(); i++)
175 d += newPoints.get(i).distance(t.points[i]);
176 return d / newPoints.size();
179 /* help methods */
180 private static double deg2Rad(double d) {
181 return (d * Math.PI / 180.0);