benchmark.cpp 13.2 KB
Newer Older
1 2
#include <opencv2/core/utility.hpp>
#include <opencv2/tracking.hpp>
VBystricky's avatar
VBystricky committed
3
#include <opencv2/videoio.hpp>
4 5 6 7 8 9
#include <opencv2/highgui.hpp>
#include <iostream>
#include <time.h>
#include <cstring>
#include <climits>

Alex Leontiev's avatar
Alex Leontiev committed
10
const int CMDLINEMAX = 30;
Alex Leontiev's avatar
Alex Leontiev committed
11
      int ASSESS_TILL = INT_MAX;
Alex Leontiev's avatar
Alex Leontiev committed
12
const int LINEMAX = 40;
13 14 15 16 17 18 19 20 21 22

using namespace std;
using namespace cv;

/* TODO:  
            do normalization ala Kalal's assessment protocol for TLD
 */

static Mat image;
static bool paused;
Alex Leontiev's avatar
Alex Leontiev committed
23 24
static bool saveImageKey;
static vector<Scalar> palette;
25

Alex Leontiev's avatar
Alex Leontiev committed
26
void print_table(char* videos[],int videoNum,char* algorithms[],int algNum,const vector<vector<char*> >& results,char* tableName);
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

static int lineToRect(char* line,Rect2d& res){
  char * ptr=line,*pos=ptr;
  if(line==NULL || line[0]=='\0'){
      return -1;
  }
  if(strcmp(line,"NaN,NaN,NaN,NaN\n")==0){
      res.height=res.width=-1.0;
      return 0;
  }

  double nums[4]={0};
  for(int i=0; i<4 && (ptr=strpbrk(ptr,"0123456789-"))!= NULL;i++,ptr=pos){
    nums[i]=strtod(ptr,&pos);
    if(pos==ptr){
      printf("lineToRect had problems with decoding line %s\n",line);
      return -1;
    }
  }
  res.x=cv::min(nums[0],nums[2]);
  res.y=cv::min(nums[1],nums[3]);
  res.width=cv::abs(nums[0]-nums[2]);
  res.height=cv::abs(nums[1]-nums[3]);
  return 0;
}
static inline double overlap(Rect2d r1,Rect2d r2){
    if(r1.width<0 || r2.width<0 || r1.height<0 || r1.width<0)return -1.0;
    double a1=r1.area(), a2=r2.area(), a0=(r1&r2).area();
    return a0/(a1+a2-a0);
}
static void help(){
  cout << "\nThis example shows the functionality of \"Long-term optical tracking API\""
       "-- pause video [p] and draw a bounding box around the target to start the tracker\n"
       "Example of <video_name> is in opencv_extra/testdata/cv/tracking/\n"
       "Call:\n"
       "./tracker [<keys and args>] <video_name> <ground_truth> <algorithm1> <init_box1> <algorithm2> <init_box2> ...\n"
       << endl;

Alex Leontiev's avatar
Alex Leontiev committed
65 66 67 68
  cout << "\n\nConsole keys: \n"
       "\t-s - save images\n"
       "\t-l=100 - assess only, say, first 100 frames\n";

69 70 71 72 73 74
  cout << "\n\nHot keys: \n"
       "\tq - quit the program\n"
       "\tp - pause video\n";
  exit(EXIT_SUCCESS);
}
static void parseCommandLineArgs(int argc, char** argv,char* videos[],char* gts[],
Alex Leontiev's avatar
Alex Leontiev committed
75
        int* vc,char* algorithms[],char* initBoxes[][CMDLINEMAX],int* ac,char keys[CMDLINEMAX][LINEMAX]){
76 77 78 79

    *ac=*vc=0;
    for(int i=1;i<argc;i++){
        if(argv[i][0]=='-'){
Alex Leontiev's avatar
Alex Leontiev committed
80
            for(int j=0;j<CMDLINEMAX;j++){
Alex Leontiev's avatar
Alex Leontiev committed
81 82 83 84 85 86 87
                char* ptr = strchr(argv[i], '=');
                if( !strncmp(argv[i], keys[j], (ptr == NULL) ? strlen(argv[i]) : (ptr-argv[i]) ) )
                {
                    if( ptr == NULL )
                        keys[j][0]='\0';
                    else
                        strcpy(keys[j], ptr+1);
Alex Leontiev's avatar
Alex Leontiev committed
88
                }
89 90 91
            }
            continue;
        }
Alex Leontiev's avatar
Alex Leontiev committed
92
        bool isVideo=false;
Alex Leontiev's avatar
Alex Leontiev committed
93
        for(int j=0,len=(int)strlen(argv[i]);j<len;j++){
Alex Leontiev's avatar
Alex Leontiev committed
94 95
            if(!('A'<=argv[i][j] && argv[i][j]<='Z') && argv[i][j]!='.'){
                isVideo=true;
96 97 98
                break;
            }
        }
Alex Leontiev's avatar
Alex Leontiev committed
99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
        if(isVideo){
            videos[*vc]=argv[i];
            i++;
            gts[*vc]=(i<argc)?argv[i]:NULL;
            (*vc)++;
        }else{
            algorithms[*ac]=argv[i];
            i++;
            for(int j=0;j<*vc;j++,i++){
                initBoxes[*ac][j]=(i<argc)?argv[i]:NULL;
            }
            i--;(*ac)++;
        }
    }
}
Alex Leontiev's avatar
Alex Leontiev committed
115 116
void print_table(char* videos[],int videoNum,char* algorithms[],int algNum,const vector<vector<char*> >& results,char* tableName){
    printf("\n%s",tableName);
117 118 119 120 121 122
    vector<int> grid(1+algNum,0);
    char spaces[100];memset(spaces,' ',100);
    for(int i=0;i<videoNum;i++){
        grid[0]=std::max(grid[0],(int)strlen(videos[i]));
    }
    for(int i=0;i<algNum;i++){
Alex Leontiev's avatar
Alex Leontiev committed
123
        grid[i+1]=(int)strlen(algorithms[i]);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
        for(int j=0;j<videoNum;j++)
            grid[i+1]=std::max(grid[i+1],(int)strlen(results[j][i]));
    }
    printf("%.*s ",(int)grid[0],spaces);
    for(int i=0;i<algNum;i++)
        printf("%s%.*s",algorithms[i],(int)(grid[i+1]+1-strlen(algorithms[i])),spaces);
    printf("\n");
    for(int i=0;i<videoNum;i++){
        printf("%s%.*s",videos[i],(int)(grid[0]+1-strlen(videos[i])),spaces);
        for(int j=0;j<algNum;j++)
            printf("%s%.*s",results[i][j],(int)(grid[j+1]+1-strlen(results[i][j])),spaces);
        printf("\n");
    }
    printf("*************************************************************\n");
}

