chingu-gesture 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0428ff45bf1eeea2d942b7a8a86570200a9f4e8b
4
+ data.tar.gz: 3f5f4c00de0cd93992c09e5de697004e8443961a
5
+ SHA512:
6
+ metadata.gz: ebfc053add28334e4a62ea718dfe6c72be40bbf70eb3daff8f3400a030c710e27059898d0adfe4cb3207f988092bb9abf8364ae48867be4fc9c060ca14b7636c
7
+ data.tar.gz: b06c919f66cbb53a280a99dbc264359f5c4e01559024529913f736527ffb5db93b6b6aef1d26d824d5c50994c45d0df48f05c16aeb14c2b44cbeab8a3523a5e2
@@ -0,0 +1,286 @@
1
+ #include <ruby.h>
2
+ #include <stdbool.h>
3
+ #include <limits.h>
4
+ #include <float.h>
5
+
6
+ const size_t INIT_BUFFER_SIZE = 255;
7
+ const size_t INIT_GESTURE_SIZE = 15;
8
+
9
+ typedef struct {
10
+ unsigned int x, y;
11
+ } point_t;
12
+
13
+ typedef struct {
14
+ unsigned int name;
15
+ point_t* skeleton;
16
+ size_t n_skeleton;
17
+ } gesture_t;
18
+
19
+ typedef struct {
20
+ point_t* begin;
21
+ point_t* current;
22
+ size_t size;
23
+ gesture_t* gestures;
24
+ size_t n_gesture;
25
+ size_t max_gesture;
26
+ } handler_t;
27
+
28
+ point_t make_point(int x, int y) {
29
+ point_t p;
30
+ p.x = x;
31
+ p.y = y;
32
+ return p;
33
+ }
34
+
35
+ void reset_handler(handler_t* handler) {
36
+ handler->begin = NULL;
37
+ handler->size = 0;
38
+ handler->current = NULL;
39
+ handler->n_gesture = 0;
40
+ handler->gestures = NULL;
41
+ }
42
+
43
+ static void chingu_gesture_free(void* p) {
44
+ unsigned int i;
45
+ handler_t* handler = p;
46
+ for (i = 0; i < handler->n_gesture; i++) {
47
+ free(handler->gestures[i].skeleton);
48
+ }
49
+ free(handler->begin);
50
+ free(handler->gestures);
51
+ reset_handler(handler);
52
+ }
53
+
54
+ static VALUE chingu_gesture_alloc(VALUE klass) {
55
+ handler_t* ptr;
56
+ VALUE obj = Data_Make_Struct(klass, handler_t, NULL, chingu_gesture_free, ptr);
57
+
58
+ reset_handler(ptr);
59
+ return obj;
60
+ }
61
+
62
+ static VALUE chingu_gesture_init(VALUE self) {
63
+ handler_t* handler;
64
+ Data_Get_Struct(self, handler_t, handler);
65
+ handler->begin = calloc(INIT_BUFFER_SIZE, sizeof(point_t));
66
+ handler->size = INIT_BUFFER_SIZE;
67
+ handler->current = handler->begin;
68
+ handler->gestures = calloc(INIT_GESTURE_SIZE, sizeof(gesture_t));
69
+ handler->n_gesture = 0;
70
+ handler->max_gesture = INIT_GESTURE_SIZE;
71
+ if (NULL == handler->begin) {
72
+ rb_raise(rb_eNoMemError, "unable to allocate %ld bytes", INIT_BUFFER_SIZE*sizeof(point_t));
73
+ }
74
+ return self;
75
+ }
76
+
77
+ static void double_size(handler_t* handler) {
78
+ size_t c = handler->current - handler->begin;
79
+ point_t* new_points = calloc(2*handler->size, sizeof(point_t));
80
+ memcpy(new_points, handler->begin, handler->size*sizeof(point_t));
81
+ free(handler->begin);
82
+ handler->begin = new_points;
83
+ handler->size = 2*handler->size;
84
+ handler->current = new_points + c;
85
+ }
86
+
87
+ static void double_size_gesture(handler_t* handler) {
88
+ gesture_t* new_gestures = calloc(2*handler->max_gesture, sizeof(gesture_t));
89
+ memcpy(new_gestures, handler->gestures, handler->max_gesture*sizeof(gesture_t));
90
+ free(handler->gestures);
91
+ handler->gestures = new_gestures;
92
+ handler->max_gesture = 2*handler->max_gesture;
93
+ }
94
+
95
+ static VALUE chingu_gesture_add_point(VALUE self, VALUE x, VALUE y) {
96
+ handler_t* handler;
97
+ Data_Get_Struct(self, handler_t, handler);
98
+ if (handler->current >= handler->begin + handler->size) {
99
+ double_size(handler);
100
+ }
101
+ *handler->current = make_point(FIX2UINT(x), FIX2UINT(y));
102
+ handler->current++;
103
+ return self;
104
+ }
105
+
106
+ static VALUE chingu_gesture_clear(VALUE self) {
107
+ handler_t* handler;
108
+ Data_Get_Struct(self, handler_t, handler);
109
+ handler->current = handler->begin;
110
+ return self;
111
+ }
112
+
113
+ static VALUE chingu_gesture_size(VALUE self) {
114
+ handler_t* handler;
115
+ Data_Get_Struct(self, handler_t, handler);
116
+ return SIZET2NUM(handler->current - handler->begin);
117
+ }
118
+
119
+ static VALUE chingu_gesture_get_x(VALUE self, VALUE i) {
120
+ handler_t* handler;
121
+ size_t ii, csize;
122
+ Data_Get_Struct(self, handler_t, handler);
123
+ ii = NUM2SIZET(i);
124
+ csize = handler->current - handler->begin;
125
+ if (ii >= csize) {
126
+ rb_raise(rb_eRuntimeError, "Out of range");
127
+ }
128
+ return UINT2NUM(handler->begin[ii].x);
129
+ }
130
+
131
+ static VALUE chingu_gesture_get_y(VALUE self, VALUE i) {
132
+ handler_t* handler;
133
+ size_t ii, csize;
134
+ Data_Get_Struct(self, handler_t, handler);
135
+ ii = NUM2SIZET(i);
136
+ csize = handler->current - handler->begin;
137
+ if (ii >= csize) {
138
+ rb_raise(rb_eRuntimeError, "Out of range");
139
+ }
140
+ return UINT2NUM(handler->begin[ii].y);
141
+ }
142
+
143
+ static VALUE chingu_gesture_add_gesture(VALUE self, VALUE list) {
144
+ handler_t* handler;
145
+ gesture_t g;
146
+ unsigned int len;
147
+ unsigned int i;
148
+ VALUE el;
149
+ Data_Get_Struct(self, handler_t, handler);
150
+ if (handler->n_gesture >= handler->max_gesture) {
151
+ double_size_gesture(handler);
152
+ }
153
+ len = RARRAY_LEN(list);
154
+ g.name = handler->n_gesture;
155
+ g.skeleton = malloc(len*sizeof(point_t));
156
+ g.n_skeleton = len;
157
+ for (i = len-1; i < len; i--) {
158
+ el = rb_ary_pop(list);
159
+ g.skeleton[i].y = NUM2UINT(rb_ary_pop(el));
160
+ g.skeleton[i].x = NUM2UINT(rb_ary_pop(el));
161
+ }
162
+ handler->gestures[g.name] = g;
163
+ handler->n_gesture++;
164
+ return UINT2NUM(g.name);
165
+ }
166
+
167
+ inline float max(float a, float b) {
168
+ return a > b ? a : b;
169
+ }
170
+
171
+ inline float min(float a, float b) {
172
+ return a < b ? a : b;
173
+ }
174
+
175
+ static void min_and_range(point_t* first, size_t n_first, float* min_first_x, float* min_first_y, float* first_range) {
176
+ unsigned int i;
177
+ float max_first_x = FLT_MIN, max_first_y = FLT_MIN;
178
+ *min_first_x = FLT_MAX;
179
+ *min_first_y = FLT_MAX;
180
+ for (i = 0; i < n_first; i++) {
181
+ max_first_x = max(max_first_x, first[i].x);
182
+ max_first_y = max(max_first_y, first[i].y);
183
+ *min_first_x = min(*min_first_x, first[i].x);
184
+ *min_first_y = min(*min_first_y, first[i].y);
185
+ }
186
+ *first_range = max(max_first_x - (*min_first_x), max_first_y - (*min_first_y));
187
+ }
188
+
189
+ inline unsigned int index2d(unsigned int x, unsigned int y, size_t xm, size_t ym) {
190
+ return x * ym + y;
191
+ }
192
+
193
+ inline float point_dist(point_t p1, point_t p2,
194
+ float offset1_x, float offset1_y, float scale1,
195
+ float offset2_x, float offset2_y, float scale2) {
196
+ float x1 = (p1.x - offset1_x)/scale1;
197
+ float y1 = (p1.y - offset1_y)/scale1;
198
+ float x2 = (p2.x - offset2_x)/scale2;
199
+ float y2 = (p2.y - offset2_y)/scale2;
200
+ float dx = x1-x2;
201
+ float dy = y1-y2;
202
+ return dx*dx + dy*dy;
203
+ }
204
+
205
+ float sequence_dist(point_t* first, size_t n_first, point_t* second, size_t n_second) {
206
+ // Align the sequences
207
+ float min_first_x=0, min_first_y=0, first_range=0,
208
+ min_second_x=0, min_second_y=0, second_range=0;
209
+ float* table = malloc(sizeof(float) * n_first*n_second);
210
+ unsigned int x, y;
211
+ float d;
212
+ min_and_range(first, n_first, &min_first_x, &min_first_y, &first_range);
213
+ min_and_range(second, n_second, &min_second_x, &min_second_y, &second_range);
214
+ // Dynamic time warping
215
+ table[index2d(0,0, n_first, n_second)] = point_dist(first[0], second[0],
216
+ min_first_x, min_first_y, first_range,
217
+ min_second_x, min_second_y, second_range);
218
+ for (x = 1; x < n_first; x++) {
219
+ d = point_dist(first[x], second[0],
220
+ min_first_x, min_first_y, first_range,
221
+ min_second_x, min_second_y, second_range);
222
+ table[index2d(x, 0, n_first, n_second)] = d + table[index2d(x-1, 0, n_first, n_second)];
223
+ }
224
+ for (y = 1; y < n_second; y++) {
225
+ d = point_dist(first[0], second[y],
226
+ min_first_x, min_first_y, first_range,
227
+ min_second_x, min_second_y, second_range);
228
+ table[index2d(0, y, n_first, n_second)] = d + table[index2d(0, y-1, n_first, n_second)];
229
+ }
230
+ for (x = 1; x < n_first; x++) {
231
+ for (y = 1; y < n_second; y++) {
232
+ d = point_dist(first[x], second[y],
233
+ min_first_x, min_first_y, first_range,
234
+ min_second_x, min_second_y, second_range);
235
+ if (table[index2d(x-1, y, n_first, n_second)] < table[index2d(x,y-1, n_first,n_second)]) {
236
+ table[index2d(x, y, n_first, n_second)] = d + table[index2d(x-1, y, n_first, n_second)];
237
+ } else {
238
+ table[index2d(x, y, n_first, n_second)] = d + table[index2d(x, y-1, n_first, n_second)];
239
+ }
240
+ }
241
+ }
242
+
243
+
244
+ d = table[index2d(n_first-1, n_second-1, n_first, n_second)];
245
+
246
+ free(table);
247
+ // Calculate average distance per point
248
+ return d/(n_second * n_first);
249
+ }
250
+
251
+ static VALUE chingu_gesture_recognize(VALUE self) {
252
+ handler_t* handler;
253
+ unsigned int i;
254
+ unsigned int min_name = 0;
255
+ float min_dist = FLT_MAX;
256
+ float cur_dist = FLT_MAX;
257
+ Data_Get_Struct(self, handler_t, handler);
258
+ if (handler->n_gesture == 0) {
259
+ return Qnil;
260
+ }
261
+ for (i = 0; i < handler->n_gesture; i++) {
262
+ cur_dist = sequence_dist(
263
+ handler->gestures[i].skeleton, handler->gestures[i].n_skeleton,
264
+ handler->begin, (size_t) (handler->current - handler->begin));
265
+ if (cur_dist < min_dist) {
266
+ min_dist = cur_dist;
267
+ min_name = i;
268
+ }
269
+ }
270
+ return rb_ary_new3(2, UINT2NUM(min_name), UINT2NUM(min_dist));
271
+ }
272
+
273
+ void Init_chingu_gesture(void) {
274
+ VALUE klass;
275
+ klass = rb_const_get(rb_cObject, rb_intern("ChinguGesture"));
276
+
277
+ rb_define_alloc_func(klass, chingu_gesture_alloc);
278
+ rb_define_method(klass, "initialize", chingu_gesture_init, 0);
279
+ rb_define_method(klass, "add_point", chingu_gesture_add_point, 2);
280
+ rb_define_method(klass, "clear", chingu_gesture_clear, 0);
281
+ rb_define_method(klass, "size", chingu_gesture_size, 0);
282
+ rb_define_private_method(klass, "_get_x", chingu_gesture_get_x, 1);
283
+ rb_define_private_method(klass, "_get_y", chingu_gesture_get_y, 1);
284
+ rb_define_private_method(klass, "_add_gesture", chingu_gesture_add_gesture, 1);
285
+ rb_define_private_method(klass, "_recognize", chingu_gesture_recognize, 0);
286
+ }
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('chingu_gesture/chingu_gesture')
@@ -0,0 +1,35 @@
1
+ class ChinguGesture
2
+ VERSION = "1.0"
3
+ # Get all points for debugging, etc
4
+ def to_a
5
+ (0...size).collect do |i|
6
+ [_get_x(i), _get_y(i)]
7
+ end
8
+ end
9
+
10
+ def def_gesture(sym, list=nil)
11
+ list ||= yield(ListAdder.new)
12
+ @gesture_translate ||= {}
13
+ @gesture_translate[_add_gesture(list)] = sym
14
+ end
15
+
16
+ def recognize
17
+ n, d = _recognize
18
+ [@gesture_translate[n], d]
19
+ end
20
+
21
+ def translation
22
+ @gesture_translate.dup
23
+ end
24
+
25
+ class ListAdder
26
+ def initialize
27
+ @list = []
28
+ end
29
+
30
+ def [](x,y)
31
+ @list << [x,y]
32
+ end
33
+ end
34
+ end
35
+ require_relative 'chingu_gesture/chingu_gesture'
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chingu-gesture
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - zombiecalypse
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Recognition for 2d gestures from a stream of points using dynamic time
14
+ warping
15
+ email: maergil@gmail.com
16
+ executables: []
17
+ extensions:
18
+ - ext/chingu_gesture/extconf.rb
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ext/chingu_gesture/chingu_gesture.c
22
+ - ext/chingu_gesture/extconf.rb
23
+ - lib/chingu_gesture.rb
24
+ homepage:
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.2.2
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Recognize gestures fast
48
+ test_files: []