KITTI tracking evaluation code in python
"""
function that does the evaluation
input:
- result_sha (sha key where the results are located
- mail (messenger object for output messages sent via email and to cout)
output:
- True if at least one of the sub-benchmarks could be processed successfully
- False otherwise
data:
- at this point the submitted files are located in results/<result_sha>/data
- the results shall be saved as follows
-> summary statistics of the method: results/<result_sha>/stats_task.txt
here task refers to the sub-benchmark (e.g., um_lane, uu_road etc.)
file contents: numbers for main table, format: %.6f (single space separated)
note: only files with successful sub-benchmark evaluation must be created
-> detailed results/graphics/plots: results/<result_sha>/subdir
with appropriate subdir and file names (all subdir's need to be created)
"""
import sys,os,copy,math
from munkres import Munkres
from collections import defaultdict
try:
from ordereddict import OrderedDict
except:
from collections import OrderedDict
import mailpy
class tData:
"""
Utility class to load data.
"""
def __init__(self,frame=-1,obj_type="unset",truncation=-1,occlusion=-1,\
obs_angle=-10,x1=-1,y1=-1,x2=-1,y2=-1,w=-1,h=-1,l=-1,\
X=-1000,Y=-1000,Z=-1000,yaw=-10,score=-1000,track_id=-1):
"""
Constructor, initializes the object given the parameters.
"""
self.frame = frame
self.track_id = track_id
self.obj_type = obj_type
self.truncation = truncation
self.occlusion = occlusion
self.obs_angle = obs_angle
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.w = w
self.h = h
self.l = l
self.X = X
self.Y = Y
self.Z = Z
self.yaw = yaw
self.score = score
self.ignored = False
self.valid = False
self.tracker = -1
def __str__(self):
"""
Print read data.
"""
attrs = vars(self)
return '\n'.join("%s: %s" % item for item in attrs.items())
class trackingEvaluation(object):
""" tracking statistics (CLEAR MOT, id-switches, fragments, ML/PT/MT, precision/recall)
MOTA - Multi-object tracking accuracy in [0,100]
MOTP - Multi-object tracking precision in [0,100] (3D) / [td,100] (2D)
MOTAL - Multi-object tracking accuracy in [0,100] with log10(id-switches)
id-switches - number of id switches
fragments - number of fragmentations
MT, PT, ML - number of mostly tracked, partially tracked and mostly lost trajectories
recall - recall = percentage of detected targets
precision - precision = percentage of correctly detected targets
FAR - number of false alarms per frame
falsepositives - number of false positives (FP)
missed - number of missed targets (FN)
"""
def __init__(self, t_sha, gt_path="./tools/eval_kitti_track/data/tracking",\
split_version='', min_overlap=0.5, max_truncation = 0, min_height = 25,
max_occlusion = 2, mail=None, cls="car"):
filename_test_mapping = "./tools/eval_kitti_track/data/tracking/" + \
"evaluate_tracking{}.seqmap".format(split_version)
self.n_frames = []
self.sequence_name = []
with open(filename_test_mapping, "r") as fh:
for i,l in enumerate(fh):
fields = l.split(" ")
self.sequence_name.append("%04d" % int(fields[0]))
self.n_frames.append(int(fields[3]) - int(fields[2])+1)
fh.close()
self.n_sequences = i+1
self.mail = mail
self.cls = cls
if 'half' in split_version:
self.gt_path = os.path.join(
gt_path, 'label_02_{}'.format(split_version))
else:
self.gt_path = os.path.join(gt_path, "label_02")
self.t_sha = t_sha
self.t_path = os.path.join(t_sha)
self.n_gt = 0
self.n_igt = 0
self.n_gts = []
self.n_igts = []
self.n_gt_trajectories = 0
self.n_gt_seq = []
self.n_tr = 0
self.n_trs = []
self.n_itr = 0
self.n_itrs = []
self.n_igttr = 0
self.n_tr_trajectories = 0
self.n_tr_seq = []
self.MOTA = 0
self.MOTP = 0
self.MOTAL = 0
self.MODA = 0
self.MODP = 0
self.MODP_t = []
self.recall = 0
self.precision = 0
self.F1 = 0
self.FAR = 0
self.total_cost = 0
self.itp = 0
self.itps = []
self.tp = 0
self.tps = []
self.fn = 0
self.fns = []
self.ifn = 0
self.ifns = []
self.fp = 0
self.fps = []
self.mme = 0
self.fragments = 0
self.id_switches = 0
self.MT = 0
self.PT = 0
self.ML = 0
self.min_overlap = min_overlap
self.max_truncation = max_truncation
self.max_occlusion = max_occlusion
self.min_height = min_height
self.n_sample_points = 500
self.gt_trajectories = [[] for x in range(self.n_sequences)]
self.ign_trajectories = [[] for x in range(self.n_sequences)]
def createEvalDir(self):
"""
Creates directory to store evaluation results and data for visualization.
"""
self.eval_dir = os.path.join(self.t_sha, "eval", self.cls)
if not os.path.exists(self.eval_dir):
print("create directory:", self.eval_dir,)
os.makedirs(self.eval_dir)
print("done")
def loadGroundtruth(self):
"""
Helper function to load ground truth.
"""
try:
self._loadData(self.gt_path, cls=self.cls, loading_groundtruth=True)
except IOError:
return False
return True
def loadTracker(self):
"""
Helper function to load tracker data.
"""
try:
if not self._loadData(self.t_path, cls=self.cls, loading_groundtruth=False):
return False
except IOError:
return False
return True
def _loadData(self, root_dir, cls, min_score=-1000, loading_groundtruth=False):
"""
Generic loader for ground truth and tracking data.
Use loadGroundtruth() or loadTracker() to load this data.
Loads detections in KITTI format from textfiles.
"""
t_data = tData()
data = []
eval_2d = True
eval_3d = True
seq_data = []
n_trajectories = 0
n_trajectories_seq = []
for seq, s_name in enumerate(self.sequence_name):
i = 0
filename = os.path.join(root_dir, "%s.txt" % s_name)
f = open(filename, "r")
f_data = [[] for x in range(self.n_frames[seq])]
ids = []
n_in_seq = 0
id_frame_cache = []
for line in f:
line = line.strip()
fields = line.split(" ")
if "car" in cls.lower():
classes = ["car","van"]
elif "pedestrian" in cls.lower():
classes = ["pedestrian","person_sitting"]
else:
classes = [cls.lower()]
classes += ["dontcare"]
if not any([s for s in classes if s in fields[2].lower()]):
continue
t_data.frame = int(float(fields[0]))
t_data.track_id = int(float(fields[1]))
t_data.obj_type = fields[2].lower()
t_data.truncation = int(float(fields[3]))
t_data.occlusion = int(float(fields[4]))
t_data.obs_angle = float(fields[5])
t_data.x1 = float(fields[6])
t_data.y1 = float(fields[7])
t_data.x2 = float(fields[8])
t_data.y2 = float(fields[9])
t_data.h = float(fields[10])
t_data.w = float(fields[11])
t_data.l = float(fields[12])
t_data.X = float(fields[13])
t_data.Y = float(fields[14])
t_data.Z = float(fields[15])
t_data.yaw = float(fields[16])
if not loading_groundtruth:
if len(fields) == 17:
t_data.score = -1
elif len(fields) == 18:
t_data.score = float(fields[17])
else:
self.mail.msg("file is not in KITTI format")
return
if t_data.track_id is -1 and t_data.obj_type != "dontcare":
continue
idx = t_data.frame
if idx >= len(f_data):
print("extend f_data", idx, len(f_data))
f_data += [[] for x in range(max(500, idx-len(f_data)))]
try:
id_frame = (t_data.frame,t_data.track_id)
if id_frame in id_frame_cache and not loading_groundtruth:
self.mail.msg("track ids are not unique for sequence %d: frame %d" % (seq,t_data.frame))
self.mail.msg("track id %d occured at least twice for this frame" % t_data.track_id)
self.mail.msg("Exiting...")
return False
id_frame_cache.append(id_frame)
f_data[t_data.frame].append(copy.copy(t_data))
except:
print(len(f_data), idx)
raise
if t_data.track_id not in ids and t_data.obj_type!="dontcare":
ids.append(t_data.track_id)
n_trajectories +=1
n_in_seq +=1
if not loading_groundtruth and eval_2d is True and(t_data.x1==-1 or t_data.x2==-1 or t_data.y1==-1 or t_data.y2==-1):
eval_2d = False
if not loading_groundtruth and eval_3d is True and(t_data.X==-1000 or t_data.Y==-1000 or t_data.Z==-1000):
eval_3d = False
n_trajectories_seq.append(n_in_seq)
seq_data.append(f_data)
f.close()
if not loading_groundtruth:
self.tracker=seq_data
self.n_tr_trajectories=n_trajectories
self.eval_2d = eval_2d
self.eval_3d = eval_3d
self.n_tr_seq = n_trajectories_seq
if self.n_tr_trajectories==0:
return False
else:
self.dcareas = []
self.groundtruth = []
for seq_idx in range(len(seq_data)):
seq_gt = seq_data[seq_idx]
s_g, s_dc = [],[]
for f in range(len(seq_gt)):
all_gt = seq_gt[f]
g,dc = [],[]
for gg in all_gt:
if gg.obj_type=="dontcare":
dc.append(gg)
else:
g.append(gg)
s_g.append(g)
s_dc.append(dc)
self.dcareas.append(s_dc)
self.groundtruth.append(s_g)
self.n_gt_seq=n_trajectories_seq
self.n_gt_trajectories=n_trajectories
return True
def boxoverlap(self,a,b,criterion="union"):
"""
boxoverlap computes intersection over union for bbox a and b in KITTI format.
If the criterion is 'union', overlap = (a inter b) / a union b).
If the criterion is 'a', overlap = (a inter b) / a, where b should be a dontcare area.
"""
x1 = max(a.x1, b.x1)
y1 = max(a.y1, b.y1)
x2 = min(a.x2, b.x2)
y2 = min(a.y2, b.y2)
w = x2-x1
h = y2-y1
if w<=0. or h<=0.:
return 0.
inter = w*h
aarea = (a.x2-a.x1) * (a.y2-a.y1)
barea = (b.x2-b.x1) * (b.y2-b.y1)
if criterion.lower()=="union":
o = inter / float(aarea+barea-inter)
elif criterion.lower()=="a":
o = float(inter) / float(aarea)
else:
raise TypeError("Unkown type for criterion")
return o
def compute3rdPartyMetrics(self):
"""
Computes the metrics defined in
- Stiefelhagen 2008: Evaluating Multiple Object Tracking Performance: The CLEAR MOT Metrics
MOTA, MOTAL, MOTP
- Nevatia 2008: Global Data Association for Multi-Object Tracking Using Network Flows
MT/PT/ML
"""
hm = Munkres()
max_cost = 1e9
fr, ids = 0,0
for seq_idx in range(len(self.groundtruth)):
seq_gt = self.groundtruth[seq_idx]
seq_dc = self.dcareas[seq_idx]
seq_tracker = self.tracker[seq_idx]
seq_trajectories = defaultdict(list)
seq_ignored = defaultdict(list)
seqtp = 0
seqitp = 0
seqfn = 0
seqifn = 0
seqfp = 0
seqigt = 0
seqitr = 0
last_ids = [[],[]]
n_gts = 0
n_trs = 0
for f in range(len(seq_gt)):
g = seq_gt[f]
dc = seq_dc[f]
t = seq_tracker[f]
self.n_gt += len(g)
self.n_tr += len(t)
n_gts += len(g)
n_trs += len(t)
cost_matrix = []
this_ids = [[],[]]
for gg in g:
this_ids[0].append(gg.track_id)
this_ids[1].append(-1)
gg.tracker = -1
gg.id_switch = 0
gg.fragmentation = 0
cost_row = []
for tt in t:
c = 1-self.boxoverlap(gg,tt)
if c<=self.min_overlap:
cost_row.append(c)
else:
cost_row.append(max_cost)
cost_matrix.append(cost_row)
seq_trajectories[gg.track_id].append(-1)
seq_ignored[gg.track_id].append(False)
if len(g) is 0:
cost_matrix=[[]]
association_matrix = hm.compute(cost_matrix)
tmptp = 0
tmpfp = 0
tmpfn = 0
tmpc = 0
tmpcs = [0]*len(g)
for row,col in association_matrix:
c = cost_matrix[row][col]
if c < max_cost:
g[row].tracker = t[col].track_id
this_ids[1][row] = t[col].track_id
t[col].valid = True
g[row].distance = c
self.total_cost += 1-c
tmpc += 1-c
tmpcs[row] = 1-c
seq_trajectories[g[row].track_id][-1] = t[col].track_id
self.tp += 1
tmptp += 1
else:
g[row].tracker = -1
self.fn += 1
tmpfn += 1
nignoredtracker = 0
ignoredtrackers = dict()
for tt in t:
ignoredtrackers[tt.track_id] = -1
tt_height = abs(tt.y1 - tt.y2)
if ((self.cls=="car" and tt.obj_type=="van") or (self.cls=="pedestrian" and tt.obj_type=="person_sitting") or tt_height<=self.min_height) and not tt.valid:
nignoredtracker+= 1
tt.ignored = True
ignoredtrackers[tt.track_id] = 1
continue
for d in dc:
overlap = self.boxoverlap(tt,d,"a")
if overlap>0.5 and not tt.valid:
tt.ignored = True
nignoredtracker+= 1
ignoredtrackers[tt.track_id] = 1
break
ignoredfn = 0
nignoredtp = 0
nignoredpairs = 0
gi = 0
for gg in g:
if gg.tracker < 0:
if gg.occlusion>self.max_occlusion or gg.truncation>self.max_truncation\
or (self.cls=="car" and gg.obj_type=="van") or (self.cls=="pedestrian" and gg.obj_type=="person_sitting"):
seq_ignored[gg.track_id][-1] = True
gg.ignored = True
ignoredfn += 1
elif gg.tracker>=0:
if gg.occlusion>self.max_occlusion or gg.truncation>self.max_truncation\
or (self.cls=="car" and gg.obj_type=="van") or (self.cls=="pedestrian" and gg.obj_type=="person_sitting"):
seq_ignored[gg.track_id][-1] = True
gg.ignored = True
nignoredtp += 1
if ignoredtrackers[gg.tracker] > 0:
nignoredpairs += 1
tmpc -= tmpcs[gi]
gi += 1
tmptp -= nignoredtp
self.itp += nignoredtp
self.n_gt -= (ignoredfn + nignoredtp)
self.n_igt += ignoredfn + nignoredtp
self.n_itr += nignoredtracker
self.n_igttr += nignoredpairs
tmpfn += len(g)-len(association_matrix)-ignoredfn
self.fn += len(g)-len(association_matrix)-ignoredfn
self.ifn += ignoredfn
tmpfp += len(t) - tmptp - nignoredtracker - nignoredtp + nignoredpairs
self.fp += len(t) - tmptp - nignoredtracker - nignoredtp + nignoredpairs
seqtp += tmptp
seqitp += nignoredtp
seqfp += tmpfp
seqfn += tmpfn
seqifn += ignoredfn
seqigt += ignoredfn + nignoredtp
seqitr += nignoredtracker
if tmptp<0:
print(tmptp, nignoredtp)
raise NameError("Something went wrong! TP is negative")
if tmpfn<0:
print(tmpfn, len(g), len(association_matrix), ignoredfn, nignoredpairs)
raise NameError("Something went wrong! FN is negative")
if tmpfp<0:
print(tmpfp, len(t), tmptp, nignoredtracker, nignoredtp, nignoredpairs)
raise NameError("Something went wrong! FP is negative")
if tmptp + tmpfn is not len(g)-ignoredfn-nignoredtp:
print("seqidx", seq_idx)
print("frame ", f)
print("TP ", tmptp)
print("FN ", tmpfn)
print("FP ", tmpfp)
print("nGT ", len(g))
print("nAss ", len(association_matrix))
print("ign GT", ignoredfn)
print("ign TP", nignoredtp)
raise NameError("Something went wrong! nGroundtruth is not TP+FN")
if tmptp+tmpfp+nignoredtp+nignoredtracker-nignoredpairs is not len(t):
print(seq_idx, f, len(t), tmptp, tmpfp)
print(len(association_matrix), association_matrix)
raise NameError("Something went wrong! nTracker is not TP+FP")
for i,tt in enumerate(this_ids[0]):
if tt in last_ids[0]:
idx = last_ids[0].index(tt)
tid = this_ids[1][i]
lid = last_ids[1][idx]
if tid != lid and lid != -1 and tid != -1:
if g[i].truncation<self.max_truncation:
g[i].id_switch = 1
ids +=1
if tid != lid and lid != -1:
if g[i].truncation<self.max_truncation:
g[i].fragmentation = 1
fr +=1
last_ids = this_ids
MODP_t = 1
if tmptp!=0:
MODP_t = tmpc/float(tmptp)
self.MODP_t.append(MODP_t)
self.gt_trajectories[seq_idx] = seq_trajectories
self.ign_trajectories[seq_idx] = seq_ignored
self.n_gts.append(n_gts)
self.n_trs.append(n_trs)
self.tps.append(seqtp)
self.itps.append(seqitp)
self.fps.append(seqfp)
self.fns.append(seqfn)
self.ifns.append(seqifn)
self.n_igts.append(seqigt)
self.n_itrs.append(seqitr)
n_ignored_tr_total = 0
for seq_idx, (seq_trajectories,seq_ignored) in enumerate(zip(self.gt_trajectories, self.ign_trajectories)):
if len(seq_trajectories)==0:
continue
tmpMT, tmpML, tmpPT, tmpId_switches, tmpFragments = [0]*5
n_ignored_tr = 0
for g, ign_g in zip(seq_trajectories.values(), seq_ignored.values()):
if all(ign_g):
n_ignored_tr+=1
n_ignored_tr_total+=1
continue
if all([this==-1 for this in g]):
tmpML+=1
self.ML+=1
continue
last_id = g[0]
tracked = 1 if g[0]>=0 else 0
lgt = 0 if ign_g[0] else 1
for f in range(1,len(g)):
if ign_g[f]:
last_id = -1
continue
lgt+=1
if last_id != g[f] and last_id != -1 and g[f] != -1 and g[f-1] != -1:
tmpId_switches += 1
self.id_switches += 1
if f < len(g)-1 and g[f-1] != g[f] and last_id != -1 and g[f] != -1 and g[f+1] != -1:
tmpFragments += 1
self.fragments += 1
if g[f] != -1:
tracked += 1
last_id = g[f]
if len(g)>1 and g[f-1] != g[f] and last_id != -1 and g[f] != -1 and not ign_g[f]:
tmpFragments += 1
self.fragments += 1
tracking_ratio = tracked / float(len(g) - sum(ign_g))
if tracking_ratio > 0.8:
tmpMT += 1
self.MT += 1
elif tracking_ratio < 0.2:
tmpML += 1
self.ML += 1
else:
tmpPT += 1
self.PT += 1
if (self.n_gt_trajectories-n_ignored_tr_total)==0:
self.MT = 0.
self.PT = 0.
self.ML = 0.
else:
self.MT /= float(self.n_gt_trajectories-n_ignored_tr_total)
self.PT /= float(self.n_gt_trajectories-n_ignored_tr_total)
self.ML /= float(self.n_gt_trajectories-n_ignored_tr_total)
if (self.fp+self.tp)==0 or (self.tp+self.fn)==0:
self.recall = 0.
self.precision = 0.
else:
self.recall = self.tp/float(self.tp+self.fn)
self.precision = self.tp/float(self.fp+self.tp)
if (self.recall+self.precision)==0:
self.F1 = 0.
else:
self.F1 = 2.*(self.precision*self.recall)/(self.precision+self.recall)
if sum(self.n_frames)==0:
self.FAR = "n/a"
else:
self.FAR = self.fp/float(sum(self.n_frames))
if self.n_gt==0:
self.MOTA = -float("inf")
self.MODA = -float("inf")
else:
self.MOTA = 1 - (self.fn + self.fp + self.id_switches)/float(self.n_gt)
self.MODA = 1 - (self.fn + self.fp) / float(self.n_gt)
if self.tp==0:
self.MOTP = float("inf")
else:
self.MOTP = self.total_cost / float(self.tp)
if self.n_gt!=0:
if self.id_switches==0:
self.MOTAL = 1 - (self.fn + self.fp + self.id_switches)/float(self.n_gt)
else:
self.MOTAL = 1 - (self.fn + self.fp + math.log10(self.id_switches))/float(self.n_gt)
else:
self.MOTAL = -float("inf")
if sum(self.n_frames)==0:
self.MODP = "n/a"
else:
self.MODP = sum(self.MODP_t)/float(sum(self.n_frames))
return True
def createSummary(self):
"""
Generate and mail a summary of the results.
If mailpy.py is present, the summary is instead printed.
"""
summary = ""
summary += "tracking evaluation summary".center(80,"=") + "\n"
summary += self.printEntry("Multiple Object Tracking Accuracy (MOTA)", self.MOTA) + "\n"
summary += self.printEntry("Multiple Object Tracking Precision (MOTP)", self.MOTP) + "\n"
summary += self.printEntry("Multiple Object Tracking Accuracy (MOTAL)", self.MOTAL) + "\n"
summary += self.printEntry("Multiple Object Detection Accuracy (MODA)", self.MODA) + "\n"
summary += self.printEntry("Multiple Object Detection Precision (MODP)", self.MODP) + "\n"
summary += "\n"
summary += self.printEntry("Recall", self.recall) + "\n"
summary += self.printEntry("Precision", self.precision) + "\n"
summary += self.printEntry("F1", self.F1) + "\n"
summary += self.printEntry("False Alarm Rate", self.FAR) + "\n"
summary += "\n"
summary += self.printEntry("Mostly Tracked", self.MT) + "\n"
summary += self.printEntry("Partly Tracked", self.PT) + "\n"
summary += self.printEntry("Mostly Lost", self.ML) + "\n"
summary += "\n"
summary += self.printEntry("True Positives", self.tp) + "\n"
summary += self.printEntry("Ignored True Positives", self.itp) + "\n"
summary += self.printEntry("False Positives", self.fp) + "\n"
summary += self.printEntry("False Negatives", self.fn) + "\n"
summary += self.printEntry("ID-switches", self.id_switches) + "\n"
self.fp = self.fp / self.n_gt
self.fn = self.fn / self.n_gt
self.id_switches = self.id_switches / self.n_gt
summary += self.printEntry("False Positives Ratio", self.fp) + "\n"
summary += self.printEntry("False Negatives Ratio", self.fn) + "\n"
summary += self.printEntry("Ignored False Negatives Ratio", self.ifn) + "\n"
summary += self.printEntry("Missed Targets", self.fn) + "\n"
summary += self.printEntry("ID-switches", self.id_switches) + "\n"
summary += self.printEntry("Fragmentations", self.fragments) + "\n"
summary += "\n"
summary += self.printEntry("Ground Truth Objects (Total)", self.n_gt + self.n_igt) + "\n"
summary += self.printEntry("Ignored Ground Truth Objects", self.n_igt) + "\n"
summary += self.printEntry("Ground Truth Trajectories", self.n_gt_trajectories) + "\n"
summary += "\n"
summary += self.printEntry("Tracker Objects (Total)", self.n_tr) + "\n"
summary += self.printEntry("Ignored Tracker Objects", self.n_itr) + "\n"
summary += self.printEntry("Tracker Trajectories", self.n_tr_trajectories) + "\n"
summary += "="*80
return summary
def printEntry(self, key, val,width=(70,10)):
"""
Pretty print an entry in a table fashion.
"""
s_out = key.ljust(width[0])
if type(val)==int:
s = "%%%dd" % width[1]
s_out += s % val
elif type(val)==float:
s = "%%%df" % (width[1])
s_out += s % val
else:
s_out += ("%s"%val).rjust(width[1])
return s_out
def saveToStats(self):
"""
Save the statistics in a whitespace separate file.
"""
summary = self.createSummary()
mail.msg(summary)
filename = os.path.join(self.t_sha, "summary_%s.txt" % self.cls)
dump = open(filename, "w+")
dump.write(summary)
dump.close()
filename = os.path.join(self.t_sha, "stats_%s.txt" % self.cls)
dump = open(filename, "w+")
dump.write("%.6f " * 21 \
% (self.MOTA, self.MOTP, self.MOTAL, self.MODA, self.MODP, \
self.recall, self.precision, self.F1, self.FAR, \
self.MT, self.PT, self.ML, self.tp, self.fp, self.fn, self.id_switches, self.fragments, \
self.n_gt, self.n_gt_trajectories, self.n_tr, self.n_tr_trajectories))
dump.close()
filename = os.path.join(self.t_sha, "description.txt")
dump = open(filename, "w+")
dump.write("MOTA" + "MOTP" + "MOTAL" + "MODA" + "MODP" + "recall" + "precision" + "F1" + "FAR")
dump.write("MT" + "PT" + "ML" + "tp" + "fp" + "fn" + "id_switches" + "fragments")
dump.write("n_gt" + "n_gt_trajectories" + "n_tr" + "n_tr_trajectories")
def evaluate(result_sha,mail, split_version=''):
"""
Entry point for evaluation, will load the data and start evaluation for
CAR and PEDESTRIAN if available.
"""
mail.msg("Processing Result for KITTI Tracking Benchmark")
classes = []
for c in ("car", "pedestrian"):
e = trackingEvaluation(
t_sha=result_sha, mail=mail,cls=c,split_version=split_version)
try:
if not e.loadTracker():
continue
mail.msg("Loading Results - Success")
mail.msg("Evaluate Object Class: %s" % c.upper())
classes.append(c)
except:
mail.msg("Feel free to contact us (lenz@kit.edu), if you receive this error message:")
mail.msg(" Caught exception while loading result data.")
break
if not e.loadGroundtruth():
raise ValueError("Ground truth not found.")
mail.msg("Loading Groundtruth - Success")
if len(e.groundtruth) is not len(e.tracker):
mail.msg("The uploaded data does not provide results for every sequence.")
return False
mail.msg("Loaded %d Sequences." % len(e.groundtruth))
mail.msg("Start Evaluation...")
try:
e.createEvalDir()
except:
mail.msg("Feel free to contact us (lenz@kit.edu), if you receive this error message:")
mail.msg(" Caught exception while creating results.")
if e.compute3rdPartyMetrics():
e.saveToStats()
else:
mail.msg("There seem to be no true positives or false positives at all in the submitted data.")
if len(classes)==0:
mail.msg("The uploaded results could not be evaluated. Check for format errors.")
return False
mail.msg("Thank you for participating in our benchmark!")
return True
if __name__ == "__main__":
result_sha = sys.argv[1]
split_version = sys.argv[2] if len(sys.argv) >= 3 else ''
mail = mailpy.Mail("")
success = evaluate(result_sha,mail,split_version=split_version)
if len(sys.argv)==4: mail.finalize(success,"tracking",result_sha,split_version)
else: mail.finalize(success,"tracking",result_sha,"")
|