1 #include "opencv2/highgui/highgui.hpp"
 
   2 #include "opencv2/imgproc/imgproc.hpp"
 
  19 #include "stickletrack.h"
 
  22 #define BACKSECONDS 30
 
  30 normalprefs normalPrefs;
 
  32 static VideoCapture capture;
 
  34 static bool stopvideo=false;
 
  35 static bool leftbuttondown=false;
 
  36 static Point2f startClickPos;
 
  37 static int ilatetagintime=-1, iearlytagintime=-1;
 
  38 static int startClickFrame;
 
  39 static bool stoppedbefore;
 
  40 static bool dooneframe=false;
 
  41 static vector<tag> tags;
 
  42 static int tagsselected=0;
 
  43 static int nearestTags[2];
 
  44 static vector<tag> **tagintime;
 
  45 static int tagintimeidx=0;
 
  46 static int moresleep=0;
 
  47 static int gotoframe=0;
 
  48 static int maxoutputfps;
 
  49 static double rescalingfactor;
 
  50 static ofstream tanjaLogFile;
 
  54   Props.basedir = getenv("HOMEDRIVE");
 
  55   Props.basedir += getenv("HOMEPATH");
 
  57   Props.basedir = getenv("HOME");
 
  60   Props.basedir += "/.stickletrack";
 
  62   cout << "Using " << Props.basedir << " for data output." << endl;
 
  65 bool isWindowClosed (const char* name) {
 
  66   if ( cvGetWindowHandle(name) == NULL )
 
  73   cout << "Exitting ..." << endl;
 
  76   _mkdir(Props.basedir.c_str());
 
  77   _mkdir((Props.basedir+"/prefs").c_str());
 
  79   mkdir( Props.basedir.c_str(), 0777 );
 
  80   mkdir( (Props.basedir+"/prefs").c_str(), 0777 );
 
  83   cout << "Saving preferences ..." << endl;
 
  84   int scissorpointsize = Prefs.scissorpoints->size();
 
  85   ofstream prefFile( (Props.basedir+"/prefs/"+Props.videohash+".prf").c_str(), ios::binary );
 
  86   prefFile.write((char*)&Prefs, sizeof(Prefs));
 
  87   prefFile.write((char*)&scissorpointsize, sizeof(int));
 
  89   for (int ipoint = 0; ipoint < scissorpointsize; ipoint++)
 
  90     prefFile.write( (char*)&(*Prefs.scissorpoints)[ipoint], sizeof(Point) );
 
  95 void computeNormalizedPrefs() {
 
  96   normalPrefs.contours_minshortaxis = Prefs.contours_minshortaxis * Props.diagonal / 1000.0;
 
  97   normalPrefs.contours_maxshortaxis = Prefs.contours_maxshortaxis * Props.diagonal / 1000.0;
 
  98   normalPrefs.contours_minlongaxis = Prefs.contours_minlongaxis * Props.diagonal / 500.0;
 
  99   normalPrefs.contours_maxlongaxis = Prefs.contours_maxlongaxis * Props.diagonal / 500.0;
 
 101   normalPrefs.contours_minarea = pow(Prefs.contours_minarea,2) * Props.width * Props.height / 1000000.0;
 
 102   normalPrefs.contours_maxarea = pow(Prefs.contours_maxarea,2) * Props.width * Props.height / 1000000.0;
 
 104   normalPrefs.contours_mincircum = Prefs.contours_mincircum * Props.diagonal / 500.0;
 
 105   normalPrefs.contours_maxcircum = Prefs.contours_maxcircum * Props.diagonal / 500.0;
 
 107   normalPrefs.contours_maxspeed = Prefs.contours_maxspeed * Props.diagonal / 100.0;
 
 109   normalPrefs.contours_maxrot = 10 * Prefs.contours_maxrot;
 
 111   normalPrefs.halfdecay = 10.0 * Prefs.halfdecay / Props.fps;
 
 114 void trackbarCallbackUpdateNormPrefs (int trackpos, void *userdata) {
 
 115   computeNormalizedPrefs();
 
 118 void drawTimes(Mat& mContours) {
 
 123   alles << framenum << " : " << fixed << (framenum)/(double)Props.fps << "s : +" << (double)moresleep/Props.fps << "ms";
 
 125   string text = alles.str();
 
 126   int fontFace = FONT_HERSHEY_DUPLEX;
 
 128   double fontScale = Props.width / 600.0;
 
 129   int thickness = Props.width / 250.0;
 
 132   Size textSize = getTextSize(text, fontFace,
 
 133                               fontScale, thickness, &baseline);
 
 135   Point textOrg(Props.width*0.01, Props.width*0.01 + textSize.height);
 
 137   rectangle(mContours, Point(0, 0),
 
 138             Point(Props.width, Props.width*0.02 + textSize.height),
 
 139             Scalar(0,0,255), -1);
 
 141   putText(mContours, text, textOrg, fontFace, fontScale,
 
 142           Scalar::all(255), thickness, 8);
 
 145 void readCreatePrefs() {
 
 146   ifstream prefFile( (Props.basedir+"/prefs/"+Props.videohash+".prf").c_str(), ios::binary );
 
 147   if ( prefFile.is_open() ) {
 
 148     int scissorpointsize;
 
 150     prefFile.read((char*)&Prefs, sizeof(Prefs));
 
 151     prefFile.read((char*)&scissorpointsize, sizeof(int));
 
 153     Prefs.scissorpoints = new vector<Point>;
 
 154     for (int ipoint = 0; ipoint < scissorpointsize; ipoint++) {
 
 156       prefFile.read((char*)&tmpPoint, sizeof(Point));
 
 157       Prefs.scissorpoints->push_back(tmpPoint);
 
 162     cout << "Loaded saved preferences." << endl;
 
 165     Prefs.scissorpoints = new vector<Point>;
 
 169 void loadDefaultPrefs () {
 
 170   Prefs.halfdecay = 100;
 
 171   Prefs.forethreshold = 0;
 
 172   Prefs.mincolor[0] = 0; Prefs.mincolor[1] = 0; Prefs.mincolor[2] = 0;
 
 173   Prefs.maxcolor[0] = 255; Prefs.maxcolor[1] = 255; Prefs.maxcolor[2] = 255;
 
 175   Prefs.contours_minshortaxis = 0;
 
 176   Prefs.contours_maxshortaxis = 100;
 
 177   Prefs.contours_minlongaxis = 0;
 
 178   Prefs.contours_maxlongaxis = 100;
 
 179   Prefs.contours_minarea = 0;
 
 180   Prefs.contours_maxarea = 100;
 
 181   Prefs.contours_mincircum = 0;
 
 182   Prefs.contours_maxcircum = 100;
 
 183   Prefs.contours_maxspeed = 100;
 
 184   Prefs.contours_maxrot = 100;
 
 187 unsigned long frameHash(const Mat& frame) {
 
 188   unsigned long hash = 5381;
 
 190   unsigned char *str = (unsigned char*)frame.data;
 
 193     hash = ((hash << 5) + hash) + c;
 
 199 int timeWarp(int x, int y) {
 
 200   int horiMove = startClickPos.x - x;
 
 201   int wannago = startClickFrame + 10*horiMove*Props.fps/Props.width;
 
 203   int maxgo = min(ilatetagintime, Props.totframes);
 
 204   int mingo = max(1, ilatetagintime - BACKSECONDS*Props.fps + 2);
 
 208   else if (wannago < mingo)
 
 214 int rotatingTime( int whereyouwant ) {
 
 215   if ( whereyouwant < 0 )
 
 216     return BACKSECONDS*Props.fps + whereyouwant;
 
 221 void mouseTracking(int evt, int x, int y, int flags, void* param){
 
 222   if ( stopvideo && evt == CV_EVENT_LBUTTONDBLCLK ) {
 
 226     for (int itag = 0; itag < tags.size(); itag++) {
 
 227       double dist = pow(tags[itag].x - x, 2) + pow(tags[itag].y - y, 2);
 
 228       if ( dist < mindist || mindist == -1 ) {
 
 229         nearestTags[tagsselected-1] = itag;
 
 234     if (tagsselected == 2) {
 
 235       if ( nearestTags[0] < tags.size() && nearestTags[0] >= 0
 
 236            && nearestTags[1] < tags.size() && nearestTags[1] >= 0
 
 237            && nearestTags[0] != nearestTags[1] ) {
 
 241         tmp = tags[nearestTags[0]];
 
 242         tags[nearestTags[0]] = tags[nearestTags[1]];
 
 243         tags[nearestTags[1]] = tmp;
 
 244         tags[nearestTags[1]].color = tags[nearestTags[0]].color;
 
 245         tags[nearestTags[0]].color = tmp.color;
 
 247         for (int itagintime = framenum-1; itagintime <= ilatetagintime; itagintime++) {
 
 248           tmp = (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[0]];
 
 250           (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[0]] = 
 
 251             (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[1]];
 
 253           (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[1]] = tmp;
 
 255           (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[1]].color = 
 
 256             (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[0]].color;
 
 258           (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-itagintime) - 1) ])[nearestTags[0]].color = tmp.color;
 
 263       gotoframe = framenum;
 
 268   if ( evt == CV_EVENT_RBUTTONUP ) {
 
 269     stopvideo = (stopvideo+1)%2;
 
 272   if (evt==CV_EVENT_LBUTTONDOWN) {
 
 273     leftbuttondown = true;
 
 274     startClickPos = Point2f(x,y);
 
 275     startClickFrame = framenum;
 
 278       stoppedbefore = true;
 
 280       stoppedbefore = false;
 
 285   if (evt==CV_EVENT_LBUTTONUP) {
 
 286     leftbuttondown = false;
 
 288     int vertiMove = startClickPos.y - y;
 
 290     if ( ! stoppedbefore )
 
 294   if ( leftbuttondown && startClickPos.x - x != 0  && ! (startClickPos.x > Props.width*5.0/6.0 && x > Props.width*5.0/6.0) ) {
 
 295     gotoframe = timeWarp(x, y);
 
 299   if ( leftbuttondown && startClickPos.y - y != 0) {
 
 300     int vertiMove = startClickPos.y - y;
 
 302     if ( startClickPos.x > Props.width*5.0/6.0 && x > Props.width*5.0/6.0 ) {
 
 303       moresleep -= vertiMove*10000/Props.height;
 
 307       drawTimes(tracking_getFrame());
 
 312 void writeTanjaLog (int writeframenum, vector<tag> *tagstowrite) {
 
 313   stringstream outline;
 
 315   if ( writeframenum%(Props.fps/maxoutputfps) == 0 ) {
 
 316     if ( tagstowrite->size() >= Prefs.manyfish ) {
 
 317       outline << (double)writeframenum/Props.fps;
 
 318       for ( int itag = 0; itag < Prefs.manyfish; itag++) {
 
 319         outline << "," << (*tagstowrite)[itag].x << "," << (*tagstowrite)[itag].y << "," << (double)writeframenum/Props.fps-(*tagstowrite)[itag].lastseen;
 
 322       tanjaLogFile.write( outline.str().c_str(), outline.str().size() );
 
 323       tanjaLogFile.flush();
 
 328 void openTanjaLog () {
 
 329   stringstream tanjalogfilename;
 
 332   _mkdir(Props.basedir.c_str());
 
 333   _mkdir((Props.basedir+"/logs").c_str());
 
 335   mkdir( Props.basedir.c_str(), 0777 );
 
 336   mkdir( (Props.basedir+"/logs").c_str(), 0777 );
 
 340   _mkdir(Props.basedir.c_str());
 
 341   _mkdir((Props.basedir+"/logs/"+Props.videohash).c_str());
 
 343   mkdir( Props.basedir.c_str(), 0777 );
 
 344   mkdir( (Props.basedir+"/logs/"+Props.videohash).c_str(), 0777 );
 
 347   tanjalogfilename << Props.basedir << "/logs/"  << Props.videohash << "/" << time(0) << ".dat";
 
 348   tanjaLogFile.open( tanjalogfilename.str().c_str() );
 
 350   if ( ! tanjaLogFile.is_open() ) {
 
 351     cerr << "Could not open tanjalogfile " << tanjalogfilename.str() << "!" << endl;
 
 355   tanjaLogFile.write("# STICKLETRACK - TANJALOG\n", 26);
 
 356   tanjaLogFile.write("# format: <time in s>,<tag_1 xpos>,<tag_1 ypos>,<tag_1 losttime>,...,<tag_n xpos>,<tag_n ypos>,<tag_n losttime>\n", 112);
 
 358   tanjaLogFile.flush();
 
 361 int process(VideoCapture& capture) {
 
 364   bool pleaseExit = false;
 
 366   namedWindow("stickletrack_original", CV_WINDOW_KEEPRATIO);
 
 368   Mat frame, origframe, combinedmask;
 
 370   Props.fps = capture.get(CV_CAP_PROP_FPS);
 
 371   Props.width = capture.get(CV_CAP_PROP_FRAME_WIDTH)*rescalingfactor;
 
 372   Props.height = capture.get(CV_CAP_PROP_FRAME_HEIGHT)*rescalingfactor;
 
 373   Props.totframes = capture.get(CV_CAP_PROP_FRAME_COUNT);
 
 374   Props.diagonal = sqrt( pow(Props.width, 2) + pow(Props.height, 2) );
 
 376   if ( Props.width == 0 || Props.height == 0 || Props.fps == 0 || Props.totframes == 0 ) {
 
 377     cerr << "Something got wrong while reading video-file info!" << endl;
 
 378     cerr << "Width: " << Props.width << endl;
 
 379     cerr << "Height: " << Props.height << endl;
 
 380     cerr << "FPS: " << Props.fps << endl;
 
 381     cerr << "Total frames: " << Props.totframes << endl;
 
 385   Mat frameintime[BACKSECONDS*Props.fps];
 
 387   tagintime = new vector<tag>*[BACKSECONDS*Props.fps];
 
 389   for (int iback = 0; iback < BACKSECONDS*Props.fps; iback++)
 
 390     tagintime[iback] = new vector<tag>;
 
 397   stringstream tmphash;
 
 398   tmphash << frameHash(frame);
 
 399   Props.videohash = tmphash.str();
 
 404   computeNormalizedPrefs();
 
 409   tracking_init(&mouseTracking);
 
 411   capture.set(CV_CAP_PROP_POS_FRAMES, 0);
 
 413   for (; !pleaseExit;) {
 
 414     if ( !stopvideo || dooneframe ) {
 
 415       framenum = gotoframe;
 
 419       frametime = (framenum-1.0) / Props.fps;
 
 421       if ( framenum <= ilatetagintime ) {
 
 422         origframe = frameintime[ rotatingTime(tagintimeidx - (ilatetagintime-framenum) - 2) ];
 
 423         masking_getCombinedMask(origframe, combinedmask);
 
 426         for (int itag = 0; itag < tagintime[ rotatingTime( tagintimeidx - 2 - (ilatetagintime-framenum) ) ]->size(); itag++)
 
 427           tags.push_back( (*tagintime[ rotatingTime(tagintimeidx - (ilatetagintime-framenum) - 2) ])[itag] );
 
 429         tracking_locateTags (tags, combinedmask);
 
 437         frame.convertTo(origframe, CV_32FC3);
 
 438         resize(origframe, origframe, Size(0,0), rescalingfactor, rescalingfactor);
 
 440         masking_getCombinedMask(origframe, combinedmask);
 
 442         tracking_locateTags (tags, combinedmask);
 
 444         tagintime[tagintimeidx]->clear();
 
 445         for (int itag = 0; itag < tags.size(); itag++)
 
 446           tagintime[tagintimeidx]->push_back(tags[itag]);
 
 448         frameintime[tagintimeidx] = origframe;
 
 450         ilatetagintime = framenum;
 
 451         tagintimeidx = (tagintimeidx+1)%(BACKSECONDS*Props.fps);
 
 453         if ( ilatetagintime >= BACKSECONDS*Props.fps-1 )
 
 454           writeTanjaLog( framenum - (BACKSECONDS*Props.fps-1), tagintime[ tagintimeidx ] );
 
 457       gotoframe = framenum + 1;
 
 459       if ( ! isWindowClosed("stickletrack_original") )
 
 460         imshow("stickletrack_original", origframe/255.0);
 
 463     drawTimes(tracking_getFrame());
 
 465     if ( tagsselected == 1 ) {
 
 466       circle( tracking_getFrame(), Point2f(tags[nearestTags[0]].x, tags[nearestTags[0]].y), Props.diagonal / 100.0, Scalar(0,0,255), -1, 8 );
 
 469     if ( tracking_showFrame() )
 
 475       key = (char)waitKey(5 + (double)moresleep/Props.fps);
 
 477       key = (char)waitKey(5);
 
 489   if ( ilatetagintime >= BACKSECONDS*Props.fps-1 ) {
 
 490     int timeidx = (tagintimeidx+1)%(BACKSECONDS*Props.fps);
 
 491     for ( int itime = 0; itime < BACKSECONDS*Props.fps-1; itime++ ) {
 
 492       writeTanjaLog( framenum+1+itime - (BACKSECONDS*Props.fps-1), tagintime[ timeidx ] );
 
 493       timeidx = (timeidx+1)%(BACKSECONDS*Props.fps);
 
 497     for ( int itime = 0; itime < ilatetagintime+1; itime++ )
 
 498       writeTanjaLog( itime, tagintime[ itime ] );
 
 501   tanjaLogFile.close();
 
 505   cout << "Bye bye." << endl;
 
 510 int main(int ac, char** av) {
 
 512     cout << "Usage: stickletrack <maxoutputfps> <rescalingfactor> <videofilename>" << endl;
 
 516   maxoutputfps = atoi( av[1] );
 
 517   rescalingfactor = atof( av[2] );
 
 519   capture.open( av[3] ); 
 
 523    if (!capture.isOpened()) {
 
 524     cerr << "Failed to open a video device or video file!\n" << endl;
 
 528   return process(capture);