struct AssessmentRes{
    class Assessment{
    public:
        virtual int printf(char* buf)=0;
Alex Leontiev's avatar
Alex Leontiev committed
144
        virtual int printName(char* buf)=0;
145 146 147 148 149 150 151 152 153 154 155 156
        virtual void assess(const Rect2d& ethalon,const Rect2d& res)=0;
        virtual ~Assessment(){}
    };
    AssessmentRes(int algnum);
    int len;
    char* videoName;
    vector<vector<Ptr<Assessment> > >results;
};
class CorrectFrames : public AssessmentRes::Assessment{
public:
    CorrectFrames(double tol):tol_(tol),len_(1),correctFrames_(1){}
    int printf(char* buf){return sprintf(buf,"%d/%d",correctFrames_,len_);}
Alex Leontiev's avatar
Alex Leontiev committed
157
    int printName(char* buf){return sprintf(buf,(char*)"Num of correct frames (overlap>%g)\n",tol_);}
158 159 160 161 162 163 164 165 166 167
    void assess(const Rect2d& ethalon,const Rect2d& res){len_++;if(overlap(ethalon,res)>tol_)correctFrames_++;}
private:
    double tol_;
    int len_;
    int correctFrames_;
};
class AvgTime : public AssessmentRes::Assessment{
public:
    AvgTime(double res):res_(res){}
    int printf(char* buf){return sprintf(buf,"%gms",res_);}
Alex Leontiev's avatar
Alex Leontiev committed
168
    int printName(char* buf){return sprintf(buf,(char*)"Average frame tracking time\n");}
169 170 171 172 173 174 175
    void assess(const Rect2d& /*ethalon*/,const Rect2d&/* res*/){};
private:
    double res_;
};
class PRF : public AssessmentRes::Assessment{
public:
    PRF():occurences_(0),responses_(0),true_responses_(0){};
Alex Leontiev's avatar
Alex Leontiev committed
176
    int printName(char* buf){return sprintf(buf,(char*)"PRF\n");}
177 178 179 180 181 182 183 184 185 186 187 188
    int printf(char* buf){return sprintf(buf,"%g/%g/%g",(1.0*true_responses_)/responses_,(1.0*true_responses_)/occurences_,
            (2.0*true_responses_)/(responses_+occurences_));}
    void assess(const Rect2d& ethalon,const Rect2d& res){
        if(res.height>=0)responses_++;
        if(ethalon.height>=0)occurences_++;
        if(ethalon.height>=0 && res.height>=0)true_responses_++;
    }
private:
    int occurences_,responses_,true_responses_;
};
AssessmentRes::AssessmentRes(int algnum):len(0),results(algnum){
    for(int i=0;i<(int)results.size();i++){
Alex Leontiev's avatar
Alex Leontiev committed
189 190
        results[i].push_back(Ptr<Assessment>(new CorrectFrames(0.0)));
        results[i].push_back(Ptr<Assessment>(new CorrectFrames(0.5)));
191 192 193 194 195 196 197 198 199 200
        results[i].push_back(Ptr<Assessment>(new PRF()));
    }
}

