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 +7 -0
- data/ext/chingu_gesture/chingu_gesture.c +286 -0
- data/ext/chingu_gesture/extconf.rb +3 -0
- data/lib/chingu_gesture.rb +35 -0
- metadata +48 -0
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,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: []
|