#include "ofxCvBlobTracker.h" ofxCvBlobTracker::ofxCvBlobTracker() { listener = NULL; currentID = 1; extraIDs = 0; reject_distance_threshold = 150; minimumDisplacementThreshold = 2.0f; ghost_frames = 2; } void ofxCvBlobTracker::setListener( ofxCvBlobListener* _listener ) { listener = _listener; } int ofxCvBlobTracker::findOrder( int id ) { // This is a bit inefficient but ok when // assuming low numbers of blobs // a better way would be to use a hash table int count = 0; for( int i=0; i& _blobs ) { unsigned int i, j, k; // Push to history, clear history.push_back( blobs ); if( history.size() > 4 ) { history.erase( history.begin() ); } blobs.clear(); // Load new blobs for( i=0; i<_blobs.size(); i++ ) { blobs.push_back( ofxCvTrackedBlob(_blobs[i]) ); } vector *prev = &history[history.size()-1]; int cursize = blobs.size(); int prevsize = (*prev).size(); // now figure out the 'error' (distance) to all blobs in the previous // frame. We are optimizing for the least change in distance. // While this works really well we could also optimize for lowest // deviation from predicted position, change in size etc... for( i=0; i 0 ) { permute(0); } unsigned int num_results = matrix.size(); // loop through all the potential // ID configurations and find one with lowest error float best_error = 99999, error; int best_error_ndx = -1; for( j=0; jcentroid.x); blobs[i].deltaLoc.y = (blobs[i].centroid.y - oldblob->centroid.y); blobs[i].deltaArea = blobs[i].area - oldblob->area; blobs[i].predictedPos.x = blobs[i].centroid.x + blobs[i].deltaLoc.x; blobs[i].predictedPos.y = blobs[i].centroid.y + blobs[i].deltaLoc.y; blobs[i].deltaLocTotal.x = oldblob->deltaLocTotal.x + blobs[i].deltaLoc.x; blobs[i].deltaLocTotal.y = oldblob->deltaLocTotal.y + blobs[i].deltaLoc.y; } else { blobs[i].deltaLoc = ofPoint( 0.0f, 0.0f ); blobs[i].deltaArea = 0; blobs[i].predictedPos = blobs[i].centroid; blobs[i].deltaLocTotal = ofPoint( 0.0f, 0.0f ); } } } // fire events // // assign ID's for any blobs that are new this frame (ones that didn't get // matched up with a blob from the previous frame). for( i=0; i= 65535 ) { currentID = 0; } //doTouchEvent(blobs[i].getTouchData()); doBlobOn( blobs[i] ); } else { float totalLength = (float)sqrt( blobs[i].deltaLocTotal.x*blobs[i].deltaLocTotal.x + blobs[i].deltaLocTotal.y*blobs[i].deltaLocTotal.y ); if( totalLength >= minimumDisplacementThreshold ) { //doUpdateEvent( blobs[i].getTouchData() ); doBlobMoved( blobs[i] ); blobs[i].deltaLocTotal = ofPoint( 0.0f, 0.0f ); } } } // if a blob disappeared this frame, send a blob off event // for each one in the last frame, see if it still exists in the new frame. for( i=0; iblobOn( b.centroid.x, b.centroid.y, b.id, findOrder(b.id) ); } else { cout << "doBlobOn() event for blob: " << b.id << endl; } } void ofxCvBlobTracker::doBlobMoved( const ofxCvTrackedBlob& b ) { if( listener != NULL ) { listener->blobMoved( b.centroid.x, b.centroid.y, b.id, findOrder(b.id) ); } else { cout << "doBlobMoved() event for blob: " << b.id << endl; } } void ofxCvBlobTracker::doBlobOff( const ofxCvTrackedBlob& b ) { if( listener != NULL ) { listener->blobOff( b.centroid.x, b.centroid.y, b.id, findOrder(b.id) ); } else { cout << "doBlobOff() event for blob: " << b.id << endl; } } // Helper Methods // // inline void ofxCvBlobTracker::permute( int start ) { if( start == ids.size() ) { //for( int i=0; i reject_distance_threshold ) { break; } ids[start] = blobs[start].closest[i]; if( checkValid(start) ) { permute( start+1 ); numchecked++; } if( numchecked >= numcheck ) { break; } } if( extraIDs > 0 ) { ids[start] = -1; // new ID if( checkValidNew(start) ) { permute(start+1); } } } } inline bool ofxCvBlobTracker::checkValidNew( int start ) { int newidcount = 0; newidcount ++; for( int i=0; i extraIDs ) { return false; } return true; } inline bool ofxCvBlobTracker::checkValid( int start ) { for(int i=0; i