static AssessmentRes assessment(char* video,char* gt_str, char* algorithms[],char* initBoxes_str[],int algnum){
  char buf[200];
  int start_frame=0;
  int linecount=0;
  Rect2d boundingBox;
  vector<double> averageMillisPerFrame(algnum,0.0);
Alex Leontiev's avatar
Alex Leontiev committed
201 202
  static int videoNum=0;
  videoNum++;
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

  FILE* gt=fopen(gt_str,"r");
  if(gt==NULL){
      printf("cannot open the ground truth file %s\n",gt_str);
      exit(EXIT_FAILURE);
  }
  for(linecount=0;fgets(buf,sizeof(buf),gt)!=NULL;linecount++);
  if(linecount==0){
      printf("ground truth file %s has no lines\n",gt_str);
      exit(EXIT_FAILURE);
  }
  fseek(gt,0,SEEK_SET);
  if(fgets(buf,sizeof(buf),gt)==NULL){
      printf("ground truth file %s has no lines\n",gt_str);
      exit(EXIT_FAILURE);
  }

  std::vector<Rect2d> initBoxes(algnum);
  for(int i=0;i<algnum;i++){
      printf("%s %s\n",algorithms[i],initBoxes_str[CMDLINEMAX*i]);
      if(lineToRect(initBoxes_str[CMDLINEMAX*i],boundingBox)<0){
          printf("please, specify bounding box for video %s, algorithm %s\n",video,algorithms[i]);
          printf("FYI, initial bounding box in ground truth is %s\n",buf);
          if(gt!=NULL){
              fclose(gt);
          }
          exit(EXIT_FAILURE);
      }else{
          initBoxes[i].x=boundingBox.x;
          initBoxes[i].y=boundingBox.y;
          initBoxes[i].width=boundingBox.width;
          initBoxes[i].height=boundingBox.height;
      }
  }

  VideoCapture cap;
  cap.open( String(video) );
  cap.set( CAP_PROP_POS_FRAMES, start_frame );

  if( !cap.isOpened() ){
    printf("cannot open video %s\n",video);
    help();
  }

  Mat frame;
  namedWindow( "Tracking API", 1 );

  std::vector<Ptr<Tracker> >trackers(algnum);
  for(int i=0;i<algnum;i++){
      trackers[i] = Tracker::create( algorithms[i] );
      if( trackers[i] == NULL ){
        printf("error in the instantiation of the tracker %s\n",algorithms[i]);
        if(gt!=NULL){
            fclose(gt);
        }
        exit(EXIT_FAILURE);
      }
  }

  cap >> frame;
  frame.copyTo( image );
  if(lineToRect(buf,boundingBox)<0){
      if(gt!=NULL){
          fclose(gt);
      }
      exit(EXIT_FAILURE);
  }
  rectangle( image, boundingBox,palette[0], 2, 1 );
  for(int i=0;i<(int)trackers.size();i++){
      rectangle(image,initBoxes[i],palette[i+1], 2, 1 );
      if( !trackers[i]->init( frame, initBoxes[i] ) ){
        printf("could not initialize tracker %s with box %s at video %s\n",algorithms[i],initBoxes_str[i],video);
        if(gt!=NULL){
            fclose(gt);
        }
        exit(EXIT_FAILURE);
      }
  }
  imshow( "Tracking API", image );

  int frameCounter = 0;
Alex Leontiev's avatar
Alex Leontiev committed
284
  AssessmentRes res((int)trackers.size());
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304

  for ( ;; ){
    if( !paused ){
      cap >> frame;
      if(frame.empty()){
        break;
      }
      frame.copyTo( image );

      if(fgets(buf,sizeof(buf),gt)==NULL){
          printf("ground truth is over\n");
          break;
      }
      if(lineToRect(buf,boundingBox)<0){
          if(gt!=NULL){
              fclose(gt);
          }
          exit(EXIT_FAILURE);
      }
      rectangle( image, boundingBox,palette[0], 2, 1 );
Alex Leontiev's avatar
Alex Leontiev committed
305
      putText(image, "GROUND TRUTH", Point(1,16 + 0*14), FONT_HERSHEY_SIMPLEX, 0.5, palette[0],2);
306 307 308 309 310 311 312 313
      
      frameCounter++;
      for(int i=0;i<(int)trackers.size();i++){
          bool trackerRes=true;
          clock_t start;start=clock();
          trackerRes=trackers[i]->update( frame, initBoxes[i] );
          start=clock()-start;
          averageMillisPerFrame[i]+=1000.0*start/CLOCKS_PER_SEC;
Alex Leontiev's avatar
Alex Leontiev committed
314 315
          if( trackerRes == false )
          {
316
              initBoxes[i].height=initBoxes[i].width=-1.0;
Alex Leontiev's avatar
Alex Leontiev committed
317 318 319
          }
          else
          {
320
              rectangle( image, initBoxes[i], palette[i+1], 2, 1 );
Alex Leontiev's avatar
Alex Leontiev committed
321
              putText(image, algorithms[i], Point(1,16 + (i+1)*14), FONT_HERSHEY_SIMPLEX, 0.5, palette[i+1],2);
322 323 324 325 326
          }
          for(int j=0;j<(int)res.results[i].size();j++)
              res.results[i][j]->assess(boundingBox,initBoxes[i]);
      }
      imshow( "Tracking API", image );
Alex Leontiev's avatar
Alex Leontiev committed
327 328 329 330 331
      if(saveImageKey){
          char inbuf[LINEMAX];
          sprintf(inbuf,"image%d_%d.jpg",videoNum,frameCounter);
          imwrite(inbuf,image);
      }
332

Alex Leontiev's avatar
Alex Leontiev committed
333
      if((frameCounter+1)>=ASSESS_TILL){
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
          break;
      }

      char c = (char) waitKey( 2 );
      if( c == 'q' )
        break;
      if( c == 'p' )
        paused = !paused;
      }
  }
  if(gt!=NULL){
      fclose(gt);
  }
  destroyWindow( "Tracking API");

  res.len=linecount;
  res.videoName=video;
  for(int i=0;i<(int)res.results.size();i++)
      res.results[i].push_back(Ptr<AssessmentRes::Assessment>(new AvgTime(averageMillisPerFrame[i]/res.len)));
  return res;
}

