root / branches / tbeta / Windows-PS3EyeMuticam / addons / ofxNCore / src / Tracking / Tracking.cpp @ 199

View | Annotate | Download (16.9 KB)

1
/*
2
*  Tracking.cpp
3
*
4
*  Created by Ramsin Khoshabeh on 5/4/08.
5
*  Copyright 2008 risenparadigm. All rights reserved.
6
*
7
*  Changelog:
8
*  08/15/08 -- Fixed a major bug in the track algorithm
9
*
10
*
11
*/
12
13
#include "Tracking.h"
14
15
BlobTracker::BlobTracker()
16
{
17
        IDCounter = 0;
18
        isCalibrating = false;
19
}
20
21
BlobTracker::~BlobTracker()
22
{
23
        delete calibrate;
24
}
25
26
void BlobTracker::passInCalibration(CalibrationUtils* calibrater) 
27
{
28
    calibrate = calibrater;
29
}
30
31
//assigns IDs to each blob in the contourFinder
32
void BlobTracker::track(ContourFinder* newBlobs)
33
{
34
        //initialize ID's of all blobs
35
        for(int i=0; i<newBlobs->nBlobs; i++)
36
                newBlobs->blobs[i].id=-1;
37
38
        // STEP 1: Blob matching
39
        //
40
        //go through all tracked blobs to compute nearest new point
41
        for(int i = 0; i < trackedBlobs.size(); i++)
42
        {
43
                /******************************************************************
44
                 * *****************TRACKING FUNCTION TO BE USED*******************
45
                 * Replace 'trackKnn(...)' with any function that will take the
46
                 * current track and find the corresponding track in the newBlobs
47
                 * 'winner' should contain the index of the found blob or '-1' if
48
                 * there was no corresponding blob
49
                 *****************************************************************/
50
                int winner = trackKnn(newBlobs, &(trackedBlobs[i]), 3, 0);
51
52
                if(winner == -1) //track has died, mark it for deletion
53
                {
54
                        //SEND BLOB OFF EVENT
55
                        TouchEvents.messenger = trackedBlobs[i];
56
57
                        if(isCalibrating)
58
                        {
59
                                TouchEvents.RAWmessenger = trackedBlobs[i];
60
                                TouchEvents.notifyRAWTouchUp(NULL);
61
                        }
62
                        calibrate->transformDimension(TouchEvents.messenger.boundingRect.width, TouchEvents.messenger.boundingRect.height);
63
                        calibrate->cameraToScreenPosition(TouchEvents.messenger.centroid.x, TouchEvents.messenger.centroid.y);
64
                        //erase calibrated blob from map
65
                        calibratedBlobs.erase(TouchEvents.messenger.id);
66
67
                        TouchEvents.notifyTouchUp(NULL);
68
                        //mark the blob for deletion
69
                        trackedBlobs[i].id = -1;
70
                }
71
                else //still alive, have to update
72
                {
73
                        //if winning new blob was labeled winner by another track\
74
                        //then compare with this track to see which is closer
75
                        if(newBlobs->blobs[winner].id!=-1)
76
                        {
77
                                //find the currently assigned blob
78
                                int j; //j will be the index of it
79
                                for(j=0; j<trackedBlobs.size(); j++)
80
                                {
81
                                        if(trackedBlobs[j].id==newBlobs->blobs[winner].id)
82
                                                break;
83
                                }
84
85
                                if(j==trackedBlobs.size())//got to end without finding it
86
                                {
87
                                        newBlobs->blobs[winner].id = trackedBlobs[i].id;
88
                                        newBlobs->blobs[winner].age = trackedBlobs[i].age;
89
                                        newBlobs->blobs[winner].sitting = trackedBlobs[i].sitting;
90
                                        newBlobs->blobs[winner].downTime = trackedBlobs[i].downTime;
91
                                        newBlobs->blobs[winner].color = trackedBlobs[i].color;
92
                                        newBlobs->blobs[winner].lastTimeTimeWasChecked = trackedBlobs[i].lastTimeTimeWasChecked;
93
94
                                        trackedBlobs[i] = newBlobs->blobs[winner];
95
                                }
96
                                else //found it, compare with current blob
97
                                {
98
                                        double x = newBlobs->blobs[winner].centroid.x;
99
                                        double y = newBlobs->blobs[winner].centroid.y;
100
                                        double xOld = trackedBlobs[j].centroid.x;
101
                                        double yOld = trackedBlobs[j].centroid.y;
102
                                        double xNew = trackedBlobs[i].centroid.x;
103
                                        double yNew = trackedBlobs[i].centroid.y;
104
                                        double distOld = (x-xOld)*(x-xOld)+(y-yOld)*(y-yOld);
105
                                        double distNew = (x-xNew)*(x-xNew)+(y-yNew)*(y-yNew);
106
107
                                        //if this track is closer, update the ID of the blob
108
                                        //otherwise delete this track.. it's dead
109
                                        if(distNew<distOld) //update
110
                                        {
111
                                                newBlobs->blobs[winner].id = trackedBlobs[i].id;
112
                                                newBlobs->blobs[winner].age = trackedBlobs[i].age;
113
                                                newBlobs->blobs[winner].sitting = trackedBlobs[i].sitting;
114
                                                newBlobs->blobs[winner].downTime = trackedBlobs[i].downTime;
115
                                                newBlobs->blobs[winner].color = trackedBlobs[i].color;
116
                                                newBlobs->blobs[winner].lastTimeTimeWasChecked = trackedBlobs[i].lastTimeTimeWasChecked;
117
118
//TODO--------------------------------------------------------------------------
119
                                                //now the old winning blob has lost the win.
120
                                                //I should also probably go through all the newBlobs
121
                                                //at the end of this loop and if there are ones without
122
                                                //any winning matches, check if they are close to this
123
                                                //one. Right now I'm not doing that to prevent a
124
                                                //recursive mess. It'll just be a new track.
125
126
                                                //SEND BLOB OFF EVENT
127
                                                TouchEvents.messenger = trackedBlobs[j];
128
129
                                                if(isCalibrating)
130
                                                {
131
                                                        TouchEvents.RAWmessenger = trackedBlobs[j];
132
                                                        TouchEvents.notifyRAWTouchUp(NULL);
133
                                                }
134
135
                        calibrate->transformDimension(TouchEvents.messenger.boundingRect.width, TouchEvents.messenger.boundingRect.height);
136
                        calibrate->cameraToScreenPosition(TouchEvents.messenger.centroid.x, TouchEvents.messenger.centroid.y);
137
                                                //erase calibrated blob from map
138
                                                calibratedBlobs.erase(TouchEvents.messenger.id);
139
140
                                             TouchEvents.notifyTouchUp(NULL);
141
                                                //mark the blob for deletion
142
                                                trackedBlobs[j].id = -1;
143
//------------------------------------------------------------------------------
144
                                        }
145
                                        else //delete
146
                                        {
147
                                                //SEND BLOB OFF EVENT
148
                                                TouchEvents.messenger = trackedBlobs[i];
149
150
                                                if(isCalibrating)
151
                                                {
152
                                                        TouchEvents.RAWmessenger = trackedBlobs[i];
153
                                                        TouchEvents.notifyRAWTouchUp(NULL);
154
                                                }
155
156
                        calibrate->transformDimension(TouchEvents.messenger.boundingRect.width, TouchEvents.messenger.boundingRect.height);
157
                        calibrate->cameraToScreenPosition(TouchEvents.messenger.centroid.x, TouchEvents.messenger.centroid.y);
158
                                                //erase calibrated blob from map
159
                                                calibratedBlobs.erase(TouchEvents.messenger.id);
160
161
                                                TouchEvents.notifyTouchUp(NULL);
162
                                                //mark the blob for deletion
163
                                                trackedBlobs[i].id = -1;
164
                                        }
165
                                }
166
                        }
167
                        else //no conflicts, so simply update
168
                        {
169
                                newBlobs->blobs[winner].id = trackedBlobs[i].id;
170
                                newBlobs->blobs[winner].age = trackedBlobs[i].age;
171
                                newBlobs->blobs[winner].sitting = trackedBlobs[i].sitting;
172
                                newBlobs->blobs[winner].downTime = trackedBlobs[i].downTime;
173
                                newBlobs->blobs[winner].color = trackedBlobs[i].color;
174
                                newBlobs->blobs[winner].lastTimeTimeWasChecked = trackedBlobs[i].lastTimeTimeWasChecked;
175
                        }
176
                }
177
        }
178
179
        // AlexP
180
        // save the current time since we will be using it a lot
181
        int now = ofGetElapsedTimeMillis();
182
183
        // STEP 2: Blob update
184
        //
185
        //--Update All Current Tracks
186
        //remove every track labeled as dead (ID='-1')
187
        //find every track that's alive and copy it's data from newBlobs
188
        for(int i = 0; i < trackedBlobs.size(); i++)
189
        {
190
                if(trackedBlobs[i].id == -1) //dead
191
                {
192
                        //erase track
193
                        trackedBlobs.erase(trackedBlobs.begin()+i, trackedBlobs.begin()+i+1);
194
                        i--; //decrement one since we removed an element
195
                }
196
                else //living, so update it's data
197
                {
198
                        for(int j = 0; j < newBlobs->nBlobs; j++)
199
                        {
200
                                if(trackedBlobs[i].id == newBlobs->blobs[j].id)
201
                                {
202
                                        //update track
203
                                        ofPoint tempLastCentroid = trackedBlobs[i].centroid; // assign the new centroid to the old
204
                                        trackedBlobs[i] = newBlobs->blobs[j];
205
                                        trackedBlobs[i].lastCentroid = tempLastCentroid;
206
207
                                        ofPoint tD;
208
                                        //get the Differences in position
209
                                        tD.set(trackedBlobs[i].centroid.x - trackedBlobs[i].lastCentroid.x, 
210
                                                        trackedBlobs[i].centroid.y - trackedBlobs[i].lastCentroid.y);
211
                                        //calculate the acceleration
212
                                        float posDelta = sqrtf((tD.x*tD.x)+(tD.y*tD.y));
213
214
                                        // AlexP
215
                                        // now, filter the blob position based on MOVEMENT_FILTERING value
216
                                        // the MOVEMENT_FILTERING ranges [0,15] so we will have that many filtering steps
217
                                        // Here we have a weighted low-pass filter
218
                                        // adaptively adjust the blob position filtering strength based on blob movement
219
                                        // http://www.wolframalpha.com/input/?i=plot+1/exp(x/15)+and+1/exp(x/10)+and+1/exp(x/5)+from+0+to+100
220
                                        float a = 1.0f - 1.0f / expf(posDelta / (1.0f + (float)MOVEMENT_FILTERING*10));
221
                                        trackedBlobs[i].centroid.x = a * trackedBlobs[i].centroid.x + (1-a) * trackedBlobs[i].lastCentroid.x;
222
                                        trackedBlobs[i].centroid.y = a * trackedBlobs[i].centroid.y + (1-a) * trackedBlobs[i].lastCentroid.y;
223
224
                                        //get the Differences in position
225
                                        trackedBlobs[i].D.set(trackedBlobs[i].centroid.x - trackedBlobs[i].lastCentroid.x, 
226
                                                                                        trackedBlobs[i].centroid.y - trackedBlobs[i].lastCentroid.y);
227
228
                                        //if( abs((int)trackedBlobs[i].D.x) > 1 || abs((int)trackedBlobs[i].D.y) > 1) {
229
//                                                printf("\nUNUSUAL BLOB @ %f\n-----------------------\ntrackedBlobs[%i]\nD = (%f, %f)\nXY= (%f, %f)\nlastTimeTimeWasChecked = %f\nsitting = %f\n",
230
//                                                           ofGetElapsedTimeMillis(),
231
//                                                           i,
232
//                                                           trackedBlobs[i].D.x,  trackedBlobs[i].D.y,
233
//                                                           trackedBlobs[i].centroid.x, trackedBlobs[i].centroid.y,
234
//                                                           trackedBlobs[i].lastTimeTimeWasChecked,
235
//                                                           trackedBlobs[i].downTime,
236
//                                                           trackedBlobs[i].sitting
237
//                                                );
238
//                                        }
239
240
                                        //calculate the acceleration again
241
                                        tD = trackedBlobs[i].D;
242
                                        trackedBlobs[i].maccel = sqrtf((tD.x* tD.x)+(tD.y*tD.y)) / (now - trackedBlobs[i].lastTimeTimeWasChecked);
243
244
                                        //calculate the age
245
                                        trackedBlobs[i].age = ofGetElapsedTimef() - trackedBlobs[i].downTime;
246
247
                                        //set sitting (held length)
248
                    if(trackedBlobs[i].maccel < 7)
249
                                        {        //1 more frame of sitting
250
                                                if(trackedBlobs[i].sitting != -1)
251
                                                        trackedBlobs[i].sitting = ofGetElapsedTimef() - trackedBlobs[i].downTime;           
252
                                        }
253
                                        else
254
                                                trackedBlobs[i].sitting = -1;
255
256
                                        //printf("time: %f\n", ofGetElapsedTimeMillis());
257
                                        //printf("%i age: %f, downTimed at: %f\n", i, trackedBlobs[i].age, trackedBlobs[i].downTime);
258
259
                                        //if blob has been 'holding/sitting' for 1 second send a held event
260
                                        if(trackedBlobs[i].sitting > 1.0f)
261
                                        {
262
                                                //SEND BLOB HELD EVENT
263
                                                TouchEvents.messenger = trackedBlobs[i];
264
265
                                                if(isCalibrating)
266
                                                {
267
                                                        TouchEvents.RAWmessenger = trackedBlobs[i];
268
                                                        TouchEvents.notifyRAWTouchHeld(NULL);
269
                                                }
270
271
                                                //calibrated values
272
                                                calibrate->transformDimension(TouchEvents.messenger.boundingRect.width, TouchEvents.messenger.boundingRect.height);
273
                                                calibrate->cameraToScreenPosition(TouchEvents.messenger.centroid.x, TouchEvents.messenger.centroid.y);
274
                                                calibrate->cameraToScreenPosition(TouchEvents.messenger.lastCentroid.x, TouchEvents.messenger.lastCentroid.y);
275
                                                
276
                                                //Calibrated dx/dy
277
                                                TouchEvents.messenger.D.set(trackedBlobs[i].centroid.x - trackedBlobs[i].lastCentroid.x, 
278
                                                                                                trackedBlobs[i].centroid.y - trackedBlobs[i].lastCentroid.y);
279
280
                                                //calibrated acceleration
281
                                                ofPoint tD = TouchEvents.messenger.D;
282
                                                TouchEvents.messenger.maccel = sqrtf((tD.x*tD.x)+(tD.y*tD.y)) / (now - TouchEvents.messenger.lastTimeTimeWasChecked);
283
                                                TouchEvents.messenger.lastTimeTimeWasChecked = now;
284
285
                                                //add to calibration map
286
                                                calibratedBlobs[TouchEvents.messenger.id] = TouchEvents.messenger;
287
288
                        //held event only happens once so set to -1
289
                        trackedBlobs[i].sitting = -1;
290
291
                                                TouchEvents.notifyTouchHeld(NULL);
292
                                        } 
293
                                        else 
294
                                        {
295
                                                //printf("(%f, %f) -> (%f, %f) \n", trackedBlobs[i].lastCentroid.x, trackedBlobs[i].lastCentroid.y, trackedBlobs[i].centroid.x, trackedBlobs[i].centroid.y);
296
297
                                                //SEND BLOB MOVED EVENT
298
                                                TouchEvents.messenger = trackedBlobs[i];
299
300
                                                if(isCalibrating)
301
                                                {
302
                                                        TouchEvents.RAWmessenger = trackedBlobs[i];
303
                                                        TouchEvents.notifyRAWTouchMoved(NULL);
304
                                                }
305
306
                                                //calibrated values
307
                                                calibrate->transformDimension(TouchEvents.messenger.boundingRect.width, TouchEvents.messenger.boundingRect.height);
308
                                                calibrate->cameraToScreenPosition(TouchEvents.messenger.centroid.x, TouchEvents.messenger.centroid.y);
309
                                                calibrate->cameraToScreenPosition(TouchEvents.messenger.lastCentroid.x, TouchEvents.messenger.lastCentroid.y);
310
311
                                                //Calibrated dx/dy
312
                                                TouchEvents.messenger.D.set(trackedBlobs[i].centroid.x - trackedBlobs[i].lastCentroid.x, 
313
                                                                                                trackedBlobs[i].centroid.y - trackedBlobs[i].lastCentroid.y);
314
315
                                                //printf("d(%0.4f, %0.4f)\n", TouchEvents.messenger.D.x, TouchEvents.messenger.D.y);
316
                                
317
                                                //calibrated acceleration
318
                                                ofPoint tD = TouchEvents.messenger.D;
319
                                                TouchEvents.messenger.maccel = sqrtf((tD.x*tD.x)+(tD.y*tD.y)) / (now - TouchEvents.messenger.lastTimeTimeWasChecked);
320
                                                TouchEvents.messenger.lastTimeTimeWasChecked = now;
321
322
                                                //add to calibration map
323
                                                calibratedBlobs[TouchEvents.messenger.id] = TouchEvents.messenger;
324
325
                                                TouchEvents.notifyTouchMoved(NULL);
326
                                        }
327
                                        // AlexP
328
                                        // The last lastTimeTimeWasChecked is updated at the end after all acceleration values are calculated
329
                                        trackedBlobs[i].lastTimeTimeWasChecked = now;
330
                                }
331
                        }
332
                }
333
        }
334
335
        // STEP 3: add tracked blobs to TouchEvents
336
        //--Add New Living Tracks
337
        //now every new blob should be either labeled with a tracked ID or\
338
        //have ID of -1... if the ID is -1... we need to make a new track
339
        for(int i = 0; i < newBlobs->nBlobs; i++)
340
        {
341
                if(newBlobs->blobs[i].id==-1)
342
                {
343
                        //add new track
344
                        newBlobs->blobs[i].id=IDCounter++;
345
                        newBlobs->blobs[i].downTime = ofGetElapsedTimef();
346
                        //newBlobs->blobs[i].lastTimeTimeWasChecked = ofGetElapsedTimeMillis();
347
348
                        //random color for blob. Could be useful?
349
                        int r = ofRandom(0, 255);
350
            int g = ofRandom(0, 255);
351
            int b = ofRandom(0, 255);
352
            //Convert to hex
353
            int rgbNum = ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
354
            //Set color
355
            newBlobs->blobs[i].color = rgbNum;
356
357
                        //Add to blob messenger
358
                        TouchEvents.messenger = newBlobs->blobs[i];
359
360
                        if(isCalibrating)
361
                        {
362
                                TouchEvents.RAWmessenger = newBlobs->blobs[i];
363
                                TouchEvents.notifyRAWTouchDown(NULL);
364
                        }
365
            calibrate->transformDimension(TouchEvents.messenger.boundingRect.width, TouchEvents.messenger.boundingRect.height);
366
            calibrate->cameraToScreenPosition(TouchEvents.messenger.centroid.x, TouchEvents.messenger.centroid.y);
367
                        //add to calibrated blob map
368
                        calibratedBlobs[TouchEvents.messenger.id] = TouchEvents.messenger;
369
370
                        //Send Event
371
                        TouchEvents.notifyTouchDown(NULL);
372
                        trackedBlobs.push_back(newBlobs->blobs[i]);
373
                }
374
        }
375
}
376
377
std::map<int, Blob> BlobTracker::getTrackedBlobs()
378
{
379
    return calibratedBlobs;
380
}
381
382
/*************************************************************************
383
* Finds the blob in 'newBlobs' that is closest to the trackedBlob with index
384
* 'ind' according to the KNN algorithm and returns the index of the winner
385
* newBlobs        = list of blobs detected in the latest frame
386
* track                = current tracked blob being tested
387
* k                        = number of nearest neighbors to consider\
388
*                          1,3,or 5 are common numbers..\
389
*                          must always be an odd number to avoid tying
390
* thresh        = threshold for optimization
391
**************************************************************************/
392
int BlobTracker::trackKnn(ContourFinder *newBlobs, Blob *track, int k, double thresh = 0)
393
{
394
395
        int winner = -1; //initially label track as '-1'=dead
396
        if((k%2)==0) k++; //if k is not an odd number, add 1 to it
397
398
        //if it exists, square the threshold to use as square distance
399
        if(thresh>0)
400
                thresh *= thresh;
401
402
        //list of neighbor point index and respective distances
403
        std::list<std::pair<int,double> > nbors;
404
        std::list<std::pair<int,double> >::iterator iter;
405
406
        //find 'k' closest neighbors of testpoint
407
        double x, y, xT, yT, dist;
408
        for(int i=0; i<newBlobs->nBlobs; i++)
409
        {
410
                x = newBlobs->blobs[i].centroid.x;
411
                y = newBlobs->blobs[i].centroid.y;
412
413
                xT = track->centroid.x;
414
                yT = track->centroid.y;
415
                dist = (x-xT)*(x-xT)+(y-yT)*(y-yT);
416
417
                if(dist<=thresh)//it's good, apply label if no label yet and return
418
                {
419
                        winner = i;
420
                        return winner;
421
                }
422
423
                /****************************************************************
424
                * check if this blob is closer to the point than what we've seen
425
                *so far and add it to the index/distance list if positive
426
                ****************************************************************/
427
428
                //search the list for the first point with a longer distance
429
                for(iter=nbors.begin(); iter!=nbors.end()
430
                        && dist>=iter->second; iter++);
431
432
                if((iter!=nbors.end())||(nbors.size()<k)) //it's valid, insert it
433
                {
434
                        nbors.insert(iter, 1, std::pair<int, double>(i, dist));
435
                        //too many items in list, get rid of farthest neighbor
436
                        if(nbors.size()>k)
437
                                nbors.pop_back();
438
                }
439
        }
440
441
        /********************************************************************
442
        * we now have k nearest neighbors who cast a vote, and the majority
443
        * wins. we use each class average distance to the target to break any
444
        * possible ties.
445
        *********************************************************************/
446
447
        // a mapping from labels (IDs) to count/distance
448
        std::map<int, std::pair<int, double> > votes;
449
450
        //remember:
451
        //iter->first = index of newBlob
452
        //iter->second = distance of newBlob to current tracked blob
453
        for(iter=nbors.begin(); iter!=nbors.end(); iter++)
454
        {
455
                //add up how many counts each neighbor got
456
                int count = ++(votes[iter->first].first);
457
                double dist = (votes[iter->first].second+=iter->second);
458
459
                /* check for a possible tie and break with distance */
460
                if(count>votes[winner].first || count==votes[winner].first
461
                        && dist<votes[winner].second)
462
                {
463
                        winner = iter->first;
464
                }
465
        }
466
        return winner;
467
}