#!/usr/bin/perl # This software was produced by NIST, an agency of the # U.S. government, and by statute is not subject to copyright in the # United States. Recipients of this software assume all # responsibilities associated with its operation, modification and # maintenance. # Usage: Usage: evalLLf.pl [-d] file1 ..., -d to get precise details # Evaluation script: calculates truePositives, falseNegatives, falsePositives, trueNegatives # of low level features (pan, tilt and zoom) # 1) Read truth data for features $fn(n=1-3) into a hash %td (key = "fn_shotId", value="0|1") # 2) Counts true data for each $fn in %td in %relT (key = fn, value=Pn) # 3) Counts false data for each $fn in %td in %falT (key = fn, value=Qn) # 4) Read submission data for features fn into a hash %sd for shotIds defined in %td (key = fn_shotId, value="1") # 5) Counts true positive (number of true data for each fn in %sd and %td) in %truePos (key = fn, value=Rn) # 6) Counts true negative (number of false data for each fn in %td and doesn't exist in %sd) in @tn (index=fn, Sn) # 7) Counts false positive (number of false data for each fn in %td and exist and true in %sd) in %falsePos (key = fn, value=Tn) # 8) Counts false negative (number of true data for each fn in %td and doesn't exist in %sd) in @fn (index=fn, values=Vn) # 9) Calculate precision and recall for each feature # # foreach f in %relT # precision = $truePos{$fn}/($truePos{$fn}+$falsePos{$fn}); # recall = $truePos{$fn}/$relT{$fn}; # # tzveta, September 20, 2005 use warnings; use strict; @ARGV > 0 or die "Usage: evalLLf.pl [-d] file1 ...\n"; my $flag="-t"; # flag set to default "-t" for table sumary output when script is run without "-d" option if ($ARGV[0] eq "-d"){ # if detailed output is requested (script run with "-d" option) $flag = shift(@ARGV); # change default flag to "-d" and remove its value from argument list }; my %td; # truth data hash my %sd; # submit data hash (only existing data in %td is considered) my %relT; # count true data in %td per feature my %falT; # count false data in %td per feature my %truePos; # count true data in %sd per feature (data with same key in %td must have value 1(true)) my %falsePos; # count false true data in %sd per feature (data with same key in %td must have value 0(false)) my $key; ############ CHANGE THIS PATH ACCORDING YOUR llf.truth FILE LOCATION ############# my $truthData = "/home/barney1/trecvid/5/llf/llftruth/tv5.llf.truth"; open (TRUTHDATA, "$truthData") || die "cannot open $truthData for reading: $!"; # Loop thru truth data (llf.truth) file to: # Fill truth data hash %td according llf.truth file. Feature number (fN, N=1-3) concatenated with shotId is used for key. # Count number of true shots ( value 1 in %td ) per feature in hash table %relT. Feature number (1-3) is used as key. # Count number of false shots ( value 0 in %td ) per feature in hash table %falT. Feature number (1-3) is used as key. while (){ if ( my ($f, $shot1, $shot2, $value) = /^(\d+)\s+ shot(\d+)_(\d+)\s+(\d+)\s*$/){ my $shotId = "shot".$shot1."_".$shot2; my $k1 = $f."_".$shotId; # key $td{$k1}=$value; # for each key put the corresponding value ("0" or "1") if ($value eq 1){ # if true # initialize true data counter in %td to "0" (used only to get rid on undefined warning in the output file) if (!defined($relT{$f})){$relT{$f}=0} $relT{$f}++; # count number of true shots per feature in truth data } else { if (!defined($falT{$f})){$falT{$f}=0} # initialize false data counter in %td to "0" $falT{$f}++; # count number of false shots per feature in truth data } } } close (TRUTHDATA) || die "can't close $truthData $!"; my $fN; # Loop thru submission files for my $fname (@ARGV){ open (IN, $fname) || die "cannot open $fname for reading: $!"; # when detailed output is requested (script run with option -d ) # print total shots in truth data per feature # print total TRUE shots in truth data per feature if ($flag eq "-d"){ print("Distribution of data:\n---------------------\n"); foreach $key (sort(keys (%relT))) { # for each feature print("total feature ", $key, ":\t", $relT{$key}+$falT{$key},"\n"); # how much is the total data per feature print("relevant feature ", $key, ":\t", $relT{$key},"\n"); # how much is the total true data per feature } } my($sysId,$id,$priority); # Read each line in current file and extract various pieces of info # needed for the evaluation while (my $line = ) { # Parse out the sysId if ($line =~ /sysId\s*=\s*"([^"]+)/) { $sysId = $1; } # Parse out the priority if ($line =~ /\s*priority="([^"]+)"/) { $priority = $1; $id=$sysId." "."priority"." ".$priority; # concatenate sysId with priority $fname = substr($fname,rindex($fname,"/")+1,100); # extract the file name from the path if ($flag eq "-d"){print ("\nFile: ",$fname,"\t","Run: ",$id,"\n")} $fN=0; %sd=(); # submit data hash is made empty for each new run %truePos=(); # number of true positive data per feature is made empty for each new run %falsePos=(); # number of false positive data per feature is made empty for each new run } if ($line =~ /fNum\s*=\s*"([^"]+)"/){$fN = $1} # Parse out the shot id if ($line =~ /shotId\s*=\s*"([^"]+)"/){ my $shot = $1; my $k2 = $fN."_".$shot; # same format of key as in %td # if jugged eg. exist in %td and doesn't exist in %sd (this to avoid duplicates) if ((defined($td{$k2})) && (!defined($sd{$k2}))){ $sd{$k2}=1; # fill with value 1, the key in the submit data hash %sd if ($td{$k2} eq 1){ # if hits eg. has value "1" also in %td if (!defined($truePos{$fN})){$truePos{$fN}=0} # get rid of the undefined warning $truePos{$fN}++; # increment the true submit data per feature counter } if ($td{$k2} eq 0){ # false positive eg. has value "0" in %td if (!defined($falsePos{$fN})){$falsePos{$fN}=0} # get rid of the undefined warning $falsePos{$fN}++; # increment the false positive submit data per feature counter } } else {} #print($fN,"\t",$shot," ignored\n\n")} } # When a result is complete calculate precision and recall and print them out if ($line =~ /\/videoCameraMotionDetectionRunResult/ ){ my (@tn,@fn,@prec,@rec); my ($avgPr, $avgRec) = 0; foreach $key (sort(keys (%relT))) { # for each feature #set false positive counter to zero when there aren't false positive shots in the result if (!defined($falsePos{$key})){$falsePos{$key}=0} $prec[$key]=$truePos{$key}/($truePos{$key}+$falsePos{$key}); # precision (per feature) $rec[$key]=$truePos{$key}/$relT{$key}; #recall (per feature) if ($flag eq "-d"){ printf("\n\nPrecision for feature %1d : %5.3f\n",$key, $prec[$key]); printf("Recall for feature %1d : %5.3f\n\n",$key, $rec[$key]); } $avgPr += $prec[$key]; # $truePos{$key}/($truePos{$key}+$falsePos{$key}); # sum(precision(fN)), N=3 $avgRec += $rec[$key]; # $truePos{$key}/$relT{$key}; # sum(recall(fN)), N=3 my $search=$key; # number of feature (1 .. 3) if ($flag eq "-d"){print("TRUE POSITIVE: (", $truePos{$key}, ")\n---------------------\n")} foreach $key (sort (keys (%td))) { if (substr($key,0,1) eq $search){ # extract the number of feature from the key # if true in %td and exist and true in %sd print the shot name if (($flag eq "-d") && ($td{$key} eq 1) && (defined($sd{$key}))){print(substr($key,2,20),"\n")} } } if ($flag eq "-d"){print("\nTRUE NEGATIVE:\t")} $tn[$search]=0; #initializing true negative's counter foreach $key (sort(keys (%td))) { if (substr($key,0,1) eq $search){ # count true negative shots ( false in %td and doesn't exist in %sd ) if (($td{$key} eq 0) && (!defined($sd{$key}))){$tn[$search]++} } } if ($flag eq "-d"){ print("(",$tn[$search],")\n----------------------\n"); foreach $key (sort(keys (%td))) { if (substr($key,0,1) eq $search){ # print true negative ( false in %td and doesn't exist in %sd ) shot name if (($td{$key} eq 0) && (!defined($sd{$key}))){print(substr($key,2,20),"\n")} } } print("\nFALSE POSITIVE: (", $falsePos{$key}, ")\n---------------------\n"); foreach $key (sort(keys (%td))) { if (substr($key,0,1) eq $search){ # print false positive ( false in %td and exist and true in %sd ) shot name if (($td{$key} eq 0) && (defined($sd{$key}))){print(substr($key,2,20),"\n")} } } print("\nFALSE NEGATIVE:\t"); } $fn[$search]=0; #initializing false negative's counter foreach $key (sort(keys (%td))) { if (substr($key,0,1) eq $search){ # count false negative shots ( true in %td and doesn't exist in %sd ) if (($td{$key} eq 1) && (!defined($sd{$key}))){$fn[$search]++} } } if ($flag eq "-d"){ print("(",$fn[$search],")\n----------------------\n"); foreach $key (sort(keys (%td))) { if (substr($key,0,1) eq $search){ # print false negative ( true in %td and doesn't exist in %sd ) shot name if (($td{$key} eq 1) && (!defined($sd{$key}))){print(substr($key,2,20),"\n")} } } } } $avgPr /= 3; # mean precision: sum(precision(fN)/N), N=3 $avgRec /= 3; # mean recall sum(recall(fN)/N), N=3 if ($flag eq "-d"){ printf("\n\nMEAN PRECISION: %5.3f\n", $avgPr); printf("MEAN RACALL: %5.3f\n\n", $avgRec); } # When a result is complete and no detailed results were requested print them out in default (table) output if ($flag eq "-t"){ my $old = select STDOUT; $= = 70; # changing the default page length (60 lines) to 70 lines select $old; write; format STDOUT = @<<<<<<<<<<<@ @### @### @### @### @.### @.### @### @### @### @### @.### @.### @### @### @### @### @.### @.### @.### @.### $sysId,$priority,$truePos{1},$tn[1],$falsePos{1},$fn[1],$prec[1],$rec[1],$truePos{2},$tn[2],$falsePos{2},$fn[2],$prec[2],$rec[2],$truePos{3},$tn[3],$falsePos{3},$fn[3],$prec[3],$rec[3],$avgPr,$avgRec . } } } close(IN) || die "can't close $fname: $!"; } format STDOUT_TOP = | P A N * ( f1 ) | T I L T * ( f2 ) | Z O O M * ( f3 ) | MEAN ============= ==== ==== ==== ==== ===== ===== ==== ==== ==== ==== ===== ===== ==== ==== ==== ==== ===== ===== ===== ===== sysId true true fal. fal. prec. recal true true fal. fal. prec. recal true true fal. fal. prec. recal PREC. RECAL priority | + - + - | + - + - | + - + - | ============= ==== ==== ==== ==== ===== ===== ==== ==== ==== ==== ===== ===== ==== ==== ==== ==== ===== ===== ===== ===== .