int main( int argc, char** argv ){
Alex Leontiev's avatar
Alex Leontiev committed
357 358 359 360
  palette.push_back(Scalar(255,0,0));//BGR, blue
  palette.push_back(Scalar(0,0,255));//red
  palette.push_back(Scalar(0,255,255));//yellow
  palette.push_back(Scalar(255,255,0));//orange
361 362
  int vcount=0,acount=0;
  char* videos[CMDLINEMAX],*gts[CMDLINEMAX],*algorithms[CMDLINEMAX],*initBoxes[CMDLINEMAX][CMDLINEMAX];
Alex Leontiev's avatar
Alex Leontiev committed
363 364
  char keys[CMDLINEMAX][LINEMAX];
  strcpy(keys[0],"-s");
Alex Leontiev's avatar
Alex Leontiev committed
365 366
  strcpy(keys[1],"-a");

Alex Leontiev's avatar
Alex Leontiev committed
367
  parseCommandLineArgs(argc,argv,videos,gts,&vcount,algorithms,initBoxes,&acount,keys);
Alex Leontiev's avatar
Alex Leontiev committed
368

Alex Leontiev's avatar
Alex Leontiev committed
369
  saveImageKey=(keys[0][0]=='\0');
Alex Leontiev's avatar
Alex Leontiev committed
370 371 372 373
  if( strcmp(keys[1],"-a") != 0 )
      ASSESS_TILL = atoi(keys[1]);
  else
      ASSESS_TILL = INT_MAX;
Alex Leontiev's avatar
Alex Leontiev committed
374

375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
  CV_Assert(acount<CMDLINEMAX && vcount<CMDLINEMAX);
  printf("videos and gts\n");
  for(int i=0;i<vcount;i++){
      printf("%s %s\n",videos[i],gts[i]);
  }
  printf("algorithms and boxes (%d)\n",acount);
  for(int i=0;i<acount;i++){
      printf("%s ",algorithms[i]);
      for(int j=0;j<vcount;j++){
        printf("%s ",initBoxes[i][j]);
      }
      printf("\n");
  }

  std::vector<AssessmentRes> results;
Alex Leontiev's avatar
Alex Leontiev committed
390
  for(int i=0;i<vcount;i++)
391
      results.push_back(assessment(videos[i],gts[i],algorithms,((char**)initBoxes)+i,acount));
Alex Leontiev's avatar
Alex Leontiev committed
392
  CV_Assert( (int)results[0].results[0].size() < CMDLINEMAX );
393 394
  printf("\n\n");

Alex Leontiev's avatar
Alex Leontiev committed
395
  char buf[CMDLINEMAX*CMDLINEMAX*LINEMAX], buf2[CMDLINEMAX*40];
396
  vector<vector<char*> > resultStrings(vcount);
Alex Leontiev's avatar
Alex Leontiev committed
397
  vector<char*> nameStrings;
398 399
  for(int i=0;i<vcount;i++){
      for(int j=0;j<acount;j++){
Alex Leontiev's avatar
Alex Leontiev committed
400
          resultStrings[i].push_back(buf+i*CMDLINEMAX*LINEMAX + j*40);
401 402
      }
  }
Alex Leontiev's avatar
Alex Leontiev committed
403
  for(int i=0;i<(int)results[0].results[0].size();i++)
Alex Leontiev's avatar
Alex Leontiev committed
404
      nameStrings.push_back(buf2+LINEMAX*i);
Alex Leontiev's avatar
Alex Leontiev committed
405 406
  for(int tableCount=0;tableCount<(int)results[0].results[0].size();tableCount++)
  {
Alex Leontiev's avatar
Alex Leontiev committed
407
      CV_Assert(results[0].results[0][tableCount]->printName(nameStrings[tableCount])<LINEMAX);
408 409 410 411
      for(int videoCount=0;videoCount<(int)results.size();videoCount++)
          for(int algoCount=0;algoCount<(int)results[0].results.size();algoCount++){
              (results[videoCount].results[algoCount][tableCount])->printf(resultStrings[videoCount][algoCount]);
          }
Alex Leontiev's avatar
Alex Leontiev committed
412
      print_table(videos,vcount,algorithms,acount,resultStrings,nameStrings[tableCount]);
413 414 415
  }
  return 0;
}