Permalink
Browse files

Correct rounding errors in Resample, implement OptimalCosineDistance

  • Loading branch information...
1 parent 9b32d8c commit 073e4c8fb0c0058f086970cfbb1b97dc41d5181b @britg committed Jun 17, 2011
View
3 WTMGlyph/WTMGlyph.h
@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
#import "WTMGlyphStroke.h"
+#import "WTMGlyphTemplate.h"
@interface WTMGlyph : NSObject {
NSString *name;
@@ -33,6 +34,8 @@
- (void)permuteStrokeOrders:(int)count;
- (void)createUnistrokes;
+- (float)recognize:(WTMGlyphTemplate *)_template;
+
- (void)addPoint:(CGPoint)point;
- (void)startStroke;
- (void)endStroke;
View
20 WTMGlyph/WTMGlyph.m
@@ -9,6 +9,7 @@
#import "WTMGlyph.h"
#import "WTMGlyphStroke.h"
#import "WTMGlyphTemplate.h"
+#import "WTMGlyphUtilities.h"
#import "CJSONDeserializer.h"
@implementation WTMGlyph
@@ -161,6 +162,25 @@ - (void)createUnistrokes {
}
}
+#pragma mark - Recognition
+
+- (float)recognize:(WTMGlyphTemplate *)input {
+
+ float lowestDistance = FLT_MAX;
+ float distance = FLT_MAX;
+
+ for (int i = 0; i < [self.templates count]; i++) {
+ WTMGlyphTemplate *template = [self.templates objectAtIndex:i];
+ distance = OptimalCosineDistance(template.vector, input.vector);
+
+ if (distance < lowestDistance) {
+ lowestDistance = distance;
+ }
+ }
+
+ return lowestDistance;
+}
+
#pragma mark - On-the-fly creation
- (void)addPoint:(CGPoint)point {
View
2 WTMGlyph/WTMGlyphDetector.h
@@ -32,7 +32,7 @@
- (id)initWithGlyphs:(NSArray *)_glyphs;
- (void)addGlyph:(WTMGlyph *)glyph;
-- (void)addGlyphFromJSON:(NSData *)jsonData;
+- (void)addGlyphFromJSON:(NSData *)jsonData name:(NSString *)name;
- (void)removeGlyphByName:(NSString *)name;
- (void)addPoint:(CGPoint)point;
View
20 WTMGlyph/WTMGlyphDetector.m
@@ -8,6 +8,7 @@
#import "WTMGlyphDetector.h"
#import "WTMGlyphDefaults.h"
+#import "WTMGlyphTemplate.h"
@implementation WTMGlyphDetector
@@ -55,8 +56,8 @@ - (void)addGlyph:(WTMGlyph *)glyph {
}
-- (void)addGlyphFromJSON:(NSData *)jsonData {
- WTMGlyph *t = [[WTMGlyph alloc] initWithName:@"t" JSONData:jsonData];
+- (void)addGlyphFromJSON:(NSData *)jsonData name:(NSString *)name {
+ WTMGlyph *t = [[WTMGlyph alloc] initWithName:name JSONData:jsonData];
[self addGlyph:t];
}
@@ -88,6 +89,21 @@ - (void)detectGlyph {
// Compare the template against existing templates and find the best match.
// If the best match is within a threshold, consider it a true match.
+ WTMGlyphTemplate *inputTemplate = [[WTMGlyphTemplate alloc] initWithName:@"Input" points:self.points];
+ WTMGlyph *glyph;
+ NSEnumerator *eachGlyph = [self.glyphs objectEnumerator];
+ WTMGlyph *bestMatch;
+ float highestScore = 0;
+
+ while ((glyph = (WTMGlyph *)[eachGlyph nextObject])) {
+ float score = 1 / [glyph recognize:inputTemplate];
+ if (score > highestScore) {
+ highestScore = score;
+ bestMatch = glyph;
+ }
+ }
+ DebugLog(@"Glyph detected! %@ with a score of %f", bestMatch.name, highestScore);
+ [delegate glyphDetected:bestMatch];
}
- (NSArray *)resample:(NSArray *)_points {
View
3 WTMGlyph/WTMGlyphUtilities.h
@@ -17,4 +17,5 @@ float Distance(CGPoint point1, CGPoint point2);
float IndicativeAngle(NSArray *points);
NSArray* TranslateToOrigin(NSArray *points);
CGPoint CalcStartUnitVector(NSArray *points, int count);
-NSArray* Vectorize(NSArray *points);
+NSArray* Vectorize(NSArray *points);
+float OptimalCosineDistance(NSArray *v1, NSArray *v2);
View
35 WTMGlyph/WTMGlyphUtilities.m
@@ -43,9 +43,12 @@
}
// rounding error handling
- if ( newPoints.count == num - 1 ) {
+ if ( newPoints.count < num ) {
NSValue *finalValue = [points objectAtIndex:points.count-1];
- [newPoints addObject:finalValue];
+
+ for (int j = 0; j < (num-newPoints.count); j++) {
+ [newPoints addObject:finalValue];
+ }
}
return newPoints;
@@ -225,3 +228,31 @@ CGPoint CalcStartUnitVector(NSArray *points, int count) {
return vector;
}
+
+float OptimalCosineDistance(NSArray *v1, NSArray *v2) {
+ float a = 0.0;
+ float b = 0.0;
+ float v1i;
+ float v2i;
+ float v1next;
+ float v2next;
+ float angle;
+ float score;
+
+ int mincount = (v1.count < v2.count ? v1.count : v2.count);
+
+ for (int i = 0; i < mincount; i+=2) {
+ v1i = [[v1 objectAtIndex:i] floatValue];
+ v2i = [[v2 objectAtIndex:i] floatValue];
+ v1next = [[v1 objectAtIndex:(i+1)] floatValue];
+ v2next = [[v2 objectAtIndex:(i+1)] floatValue];
+
+ a += v1i * v2i + v1next * v2next;
+ b += v1i * v2next + v1next * v2i;
+ }
+
+ angle = atanf( b / a );
+ score = acosf(a * cos(angle) + b * sin(angle));
+
+ return score;
+}
View
3 research/demo/aimgroup/proj/dollar/ndollar.js
@@ -364,11 +364,13 @@ function Resample(points, n)
for (var i = 1; i < points.length; i++)
{
var d = Distance(points[i - 1], points[i]);
+ debugger;
if ((D + d) >= I)
{
var qx = points[i - 1].X + ((I - D) / d) * (points[i].X - points[i - 1].X);
var qy = points[i - 1].Y + ((I - D) / d) * (points[i].Y - points[i - 1].Y);
var q = new Point(qx, qy);
+ debugger;
newpoints[newpoints.length] = q; // append new point 'q'
points.splice(i, 0, q); // insert 'q' at position i in points s.t. 'q' will be the next i
D = 0.0;
@@ -380,6 +382,7 @@ function Resample(points, n)
{
newpoints[newpoints.length] = new Point(points[points.length - 1].X, points[points.length - 1].Y);
}
+ debugger;
return newpoints;
}
function IndicativeAngle(points)

0 comments on commit 073e4c8

Please sign in to comment.