cocoeval.py的COCOeval类
self._gts = defaultdict(list) # gt for evaluation
self._dts = defaultdict(list) # dt for evaluation
for gt in gts:
self._gts[gt['image_id'], gt['category_id']].append(gt)
for dt in dts:
self._dts[dt['image_id'], dt['category_id']].append(dt)
self.evalImgs = defaultdict(list) # per-image per-category evaluation results
self.eval = {} # accumulated evaluation results
self.ious = {(imgId, catId): computeIoU(imgId, catId) \
for imgId in p.imgIds
for catId in catIds}
然后调用evaluateImg评估每一个类别,每一个区域。每一个id。主要是进行匹配。得到 self.evalImgs:
evaluateImg返回值:
'image_id': imgId,
'category_id': catId,
'aRng': aRng,
'maxDet': maxDet,
'dtIds': [d['id'] for d in dt],
'gtIds': [g['id'] for g in gt],
'dtMatches': dtm,
'gtMatches': gtm,
'dtScores': [d['score'] for d in dt],
'gtIgnore': gtIg,
'dtIgnore': dtIg,
self.evalImgs = [evaluateImg(imgId, catId, areaRng, maxDet)
for catId in catIds
for areaRng in p.areaRng
for imgId in p.imgIds
]
gt = self._gts[imgId,catId]
dt = self._dts[imgId,catId]
gtind = np.argsort([g['_ignore'] for g in gt], kind='mergesort')
gt = [gt[i] for i in gtind]
dtind = np.argsort([-d['score'] for d in dt], kind='mergesort')
dt = [dt[i] for i in dtind[0:maxDet]]
iscrowd = [int(o['iscrowd']) for o in gt]
# load computed ious
ious = self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId]
进行匹配,在给定的阈值下,各阈值互不影响:
对每一个dt,寻找与之最为匹配的gt,
收尾:
设置各种变量,索引
T = len(p.iouThrs)
R = len(p.recThrs)
K = len(p.catIds) if p.useCats else 1
A = len(p.areaRng)
M = len(p.maxDets)
precision = -np.ones((T,R,K,A,M)) # -1 for the precision of absent categories
recall = -np.ones((T,K,A,M))
scores = -np.ones((T,R,K,A,M))
# 遍历所有类别
for k, k0 in enumerate(k_list):
Nk = k0*A0*I0
# 遍历所有Area
for a, a0 in enumerate(a_list):
Na = a0*I0
# 遍历所有MaxDet
for m, maxDet in enumerate(m_list):
# 由于evalImgs的存放顺序是:按照K,A,I(imgId)
# E相当于取出了所有Image在当前k0,当前a0下的evaluate结果
E = [self.evalImgs[Nk + Na + i] for i in i_list]
E = [e for e in E if not e is None]
if len(E) == 0:
continue
# 根据maxDet截取dtScores,并将所有图片的分数合并到一起。
dtScores = np.concatenate([e['dtScores'][0:maxDet] for e in E])
# different sorting method generates slightly different results.
# mergesort is used to be consistent as Matlab implementation.
inds = np.argsort(-dtScores, kind='mergesort')
dtScoresSorted = dtScores[inds]
# 将dt的匹配结果按照score的顺序取出。
# dtm有两个维度。第一个维度为T,代表不同阈值;第二个维度才是匹配结果
# 最后也是将所有图片的结果拼接到一起
dtm = np.concatenate([e['dtMatches'][:,0:maxDet] for e in E], axis=1)[:,inds]
dtIg = np.concatenate([e['dtIgnore'][:,0:maxDet] for e in E], axis=1)[:,inds]
gtIg = np.concatenate([e['gtIgnore'] for e in E])
npig = np.count_nonzero(gtIg==0 )
if npig == 0:
continue
# True Positive就是匹配上
# 这个操作对所有阈值都进行了
tps = np.logical_and( dtm, np.logical_not(dtIg) )
# False Positive就是没有匹配上
fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg) )
# 对tps和fps进行积分,tps是单调递增序列,并且增加幅度最大为1。
tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float)
fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float)
# 对每个阈值,计算Precision
for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)):
tp = np.array(tp)
fp = np.array(fp)
nd = len(tp)
# 计算recall和precision
# 其中recall的值单调递增,序号代表一个一个的dt,值代表在此序号之前的recall是多少
# precision的值不一定。序号也是代表dt。非单调递减
rc = tp / npig
pr = tp / (fp+tp+np.spacing(1))
q = np.zeros((R,))
ss = np.zeros((R,))
# recall就是最大的recall
if nd:
recall[t,k,a,m] = rc[-1]
else:
recall[t,k,a,m] = 0
# numpy is slow without cython optimization for accessing elements
# use python array gets significant speed improvement
pr = pr.tolist(); q = q.tolist()
# 从后往前遍历pr,将pr修剪为单调递减的形状(该形状为包裹住原pr曲线的最小梯形)。
for i in range(nd-1, 0, -1):
if pr[i] > pr[i-1]:
pr[i-1] = pr[i]
# 搜索rec阈值(101个)在rc中的index,并用这个index来获得Precision
inds = np.searchsorted(rc, p.recThrs, side='left')
try:
for ri, pi in enumerate(inds):
q[ri] = pr[pi]
ss[ri] = dtScoresSorted[pi]
except:
pass
# q中存储的就是PR曲线在各个recall阈值的precision值
precision[t,:,k,a,m] = np.array(q)
# scores存储的是dt在各个recall阈值的score值。
scores[t,:,k,a,m] = np.array(ss)