Repository for OpenCV's extra modules
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

394 lines
12 KiB

#include <opencv2/core/utility.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
#include <time.h>
#include <cstring>
#include <climits>
const int CMDLINEMAX = 30;
const int ASSESS_TILL = 100;
const int LINEMAX = 40;
using namespace std;
using namespace cv;
/* TODO:
do normalization ala Kalal's assessment protocol for TLD
*/
static Mat image;
static bool paused;
static bool saveImageKey;
static vector<Scalar> palette;
void print_table(char* videos[],int videoNum,char* algorithms[],int algNum,const vector<vector<char*> >& results,char* tableName);
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;
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[],
int* vc,char* algorithms[],char* initBoxes[][CMDLINEMAX],int* ac,char keys[CMDLINEMAX][LINEMAX]){
*ac=*vc=0;
for(int i=1;i<argc;i++){
if(argv[i][0]=='-'){
for(int j=0;j<CMDLINEMAX;j++){
if(!strcmp(argv[i],keys[j])){
keys[j][0]='\0';
}
}
continue;
}
bool isVideo=false;
for(int j=0,len=(int)strlen(argv[i]);j<len;j++){
if(!('A'<=argv[i][j] && argv[i][j]<='Z') && argv[i][j]!='.'){
isVideo=true;
break;
}
}
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)++;
}
}
}
void print_table(char* videos[],int videoNum,char* algorithms[],int algNum,const vector<vector<char*> >& results,char* tableName){
printf("\n%s",tableName);
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++){
grid[i+1]=(int)strlen(algorithms[i]);
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;
virtual int printName(char* buf)=0;
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_);}
int printName(char* buf){return sprintf(buf,(char*)"Num of correct frames (overlap>%g)\n",tol_);}
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_);}
int printName(char* buf){return sprintf(buf,(char*)"Average frame tracking time\n");}
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){};
int printName(char* buf){return sprintf(buf,(char*)"PRF\n");}
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++){
results[i].push_back(Ptr<Assessment>(new CorrectFrames(0.0)));
results[i].push_back(Ptr<Assessment>(new CorrectFrames(0.5)));
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);
static int videoNum=0;
videoNum++;
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;
AssessmentRes res((int)trackers.size());
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 );
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;
if(trackerRes==false){
initBoxes[i].height=initBoxes[i].width=-1.0;
}else{
rectangle( image, initBoxes[i], palette[i+1], 2, 1 );
}
for(int j=0;j<(int)res.results[i].size();j++)
res.results[i][j]->assess(boundingBox,initBoxes[i]);
}
imshow( "Tracking API", image );
if(saveImageKey){
char inbuf[LINEMAX];
sprintf(inbuf,"image%d_%d.jpg",videoNum,frameCounter);
imwrite(inbuf,image);
}
if((frameCounter+1)>=ASSESS_TILL){
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 ){
palette.push_back(Scalar(255,0,0));//BGR
palette.push_back(Scalar(0,0,255));
palette.push_back(Scalar(0,255,255));
int vcount=0,acount=0;
char* videos[CMDLINEMAX],*gts[CMDLINEMAX],*algorithms[CMDLINEMAX],*initBoxes[CMDLINEMAX][CMDLINEMAX];
char keys[CMDLINEMAX][LINEMAX];
strcpy(keys[0],"-s");
parseCommandLineArgs(argc,argv,videos,gts,&vcount,algorithms,initBoxes,&acount,keys);
saveImageKey=(keys[0][0]=='\0');
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;
for(int i=0;i<vcount;i++){
results.push_back(assessment(videos[i],gts[i],algorithms,((char**)initBoxes)+i,acount));
}
CV_Assert( (int)results[0].results[0].size() < CMDLINEMAX );
printf("\n\n");
char buf[CMDLINEMAX*CMDLINEMAX*LINEMAX], buf2[CMDLINEMAX*40];
vector<vector<char*> > resultStrings(vcount);
vector<char*> nameStrings;
for(int i=0;i<vcount;i++){
for(int j=0;j<acount;j++){
resultStrings[i].push_back(buf+i*CMDLINEMAX*LINEMAX + j*40);
}
}
for(int i=0;i<(int)results[0].results[0].size();i++){
nameStrings.push_back(buf2+LINEMAX*i);
}
for(int tableCount=0;tableCount<(int)results[0].results[0].size();tableCount++){
CV_Assert(results[0].results[0][tableCount]->printName(nameStrings[tableCount])<LINEMAX);
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]);
}
print_table(videos,vcount,algorithms,acount,resultStrings,nameStrings[tableCount]);
}
return 0;
}