root / src / gesture / models / VectorGestureClassification.h @ 22

View | Annotate | Download (9.1 KB)

1
/*
2
 *  Author:
3
 *          Sashikanth Damaraju
4
 *      &
5
 *      Stjepan Rajko
6
 *      Arts, Media and Engineering Program
7
 *      Arizona State University
8
 *
9
 *  Copyright 2008 Arizona Board of Regents.
10
 *
11
 *  This file is part of the AME Patterns openFrameworks addon.
12
 *
13
 *  The AME Patterns openFrameworks addon is free software: you can redistribute it
14
 *  and/or modify it under the terms of the GNU General Public License as
15
 *  published by the Free Software Foundation, either version 3 of the License,
16
 *  or (at your option) any later version.
17
 *
18
 *  The AME Patterns openFrameworks addon is distributed in the hope that it will be
19
 *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 *  GNU General Public License for more details.
22
 *
23
 *  You should have received a copy of the GNU General Public License
24
 *  along with the AME Patterns openFrameworks addon.
25
 *  If not, see <http://www.gnu.org/licenses/>.
26
 */
27
28
29
#ifndef VECTORGESTURECLASSIFICATION
30
#define VECTORGESTURECLASSIFICATION
31
32
#include <vector>
33
#include <limits>
34
#include <iostream>
35
#include <string>
36
#include <algorithm>
37
#include <math.h>
38
#define PI 3.14159265
39
40
using namespace std;
41
42
class multitouch_filter
43
{
44
public:
45
        string feature[3]; //Number of features.
46
47
        //Additional parameter required by the angular heuristic
48
        double angle;
49
        typedef vector<double> result_type;
50
        double xBounds, yBounds;
51
        double sX, sY; //Scale Params
52
        double tX, tY; // Translate Params
53
        vector<int> reassignments;
54
        int selectedFeat;
55
        int numFingers;
56
        /**
57
         * Instantiates the required parameters for this gesture.
58
         * Determines ordering heuristic
59
         */
60
        multitouch_filter(const vector<vector<vector<double> > >  &allSamples)
61
        {
62
                feature[0]                = "Hor";
63
                feature[1]                = "Ver";
64
                feature[2]                = "Ang";
65
                selectedFeat         = -1;
66
                //Use the first frame of the first (representative) sample to set scale parameters for the filter.
67
                initParams(allSamples[0][0]);
68
69
                selectFeature(allSamples);
70
        }
71
72
        /**
73
         * Performs the filter over each frame of the sample
74
         */
75
        vector<double> operator()(const vector<double> &_frame) const
76
        {
77
                vector<double> frame = _frame;
78
                for(size_t i = 0; i + 1< frame.size(); i+=2)
79
                {
80
                        frame[i]                 = frame[i] + tX;
81
                        frame[i + 1]         = frame[i + 1] + tY;
82
                        frame[i]                 *= sX;
83
                        frame[i + 1]         *= sY;
84
                }
85
                vector<double> orderedFrame = frame;
86
                //Reorder the frame using reassignments.
87
                if(reassignments.size() == frame.size() * 2)
88
                {
89
                        for(size_t i = 0; i + 1< frame.size(); i+=2)
90
                        {
91
                                orderedFrame[reassignments[i]] = frame[i];
92
                                orderedFrame[reassignments[i] + 1] = frame[i + 1];
93
                        }
94
                }
95
                return orderedFrame;
96
        }
97
98
        /**
99
         * Determines the translate and scale parameters for this sample
100
         * The translate params ensure that the origin of the first frame is the mean of the points
101
         * The scale params ensures that the first frame has the same bounds as xBounds and yBounds
102
         */
103
        void reset_params_for(const vector<vector<double> > &sample)
104
        {
105
                //xy pairs
106
                //pick minx, miny, maxx, maxy
107
                vector<double> frame1 = sample[0];
108
                double meanX = 0, meanY = 0.;
109
                double minY = numeric_limits<double>::max();
110
                double minX = minY;
111
                double maxX = -minY;
112
                double maxY = -minY;
113
                //Iterate over contacts for frame1
114
                for(size_t i = 0; i + 1< frame1.size(); i+=2)
115
                {
116
                        meanX += frame1[i];
117
                        meanY += frame1[i + 1];
118
                        minX = minX > frame1[i]         ? frame1[i]         : minX;
119
                        minY = minY > frame1[i + 1] ? frame1[i + 1] : minY;
120
                        maxX = maxX < frame1[i]         ? frame1[i]         : maxX;
121
                        maxY = maxY < frame1[i + 1] ? frame1[i + 1] : maxY;
122
                }
123
                //cout << "\t\tminX:"<< minX << " minY:" << minY << " maxX:" << maxX << " maxY:" << maxY << endl;
124
125
                meanX /= frame1.size()/2.; //As of now size() is 2*numContacts.
126
                meanY /= frame1.size()/2.;
127
                //translate and scale. origin = meanX and meanY of points in frame1
128
                // Assuming that the samples are not centered.
129
                tX = -meanX; //If off center, add by tX,tY to center the frame
130
                tY = -meanY;
131
                sX = xBounds / (maxX - minX);
132
                sY = yBounds / (maxY - minY);
133
                cout << "Params: sX:" << sX << " sY:" << sY << " tX:" << tX << " tY:" << tY << endl;
134
135
136
                //Shouldn't be required.
137
                if(selectedFeat >= 0)
138
                {
139
                        vector<double> fVals = getFeatures(frame1, selectedFeat);
140
                        vector<double> sortedVals = fVals;
141
                        sort(sortedVals.begin(), sortedVals.end());
142
                        for(size_t i = 0; i < fVals.size(); i++)
143
                                for(size_t j = 0; j < sortedVals.size(); j++)
144
                                        if(fVals[i] == sortedVals[j])
145
                                                reassignments.push_back(j);
146
147
                }
148
        }
149
150
        bool accepts(const vector<vector<double> > &sample)
151
        {
152
                if(sample.size() > 1u && sample[0].size() == numFingers * 2u)
153
                        return true;
154
                else
155
                {
156
                        cout << "\n---\nSample Ignored: Expected Dimensions: " << numFingers * 2 << ", have " << sample[0].size() << " with size: " << sample.size() << endl;
157
                        return false;
158
                }
159
        }
160
161
        /**
162
         * Select which ordering heuristic to use for this new gesture.
163
         * Feature is selected by maximum ratio of Scatter-between fingers to scatter-within finger across samples.
164
         */
165
        void selectFeature(const vector<vector<vector<double> > > &allSamples)
166
        {
167
                vector<double> scatterRatios;
168
                //Assuming Samples have been translated and scaled
169
                for(size_t featureNum = 0; featureNum < 3; featureNum++)
170
                {
171
                        vector<vector<double> > featureVals;
172
                        //Transform and extract feature values for all 3 heuristics.
173
                        for(size_t sampleNum = 0; sampleNum < allSamples.size(); sampleNum++)
174
                        {
175
                                vector<double> frame = allSamples[sampleNum][0]; //Frame 1 of each sample
176
                                vector<double> fVals = getFeatures(frame, featureNum);
177
                                sort(fVals.begin(), fVals.end());
178
                                featureVals.push_back(fVals);
179
                        }
180
                        scatterRatios.push_back(getScatterRatio(featureVals));
181
                }
182
//                cout << "Scatter Ratios: ";
183
//                for(size_t i =0; i < scatterRatios.size(); i++)
184
//                        cout << scatterRatios[i] << ", ";
185
//                cout << endl;
186
                //Pick max scatterRatio
187
                if(scatterRatios[0] > scatterRatios[1] && scatterRatios[0] > scatterRatios[2])
188
                        selectedFeat = 0;
189
                else if(scatterRatios[1] > scatterRatios[0] && scatterRatios[1] > scatterRatios[2])
190
                        selectedFeat = 1;
191
                else
192
                        selectedFeat = 2;
193
                cout << "---\tOrdering samples by: " << feature[selectedFeat] <<endl;
194
195
        }
196
197
        vector<double> getFeatures(const vector<double> &frame, int featureNum)
198
        {
199
                vector<double> fVals;
200
                for(size_t fingNum = 0; fingNum < frame.size(); fingNum += 2)
201
                {
202
                        double value;
203
                        switch(featureNum)
204
                        {
205
                        //feature = Hor
206
                        case 0:        value = frame[fingNum]; break;//x
207
                        case 1: value = frame[fingNum + 1]; break;//y
208
                        case 2: value = atan2(frame[fingNum], frame[fingNum + 1]) * 180 / PI + 180; break; //angle of the contact from origin (mean)
209
                        }
210
                        fVals.push_back(value);
211
                }
212
                return fVals;
213
        }
214
215
216
private:
217
        void initParams(const vector<double> frame1)
218
        {
219
                //Assuming that only x,y dimensions for each finger are being used.
220
                numFingers = frame1.size() / 2;
221
                //set xBounds and yBounds
222
                double minY = numeric_limits<double>::max();
223
                double minX = minY;
224
                double maxX = -minY;
225
                double maxY = -minY;
226
                for(size_t i = 0; i + 1 < frame1.size(); i += 2)
227
                {
228
                        minX = minX > frame1[i]         ? frame1[i]         : minX;
229
                        minY = minY > frame1[i + 1] ? frame1[i + 1] : minY;
230
                        maxX = maxX < frame1[i]         ? frame1[i]         : maxX;
231
                        maxY = maxY < frame1[i + 1] ? frame1[i + 1] : maxY;
232
                }
233
                xBounds = maxX - minX;
234
                yBounds = maxY - minY;
235
                sX = sY = 1.;
236
                tX = tY = 0.;
237
                angle = 0.;
238
                cout << "NumFingers: " << numFingers << ". Bounds: [" << xBounds << ", " << yBounds << "]" << endl;
239
        }
240
241
        double getScatterRatio(vector<vector<double> > featureValues)
242
        {
243
                unsigned int numFingers = featureValues[0].size();
244
                unsigned int numSamples = featureValues.size();
245
                vector<double> means;
246
247
                for(size_t sampleNum = 0; sampleNum < numSamples; sampleNum++ )
248
                        for(size_t fingNum = 0; fingNum < numFingers; fingNum++)
249
                        {
250
                                if(sampleNum == 0)
251
                                        means.push_back(0);
252
                                means[fingNum] += featureValues[sampleNum][fingNum];
253
                        }
254
                double meanOfMeans = 0;
255
                for(size_t fingNum = 0; fingNum < numFingers; fingNum++)
256
                {
257
                        means[fingNum] /= numSamples;
258
                        meanOfMeans += means[fingNum];
259
                }
260
                meanOfMeans /= numFingers;
261
262
                double sWithin         = 0;
263
                double sBetween = 0;
264
                for(size_t fingNum = 0; fingNum < numFingers; fingNum++)
265
                {
266
                        for(size_t sampleNum = 0; sampleNum < numSamples; sampleNum++ )
267
                        {
268
                                double d = featureValues[sampleNum][fingNum] - means[fingNum];
269
                                sWithin += d*d;
270
                        }
271
                        double meanDiff = means[fingNum] - meanOfMeans;
272
                        sBetween +=  meanDiff * meanDiff * numSamples;
273
                }
274
275
                double scatterRatio = (sWithin > 1e-5) ? sBetween / sWithin : 0;
276
                cout << /*"Between: " << sBetween << "\tWithin: " << sWithin << */"\tScatter : " << scatterRatio << endl;
277
                return scatterRatio;
278
        }
279
};
280
281
class VectorGestureClassification
282
{
283
public:
284
    VectorGestureClassification();
285
    ~VectorGestureClassification();
286
287
    void addGestureWithExamplesAndFilter(const vector<vector<vector<double> > > &examples, int num_states, multitouch_filter filter);
288
    void addGestureWithExamples(const vector<vector<vector<double> > > &examples, int num_states);
289
    int classify(const vector<vector<double> > &gesture);
290
    int numGestures() const;
291
    int lastRecognition() const
292
    {   return mLastRecognition; }
293
    const vector<long double> &probabilities() const;
294
private:
295
    void *mClassificationTask;
296
    int mLastRecognition;
297
};
298
299
#endif