1 package at
.mus
.recognition
;
5 public class Recognizer2D
{
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);
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
);
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
));
31 // somtimes we fall a rounding-error short of adding the last point, so
33 if (newPoints
.size() == (n
- 1)) {
34 Point last
= points
.get(points
.size() - 1);
35 newPoints
.add(new Point(last
.x
, last
.y
));
40 private double pathLength(List
<Point
> points
) {
42 for (int i
= 1; i
< points
.size(); i
++)
43 d
+= points
.get(i
- 1).distance(points
.get(i
));
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
));
69 private Point
centroid(List
<Point
> points
) {
70 double cx
= 0.0, cy
= 0.0;
71 for (Point p
: points
) {
77 return new Point(cx
, cy
);
82 public List
<Point
> scaleAndTranslateTo(List
<Point
> points
, double size
, Point pt
) {
83 List
<Point
> newPoints
= new ArrayList
<Point
>();
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
)));
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
;
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
) {
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 public Result
recognize(List
<Point
> rawPoints
, List
<Template
> templates
, int n
, double size
, Point origin
) {
127 List
<Point
> points
= scaleAndTranslateTo(rotateToZero(resample(rawPoints
, n
)), size
, origin
);
129 Point c
= centroid(points
);
130 double b
= Double
.MAX_VALUE
, d
;
132 for (Template t
: templates
) {
133 d
= distanceAtBestAngle(points
, t
, c
, -AngleRange
, +AngleRange
, AnglePrecision
);
135 b
= d
; // best (least) distance
139 double halfDiagonal
= Math
.sqrt(size
* size
* 2) * 0.5;
140 return new Result(tpl
, 1.0 - b
/ halfDiagonal
);
143 private double distanceAtBestAngle(List
<Point
> points
, Template t
, Point c
, double a
, double b
, double threshold
) {
144 double x1
= Phi
* a
+ (1.0 - Phi
) * b
;
145 double f1
= distanceAtAngle(points
, t
, c
, x1
);
146 double x2
= (1.0 - Phi
) * a
+ Phi
* b
;
147 double f2
= distanceAtAngle(points
, t
, c
, x2
);
149 while (Math
.abs(b
- a
) > threshold
) {
154 x1
= Phi
* a
+ (1.0 - Phi
) * b
;
155 f1
= distanceAtAngle(points
, t
, c
, x1
);
160 x2
= (1.0 - Phi
) * a
+ Phi
* b
;
161 f2
= distanceAtAngle(points
, t
, c
, x2
);
164 return Math
.min(f1
, f2
);
167 private double distanceAtAngle(List
<Point
> points
, Template t
, Point c
,
169 List
<Point
> newPoints
= rotateBy(points
, c
, radians
);
173 for (int i
= 0; i
< newPoints
.size(); i
++)
174 d
+= newPoints
.get(i
).distance(t
.points
[i
]);
175 return d
/ newPoints
.size();
179 private static double deg2Rad(double d
) {
180 return (d
* Math
.PI
/ 180.0);
184 private static double rad2Deg(double r) {
185 return (r * 180.0 / Math.PI);
188 public static class Result
{
189 private Template tpl
;
190 private double match
;
192 public Result(Template tpl
, double match
) {
198 public Template
getTemplate() {
202 public void setTemplate(Template tpl
) {
206 public double getMatch() {
210 public void setMatch(double match
) {