torchsight.evaluators.flickr32.fl_eval_retrieval
module
Evaluation script for the FlickrLogos-32 dataset. See http://www.multimedia-computing.de/flickrlogos/ for details.
Please cite the following paper in your work: Scalable Logo Recognition in Real-World Images Stefan Romberg, Lluis Garcia Pueyo, Rainer Lienhart, Roelof van Zwol ACM International Conference on Multimedia Retrieval 2011 (ICMR11), Trento, April 2011.
Author
:Stefan
Romberg
,stefan.romberg
@informatik.uni
-augsburg.de
Notes: - Script was developed/tested on Windows with Python 2.7
$Date: 2013-11-18 11:15:33 +0100 (Mo, 18 Nov 2013) $ $Rev: 7621 $$Date: 2013-11-18 11:15:33 +0100 (Mo, 18 Nov 2013) $ $HeadURL: https://137.250.173.47:8443/svn/romberg/trunk/romberg/research/FlickrLogos-32_SDK/FlickrLogos-32_SDK-1.0.4/scripts/fl_eval_retrieval.py $ $Id: fl_eval_retrieval.py 7621 2013-11-18 10:15:33Z romberg $
Source code
# -*- coding: utf-8 -*-
"""
Evaluation script for the FlickrLogos-32 dataset.
See http://www.multimedia-computing.de/flickrlogos/ for details.
Please cite the following paper in your work:
Scalable Logo Recognition in Real-World Images
Stefan Romberg, Lluis Garcia Pueyo, Rainer Lienhart, Roelof van Zwol
ACM International Conference on Multimedia Retrieval 2011 (ICMR11), Trento, April 2011.
Author: Stefan Romberg, stefan.romberg@informatik.uni-augsburg.de
Notes:
- Script was developed/tested on Windows with Python 2.7
$Date: 2013-11-18 11:15:33 +0100 (Mo, 18 Nov 2013) $
$Rev: 7621 $$Date: 2013-11-18 11:15:33 +0100 (Mo, 18 Nov 2013) $
$HeadURL: https://137.250.173.47:8443/svn/romberg/trunk/romberg/research/FlickrLogos-32_SDK/FlickrLogos-32_SDK-1.0.4/scripts/fl_eval_retrieval.py $
$Id: fl_eval_retrieval.py 7621 2013-11-18 10:15:33Z romberg $
"""
__version__ = "$Id: fl_eval_retrieval.py 7621 2013-11-18 10:15:33Z romberg $"
__author__ = "Stefan Romberg, stefan.romberg@informatik.uni-augsburg.de"
# python built-in modules
import sys
from os.path import exists, basename, split, isdir
from collections import defaultdict
from .flickrlogos import fl_read_groundtruth, fl_read_csv, fl_ap, fl_mean, fl_sdev, Tee
#==============================================================================
#
#==============================================================================
def filename(x):
"""Returns the filename without the directory part including extension."""
return split(x)[1]
def sround(x, arg):
if isinstance(x, float):
return str(round(x, arg))
else:
return str(x)
def fl_read_retrieval_results_format2(result_file):
"""
Reads the retrieval results from an ASCII file.
Format:
1st column: Path to image file, should not contain spaces
2nd column: Score/similarity, higher scores are better.
Other columns: Ignored if present.
The first line contains the query image with an arbitrary score (i.e. 1.0).
"""
lines = fl_read_csv(result_file, delimiters=" \t,;")
# remove first line, contains header = query image
lines = lines[1:]
if len(lines) > 0:
assert len(lines[0]) >= 2
# Returns a list: [ (score, file0, ), (score, file1), ... ]
return [ (float(x[1]), x[0]) for x in lines ]
def fl_process_retrieval_results(results, sort, T_score):
# sort results
if sort == "sort-desc":
results = list(reversed(sorted(results)))
elif sort == "sort-asc":
results = sorted(results)
else:
pass
# keep all files with score > T_score
results = [ (score, imfile) for (score, imfile) in results if score > T_score ]
return results
#==============================================================================
#
#==============================================================================
def fl_eval_retrieval(indir, flickrlogos_dir, verbose):
#==========================================================================
# check input: --flickrlogos
#==========================================================================
if flickrlogos_dir == "":
print("ERROR: fl_eval_retrieval(): Missing ground truth directory (Missing argument --flickrlogos).")
exit(1)
if not exists(flickrlogos_dir):
print("ERROR: fl_eval_retrieval(): Directory given by --flickrlogos does not exist: '"+str(flickrlogos_dir)+"'")
exit(1)
if not flickrlogos_dir.endswith('/') and not flickrlogos_dir.endswith('\\'):
flickrlogos_dir += '/'
gt_trainvalset = flickrlogos_dir + "trainvalset.txt"
gt_testset_logosonly = flickrlogos_dir + "testset-logosonly.txt"
if not exists(gt_trainvalset):
print("ERROR: fl_eval_retrieval(): Ground truth file does not exist: '"+
str(gt_trainvalset)+"' \nWrong directory given by --flickrlogos?")
exit(1)
if not exists(gt_testset_logosonly):
print("ERROR: fl_eval_retrieval(): Ground truth file does not exist: '"+
str(gt_testset_logosonly)+"'\nWrong directory given by --flickrlogos?")
exit(1)
#==========================================================================
# check input: --indir
#==========================================================================
if indir.startswith("file:///"): # for copy-pasting stuff from browser into console
indir = indir[8:]
if not exists(indir):
print("ERROR: fl_eval_retrieval(): Directory given by --indir does not exist: '"+str(indir)+"'")
exit(1)
#==========================================================================
# read and process ground truth
#==========================================================================
gt_indexed, class_names = fl_read_groundtruth(gt_trainvalset)
assert len(class_names) == 33 # 32 logo classes + "no-logo"
assert len(gt_indexed) == 4280 # 32*10 (training set) + 32*30 (validation set)
numImagesIndexed = len(gt_indexed)
gt_queries, class_names = fl_read_groundtruth(gt_testset_logosonly)
assert len(class_names) == 32 # 32 logo classes, logos only
assert len(gt_queries) == 960 # 32*30 (test set)
numQueries = len(gt_queries)
gt_images_per_class = defaultdict(set)
for (im_file, logoclass) in gt_indexed.items():
gt_images_per_class[logoclass].add( basename(im_file) )
#==========================================================================
# now loop over all queries:
# - fetch result list/ranking
# - compute AP, P, R, etc..
#==========================================================================
APs = []
RRs = []
top4s = []
numEmpty = 0
TP = 0
TP_FP = 0
TP_FN = 0
for no, (query_file, logoclass) in enumerate(gt_queries.items()):
query = basename(query_file)
result_file = indir + "/" + query + ".result2.txt"
# current class
cur_class = gt_queries[query]
#print("cur_class:" + cur_class)
if not exists(result_file):
print("ERROR: Missing result file: '"+str(result_file)+"'! Exit.\n")
exit(1)
#----------------------------------------------------------------------
# - read retrieval results
# - sort by descending score
# - remove results with score <= T_score
#----------------------------------------------------------------------
results = fl_read_retrieval_results_format2(result_file)
T_score = 0
results = fl_process_retrieval_results(results, "sort-desc", T_score)
#----------------------------------------------------------------------
# save rank of each item
#----------------------------------------------------------------------
ranked_list = []
for (score, im_file) in results:
ranked_list.append( filename(im_file) )
if len(ranked_list) == 0:
numEmpty += 1
#----------------------------------------------------------------------
# compute mAP
#----------------------------------------------------------------------
pos = gt_images_per_class[cur_class]
assert len(pos) == 40
amb = set() # empty set, no images are ignored
AP = fl_ap(pos, amb, ranked_list)
APs.append(AP)
#----------------------------------------------------------------------
# compute precision + recall, count TP, FP, FN for now
#----------------------------------------------------------------------
tp_set = set(ranked_list).intersection(pos)
P = 0.0
if len(ranked_list) != 0:
P = float(len(tp_set)) / float(len(ranked_list))
R = 0.0
if len(pos) != 0:
R = float(len(tp_set)) / float(len(pos))
TP += len(tp_set)
TP_FP += len(ranked_list)
TP_FN += len(pos)
#----------------------------------------------------------------------
# compute AverageTop4 score, i.e. P@4 * 4.0
#----------------------------------------------------------------------
count4 = 0
for x in ranked_list[0:4]:
if x in pos:
count4 += 1
top4s.append(count4)
#----------------------------------------------------------------------
# compute response ratio (RR)
#----------------------------------------------------------------------
RR = float(len(results)) / float(numImagesIndexed)
RRs.append(RR)
if verbose:
sys.stderr.write("\r Processing retrieval result file "+str(no+1)+"/"+str(len(gt_queries))+" ...")
if verbose:
print(" Done")
#==========================================================================
# DONE
#==========================================================================
assert len(APs) == numQueries, (len(APs), numQueries)
assert len(top4s) == numQueries, (len(top4s), numQueries)
assert len(RRs) == numQueries, (len(RRs), numQueries)
print("-"*79)
print(" Results ")
print("-"*79)
print(" indir: '"+str(indir)+"'")
print("")
print(" Number of queries: "+str(numQueries))
print(" Number of empty result lists: "+str(numEmpty))
prec = 4
mAP = fl_mean(APs)
mAP_sd = fl_sdev(APs)
print(" ==> mean average precision (mAP): "+sround(mAP, prec).ljust(8)+" (stddev: "+sround(mAP_sd, prec)+")")
avgTop4 = fl_mean(top4s)
avgTop4_sd = fl_sdev(top4s)
print(" ==> Avg. top 4 score (4*P@4): "+sround(avgTop4, prec).ljust(8)+" (stddev: "+sround(avgTop4_sd, prec)+")")
mP = float(TP) / float(TP_FP)
print(" ==> mean precision (mP): "+sround(mP, prec).ljust(8))
mR = float(TP) / float(TP_FN)
print(" ==> mean recall (mR):: "+sround(mR, prec).ljust(8))
RR = fl_mean(RRs)
RR_sd = fl_sdev(RRs)
print(" ==> response ratio (RR): "+sround(RR, prec).ljust(8)+" (stddev: "+sround(RR_sd, prec)+")")
#==============================================================================
if __name__ == '__main__': # MAIN
#==============================================================================
from optparse import OptionParser
usage = "Usage: %prog --flickrlogos=<dataset root dir> --indir=<directory with result files> "
parser = OptionParser(usage=usage)
parser.add_option("--flickrlogos", dest="flickrlogos", type=str, default=None,
help="Root directory of the FlickrLogos-32 dataset.\n")
parser.add_option("--indir", dest="indir", type=str, default=None,
help="Directory holding the retrieval result files (*.results2.txt).")
parser.add_option("-o","--output", dest="output", type=str, default="-",
help="Optional: Output file, may be '-' for stdout. Default: stdout \n""")
parser.add_option("-v","--verbose", dest="verbose", action="store_true", default=False,
help="Optional: Flag for verbose output. Default: False\n""")
(options, args) = parser.parse_args()
if len(sys.argv) < 2:
parser.print_help()
exit(1)
#==========================================================================
# show passed args
#==========================================================================
if options.verbose:
print("fl_eval_retrieval.py\n"+__version__)
print("-"*79)
print("ARGS:")
print("FlickrLogos root dir (--flickrlogos):")
print(" > '"+options.flickrlogos+"'")
print("Directory with result files (--indir):")
print(" > '"+options.indir+"'")
print("Output file ( --output):")
print(" > '"+options.output+"'")
print("-"*79)
if options.flickrlogos is None or options.flickrlogos == "":
print("Missing argument: --flickrlogos=<FlickrLogos-32 root directory>. Exit.")
exit(1)
if options.indir is None or options.indir == "":
print("Missing argument: --indir=<directory with result files>. Exit.")
exit(1)
#==========================================================================
# if output is a file and not "-" then all print() statements are redirected
# to *both* stdout and a file.
#==========================================================================
if options.output is not None and options.output != "" and options.output != "-":
if isdir(options.output):
print("Invalid argument: Arg --output must denote a file. Exit.")
exit(1)
Tee(options.output, "w")
#==========================================================================
# compute scores
#==========================================================================
fl_eval_retrieval(options.indir, options.flickrlogos, options.verbose)
Functions
def filename(x)
-
Returns the filename without the directory part including extension.
Source code
def filename(x): """Returns the filename without the directory part including extension.""" return split(x)[1]
def fl_eval_retrieval(indir, flickrlogos_dir, verbose)
-
Source code
def fl_eval_retrieval(indir, flickrlogos_dir, verbose): #========================================================================== # check input: --flickrlogos #========================================================================== if flickrlogos_dir == "": print("ERROR: fl_eval_retrieval(): Missing ground truth directory (Missing argument --flickrlogos).") exit(1) if not exists(flickrlogos_dir): print("ERROR: fl_eval_retrieval(): Directory given by --flickrlogos does not exist: '"+str(flickrlogos_dir)+"'") exit(1) if not flickrlogos_dir.endswith('/') and not flickrlogos_dir.endswith('\\'): flickrlogos_dir += '/' gt_trainvalset = flickrlogos_dir + "trainvalset.txt" gt_testset_logosonly = flickrlogos_dir + "testset-logosonly.txt" if not exists(gt_trainvalset): print("ERROR: fl_eval_retrieval(): Ground truth file does not exist: '"+ str(gt_trainvalset)+"' \nWrong directory given by --flickrlogos?") exit(1) if not exists(gt_testset_logosonly): print("ERROR: fl_eval_retrieval(): Ground truth file does not exist: '"+ str(gt_testset_logosonly)+"'\nWrong directory given by --flickrlogos?") exit(1) #========================================================================== # check input: --indir #========================================================================== if indir.startswith("file:///"): # for copy-pasting stuff from browser into console indir = indir[8:] if not exists(indir): print("ERROR: fl_eval_retrieval(): Directory given by --indir does not exist: '"+str(indir)+"'") exit(1) #========================================================================== # read and process ground truth #========================================================================== gt_indexed, class_names = fl_read_groundtruth(gt_trainvalset) assert len(class_names) == 33 # 32 logo classes + "no-logo" assert len(gt_indexed) == 4280 # 32*10 (training set) + 32*30 (validation set) numImagesIndexed = len(gt_indexed) gt_queries, class_names = fl_read_groundtruth(gt_testset_logosonly) assert len(class_names) == 32 # 32 logo classes, logos only assert len(gt_queries) == 960 # 32*30 (test set) numQueries = len(gt_queries) gt_images_per_class = defaultdict(set) for (im_file, logoclass) in gt_indexed.items(): gt_images_per_class[logoclass].add( basename(im_file) ) #========================================================================== # now loop over all queries: # - fetch result list/ranking # - compute AP, P, R, etc.. #========================================================================== APs = [] RRs = [] top4s = [] numEmpty = 0 TP = 0 TP_FP = 0 TP_FN = 0 for no, (query_file, logoclass) in enumerate(gt_queries.items()): query = basename(query_file) result_file = indir + "/" + query + ".result2.txt" # current class cur_class = gt_queries[query] #print("cur_class:" + cur_class) if not exists(result_file): print("ERROR: Missing result file: '"+str(result_file)+"'! Exit.\n") exit(1) #---------------------------------------------------------------------- # - read retrieval results # - sort by descending score # - remove results with score <= T_score #---------------------------------------------------------------------- results = fl_read_retrieval_results_format2(result_file) T_score = 0 results = fl_process_retrieval_results(results, "sort-desc", T_score) #---------------------------------------------------------------------- # save rank of each item #---------------------------------------------------------------------- ranked_list = [] for (score, im_file) in results: ranked_list.append( filename(im_file) ) if len(ranked_list) == 0: numEmpty += 1 #---------------------------------------------------------------------- # compute mAP #---------------------------------------------------------------------- pos = gt_images_per_class[cur_class] assert len(pos) == 40 amb = set() # empty set, no images are ignored AP = fl_ap(pos, amb, ranked_list) APs.append(AP) #---------------------------------------------------------------------- # compute precision + recall, count TP, FP, FN for now #---------------------------------------------------------------------- tp_set = set(ranked_list).intersection(pos) P = 0.0 if len(ranked_list) != 0: P = float(len(tp_set)) / float(len(ranked_list)) R = 0.0 if len(pos) != 0: R = float(len(tp_set)) / float(len(pos)) TP += len(tp_set) TP_FP += len(ranked_list) TP_FN += len(pos) #---------------------------------------------------------------------- # compute AverageTop4 score, i.e. P@4 * 4.0 #---------------------------------------------------------------------- count4 = 0 for x in ranked_list[0:4]: if x in pos: count4 += 1 top4s.append(count4) #---------------------------------------------------------------------- # compute response ratio (RR) #---------------------------------------------------------------------- RR = float(len(results)) / float(numImagesIndexed) RRs.append(RR) if verbose: sys.stderr.write("\r Processing retrieval result file "+str(no+1)+"/"+str(len(gt_queries))+" ...") if verbose: print(" Done") #========================================================================== # DONE #========================================================================== assert len(APs) == numQueries, (len(APs), numQueries) assert len(top4s) == numQueries, (len(top4s), numQueries) assert len(RRs) == numQueries, (len(RRs), numQueries) print("-"*79) print(" Results ") print("-"*79) print(" indir: '"+str(indir)+"'") print("") print(" Number of queries: "+str(numQueries)) print(" Number of empty result lists: "+str(numEmpty)) prec = 4 mAP = fl_mean(APs) mAP_sd = fl_sdev(APs) print(" ==> mean average precision (mAP): "+sround(mAP, prec).ljust(8)+" (stddev: "+sround(mAP_sd, prec)+")") avgTop4 = fl_mean(top4s) avgTop4_sd = fl_sdev(top4s) print(" ==> Avg. top 4 score (4*P@4): "+sround(avgTop4, prec).ljust(8)+" (stddev: "+sround(avgTop4_sd, prec)+")") mP = float(TP) / float(TP_FP) print(" ==> mean precision (mP): "+sround(mP, prec).ljust(8)) mR = float(TP) / float(TP_FN) print(" ==> mean recall (mR):: "+sround(mR, prec).ljust(8)) RR = fl_mean(RRs) RR_sd = fl_sdev(RRs) print(" ==> response ratio (RR): "+sround(RR, prec).ljust(8)+" (stddev: "+sround(RR_sd, prec)+")")
def fl_process_retrieval_results(results, sort, T_score)
-
Source code
def fl_process_retrieval_results(results, sort, T_score): # sort results if sort == "sort-desc": results = list(reversed(sorted(results))) elif sort == "sort-asc": results = sorted(results) else: pass # keep all files with score > T_score results = [ (score, imfile) for (score, imfile) in results if score > T_score ] return results
def fl_read_retrieval_results_format2(result_file)
-
Reads the retrieval results from an ASCII file.
Format: 1st column: Path to image file, should not contain spaces 2nd column: Score/similarity, higher scores are better. Other columns: Ignored if present. The first line contains the query image with an arbitrary score (i.e. 1.0).
Source code
def fl_read_retrieval_results_format2(result_file): """ Reads the retrieval results from an ASCII file. Format: 1st column: Path to image file, should not contain spaces 2nd column: Score/similarity, higher scores are better. Other columns: Ignored if present. The first line contains the query image with an arbitrary score (i.e. 1.0). """ lines = fl_read_csv(result_file, delimiters=" \t,;") # remove first line, contains header = query image lines = lines[1:] if len(lines) > 0: assert len(lines[0]) >= 2 # Returns a list: [ (score, file0, ), (score, file1), ... ] return [ (float(x[1]), x[0]) for x in lines ]
def sround(x, arg)
-
Source code
def sround(x, arg): if isinstance(x, float): return str(round(x, arg)) else: return str(x)