From ba994f4404b6eadcab4e0ead46ef4d3ffeceb024 Mon Sep 17 00:00:00 2001 From: Antoine Villeret Date: Thu, 10 Jul 2014 14:39:22 +0000 Subject: lots of changes ! 1. switch to a new build system based on automake (because we need to check for some lib on ./configure before make) 2. sort files in different directory 3. add some new features (some of them need OpenCV >= 2.4.5) svn path=/trunk/externals/pix_opencv/; revision=17324 --- src/pix_opencv_trackKnn.cc | 602 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 602 insertions(+) create mode 100644 src/pix_opencv_trackKnn.cc (limited to 'src/pix_opencv_trackKnn.cc') diff --git a/src/pix_opencv_trackKnn.cc b/src/pix_opencv_trackKnn.cc new file mode 100644 index 0000000..6113b0c --- /dev/null +++ b/src/pix_opencv_trackKnn.cc @@ -0,0 +1,602 @@ +//////////////////////////////////////////////////////// +// +// GEM - Graphics Environment for Multimedia +// +// zmoelnig@iem.kug.ac.at +// +// Implementation file +// +// Copyright (c) 1997-2000 Mark Danks. +// Copyright (c) Günther Geiger. +// Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM +// Copyright (c) 2002 James Tittle & Chris Clepper +// For information on usage and redistribution, and for a DISCLAIMER OF ALL +// WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution. +// +///////////////////////////////////////////////////////// +// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv) +// and on some code from CCV : http://ccv.nuigroup.com/ (the tracker it self) +// pix_opencv_trackKnn extract and simplify contours of incomming image +// by Antoine Villeret - 2012 + +#include "pix_opencv_trackKnn.h" + +using namespace cv; +using namespace std; + +CPPEXTERN_NEW(pix_opencv_trackKnn) + +///////////////////////////////////////////////////////// +// +// pix_opencv_trackKnn +// +///////////////////////////////////////////////////////// +// Constructor +// +///////////////////////////////////////////////////////// +pix_opencv_trackKnn :: pix_opencv_trackKnn() : \ + m_taboutput(0), \ + IdCounter(0), \ + m_x_arrayname(NULL), \ + m_y_arrayname(NULL), \ + m_z_arrayname(NULL) +{ + m_dataout_right = outlet_new(this->x_obj, 0); + + //~ post("build on %s at %s", __DATE__, __TIME__); +} + +///////////////////////////////////////////////////////// +// Destructor +// +///////////////////////////////////////////////////////// +pix_opencv_trackKnn :: ~pix_opencv_trackKnn() +{ +} + +///////////////////////////////////////////////////////// +// processImage +// +///////////////////////////////////////////////////////// + +void pix_opencv_trackKnn :: processImage(imageStruct &image) +{ + // nothing to do for now... +} + +///////////////////////////////////////////////////////// +// static member function +// +///////////////////////////////////////////////////////// +void pix_opencv_trackKnn :: obj_setupCallback(t_class *classPtr) +{ + CPPEXTERN_MSG(classPtr, "cvblob", cvblobMess); + CPPEXTERN_MSG1(classPtr, "drawBlob", drawBlobMess, int); + CPPEXTERN_MSG1(classPtr, "taboutput", taboutputMess, float); + CPPEXTERN_MSG1(classPtr, "tabinput", tabinputMess, float); + CPPEXTERN_MSG(classPtr, "settab", tableMess); + CPPEXTERN_MSG0(classPtr, "reset", resetMess); +} + +///////////////////////////////////////////////////////// +// messages handling +// +///////////////////////////////////////////////////////// +void pix_opencv_trackKnn :: resetMess(){ + m_trackedBlobs.clear(); + IdCounter=0; + +} + +void pix_opencv_trackKnn :: drawBlobMess(double arg) +{ + m_drawBlob = arg > 0 ? arg : 3.; +} + +void pix_opencv_trackKnn :: cvblobMess(t_symbol *s, int argc, t_atom* argv){ + // supposed to receive a cvblob message formatted like an iem matrix : the 2 first elements are the matrix cols and rows + + // first check is matrix is valid... + if ( argc < 2 ){ + error("this doesn't seems to be a valid cvblob matrix (too few elements !)"); + return; + } + + for ( int i = 0 ; i < argc ; i++){ + if ( argv[i].a_type != A_FLOAT ){ + error("cvblob takes only float arg"); + return; + } + } + + int blob_number = (int) atom_getfloatarg(0,argc,argv); + int blob_atom_size = (int) atom_getfloatarg(1, argc, argv); + + if ( blob_number * blob_atom_size != argc - 2 ){ + error("cvblob matrix have a wrong size %d != %d x %d", argc -2, blob_number, blob_atom_size); + return; + } + + m_inputBlobs.clear(); + for ( int i = 0; i < blob_number ; i++){ + Blob blob; + blob.id=-1; + int j = i*blob_atom_size+2; + if ( blob_atom_size < 3 ) j--; // if there is more than 2 element per blob, assume, the first is Id + blob.centroid.x=atom_getfloatarg(j+1,argc,argv); + blob.centroid.y=atom_getfloatarg(j+2,argc,argv); + + if ( blob_atom_size > 2 ){ // if we pass more than just x and y coordinates, assume the same data order than pix_opencv_contours outputs + blob.angleBoundingRect.size.width=atom_getfloatarg(j+3,argc,argv); + blob.angleBoundingRect.size.height=atom_getfloatarg(j+4,argc,argv); + blob.angle=atom_getfloatarg(j+5,argc,argv); + blob.area=atom_getfloatarg(j+6,argc,argv); + + blob.nPts=atom_getfloatarg(j+15,argc,argv); + blob.length=atom_getfloatarg(j+16,argc,argv); + } + + m_inputBlobs.push_back(blob); + } + + doTracking(); +} + +void pix_opencv_trackKnn :: doTracking() +{ + // find an Id + for(unsigned int i=0; i 1 || abs((int)m_trackedBlobs[i].D.y) > 1) { + // printf("\nUNUSUAL BLOB @ %f\n-----------------------\nm_trackedBlobs[%i]\nD = (%f, %f)\nXY= (%f, %f)\nlastTimeTimeWasChecked = %f\nsitting = %f\n", + // ofGetElapsedTimeMillis(), + // i, + // m_trackedBlobs[i].D.x, m_trackedBlobs[i].D.y, + // m_trackedBlobs[i].centroid.x, m_trackedBlobs[i].centroid.y, + // m_trackedBlobs[i].lastTimeTimeWasChecked, + // m_trackedBlobs[i].downTime, + // m_trackedBlobs[i].sitting + // ); + // } + + + //calculate the accelleration + //~ofPoint tD = m_trackedBlobs[i].D; + //~m_trackedBlobs[i].maccel = sqrtf((tD.x* tD.x)+(tD.y*tD.y)/(ofGetElapsedTimeMillis() - m_trackedBlobs[i].lastTimeTimeWasChecked)); + + //~m_trackedBlobs[i].lastTimeTimeWasChecked = ofGetElapsedTimeMillis(); + + //calculate the age + //~m_trackedBlobs[i].age = ofGetElapsedTimef() - m_trackedBlobs[i].downTime; + + //if not moving more than min_movement_threshold then set to same position as last frame + // if(m_trackedBlobs[i].maccel < MIN_MOVEMENT_THRESHOLD) + // { //this helps avoid jittery blobs + // m_trackedBlobs[i].centroid.x = m_trackedBlobs[i].lastCentroid.x; + // m_trackedBlobs[i].centroid.y = m_trackedBlobs[i].lastCentroid.y; + // } + + //set sitting (held length) + //~if(m_trackedBlobs[i].maccel < 7) + { //1 more frame of sitting + //~if(m_trackedBlobs[i].sitting != -1) + //~m_trackedBlobs[i].sitting = ofGetElapsedTimef() - m_trackedBlobs[i].downTime; + //~} + //~else { + //~m_trackedBlobs[i].sitting = -1; + //~} + + //printf("time: %f\n", ofGetElapsedTimeMillis()); + //printf("%i age: %f, downTimed at: %f\n", i, m_trackedBlobs[i].age, m_trackedBlobs[i].downTime); + + //if blob has been 'holding/sitting' for 1 second send a held event + //~if(m_trackedBlobs[i].sitting > 1.0f){ + //held event only happens once so set to -1 + //~m_trackedBlobs[i].sitting = -1; + } + } + } + } + } + /*==================*/ + //--Add New Living Tracks + //now every new blob should be either labeled with a tracked ID or + //have ID of -1... if the ID is -1... we need to make a new track + for(unsigned int i=0; i(0); + Scalar scolor(ptr[0],ptr[1],ptr[2]); + + m_inputBlobs[i].color = scolor; + m_trackedBlobs.push_back(m_inputBlobs[i]); + + //~numEnter++; + //~if (numEnter > 20) + //~{ + //~printf("something wrong!\n"); + //~} + + } + } + + outputBlob(); +} + +void pix_opencv_trackKnn :: outputBlob(){ + if ( m_taboutput ) + { + //~ put contours in 3 tables. + //~ contours are separated by 0 values + + if ( m_x_arrayname == NULL || m_y_arrayname == NULL || m_id_arrayname == NULL){ + error("please settab before trying to write into..."); + return; + } + + int size(m_trackedBlobs.size()), vecxsize(0), vecysize(0), vecidsize(0); + t_garray *ax, *ay, *aid; + t_word *vecx, *vecy, *vecid; + + //~ check if array exist + if (!(ax = (t_garray *)pd_findbyclass(m_x_arrayname, garray_class))){ + error("%s: no such array", m_x_arrayname->s_name); + return; + } + if (!(ay = (t_garray *)pd_findbyclass(m_y_arrayname, garray_class))){ + error("%s: no such array", m_y_arrayname->s_name); + return; + } + if (!(aid = (t_garray *)pd_findbyclass(m_id_arrayname, garray_class))){ + error("%s: no such array", m_id_arrayname->s_name); + return; + } + + if (!garray_getfloatwords(ax, &vecxsize, &vecx)){ + error("%s: bad template for tabwrite", m_x_arrayname->s_name); + return; + } else if ( vecxsize != size ){ + garray_resize_long(ax,size); + if (!garray_getfloatwords(ax, &vecxsize, &vecx)){ + error("%s: can't resize correctly", m_x_arrayname->s_name); + return; + } + } + + if (!garray_getfloatwords(ay, &vecysize, &vecy)){ + error("%s: bad template for tabwrite", m_y_arrayname->s_name); + return; + } else if ( vecysize != size ){ + garray_resize_long(ay,size); + if (!garray_getfloatwords(ay, &vecysize, &vecy)){ + error("%s: can't resize correctly", m_y_arrayname->s_name); + return; + } + } + + if (!garray_getfloatwords(aid, &vecidsize, &vecid)){ + error("%s: bad template for tabwrite", m_id_arrayname->s_name); + return; + } else if ( vecidsize != size ){ + garray_resize_long(aid,size); + if (!garray_getfloatwords(aid, &vecidsize, &vecid)){ + error("%s: can't resize correctly", m_id_arrayname->s_name); + return; + } + } + + for( size_t i = 0 ; i < m_trackedBlobs.size(); i++ ) + { + vecx[i].w_float = m_trackedBlobs[i].centroid.x; + vecy[i].w_float = m_trackedBlobs[i].centroid.y; + vecid[i].w_float = m_trackedBlobs[i].id; + } + //~ comment the redraw fnt if not needed + garray_redraw(ax); + garray_redraw(ay); + garray_redraw(aid); + + } else { + unsigned int blob_num=m_trackedBlobs.size(); + unsigned int blobMatrixWidth=17; + unsigned int blob_atom_size = 2+blob_num*blobMatrixWidth; + + t_atom* blob_atom = new t_atom[blob_atom_size]; + SETFLOAT(&blob_atom[0], blob_num); + SETFLOAT(&blob_atom[1], blobMatrixWidth); + + for(unsigned int i=0; i newBlobs=m_inputBlobs; + Blob *track = &m_trackedBlobs[blob_index]; + + int winner = -1; //initially label track as '-1'=dead + if((k%2)==0) k++; //if k is not an odd number, add 1 to it + + //if it exists, square the threshold to use as square distance + if(thresh>0) + thresh *= thresh; + + //list of neighbor point index and respective distances + std::list > nbors; + std::list >::iterator iter; + + //find 'k' closest neighbors of testpoint + double x, y, xT, yT, dist; + for(unsigned int i=0; icentroid.x; + yT = track->centroid.y; + dist = (x-xT)*(x-xT)+(y-yT)*(y-yT); + + if(dist<=thresh)//it's good, apply label if no label yet and return + { + winner = i; + return winner; + } + + /**************************************************************** + * check if this blob is closer to the point than what we've seen + *so far and add it to the index/distance list if positive + ****************************************************************/ + + //search the list for the first point with a longer distance + for(iter=nbors.begin(); iter!=nbors.end() + && dist>=iter->second; iter++); + + if((iter!=nbors.end())||(nbors.size()(i, dist)); + //too many items in list, get rid of farthest neighbor + if(nbors.size()>k) + nbors.pop_back(); + } + } + + /******************************************************************** + * we now have k nearest neighbors who cast a vote, and the majority + * wins. we use each class average distance to the target to break any + * possible ties. + *********************************************************************/ + + // a mapping from labels (IDs) to count/distance + std::map > votes; + + //remember: + //iter->first = index of newBlob + //iter->second = distance of newBlob to current tracked blob + for(iter=nbors.begin(); iter!=nbors.end(); iter++) + { + //add up how many counts each neighbor got + int count = ++(votes[iter->first].first); + double dist = (votes[iter->first].second+=iter->second); + + /* check for a possible tie and break with distance */ + if(count>votes[winner].first || ( count==votes[winner].first && distfirst; + } + } + + return winner; +} + +void pix_opencv_trackKnn :: taboutputMess(float arg) +{ + m_taboutput = arg > 0; +} + +void pix_opencv_trackKnn :: tabinputMess(float arg) +{ + m_tabinput = arg > 0; +} + +void pix_opencv_trackKnn :: tableMess(t_symbol *s, int argc, t_atom* argv) +{ + if ( argc < 3 ){ + error("table message need at least 3 argumments"); + return; + } + + for ( int i = 0 ; i < argc ; i++ ){ + if ( argv[i].a_type != A_SYMBOL ){ + error("table message takes only symbol arguments, at most 4 at least 3"); + return; + } + } + + if ( argc == 3 && argv[0].a_type == A_SYMBOL && argv[1].a_type == A_SYMBOL && argv[2].a_type == A_SYMBOL ){ + + // check if arrays exist + m_x_arrayname = argv[0].a_w.w_symbol; + m_y_arrayname = argv[1].a_w.w_symbol; + m_z_arrayname = argv[2].a_w.w_symbol; + } + + if ( argc > 3 ){ + m_id_arrayname= argv[0].a_w.w_symbol; + m_x_arrayname = argv[1].a_w.w_symbol; + m_y_arrayname = argv[2].a_w.w_symbol; + m_z_arrayname = argv[3].a_w.w_symbol; + } + m_tabinput = 1; +} -- cgit v1.2.1