添加注释
This commit is contained in:
27
widerface_evaluate/README.md
Normal file
27
widerface_evaluate/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# WiderFace-Evaluation
|
||||
Python Evaluation Code for [Wider Face Dataset](http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
##### before evaluating ....
|
||||
|
||||
````
|
||||
python3 setup.py build_ext --inplace
|
||||
````
|
||||
|
||||
##### evaluating
|
||||
|
||||
**GroungTruth:** `wider_face_val.mat`, `wider_easy_val.mat`, `wider_medium_val.mat`,`wider_hard_val.mat`
|
||||
|
||||
````
|
||||
python3 evaluation.py -p <your prediction dir> -g <groud truth dir>
|
||||
````
|
||||
|
||||
## Bugs & Problems
|
||||
please issue
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
some code borrowed from Sergey Karayev
|
7813
widerface_evaluate/box_overlaps.c
Normal file
7813
widerface_evaluate/box_overlaps.c
Normal file
File diff suppressed because it is too large
Load Diff
303
widerface_evaluate/evaluation.py
Normal file
303
widerface_evaluate/evaluation.py
Normal file
@ -0,0 +1,303 @@
|
||||
"""
|
||||
WiderFace evaluation code
|
||||
author: wondervictor
|
||||
mail: tianhengcheng@gmail.com
|
||||
copyright@wondervictor
|
||||
"""
|
||||
|
||||
import os
|
||||
import tqdm
|
||||
import pickle
|
||||
import argparse
|
||||
import numpy as np
|
||||
from scipy.io import loadmat
|
||||
from bbox import bbox_overlaps
|
||||
from IPython import embed
|
||||
|
||||
|
||||
def get_gt_boxes(gt_dir):
|
||||
""" gt dir: (wider_face_val.mat, wider_easy_val.mat, wider_medium_val.mat, wider_hard_val.mat)"""
|
||||
|
||||
gt_mat = loadmat(os.path.join(gt_dir, 'wider_face_val.mat'))
|
||||
hard_mat = loadmat(os.path.join(gt_dir, 'wider_hard_val.mat'))
|
||||
medium_mat = loadmat(os.path.join(gt_dir, 'wider_medium_val.mat'))
|
||||
easy_mat = loadmat(os.path.join(gt_dir, 'wider_easy_val.mat'))
|
||||
|
||||
facebox_list = gt_mat['face_bbx_list']
|
||||
event_list = gt_mat['event_list']
|
||||
file_list = gt_mat['file_list']
|
||||
|
||||
hard_gt_list = hard_mat['gt_list']
|
||||
medium_gt_list = medium_mat['gt_list']
|
||||
easy_gt_list = easy_mat['gt_list']
|
||||
|
||||
return facebox_list, event_list, file_list, hard_gt_list, medium_gt_list, easy_gt_list
|
||||
|
||||
|
||||
def get_gt_boxes_from_txt(gt_path, cache_dir):
|
||||
|
||||
cache_file = os.path.join(cache_dir, 'gt_cache.pkl')
|
||||
if os.path.exists(cache_file):
|
||||
f = open(cache_file, 'rb')
|
||||
boxes = pickle.load(f)
|
||||
f.close()
|
||||
return boxes
|
||||
|
||||
f = open(gt_path, 'r')
|
||||
state = 0
|
||||
lines = f.readlines()
|
||||
lines = list(map(lambda x: x.rstrip('\r\n'), lines))
|
||||
boxes = {}
|
||||
print(len(lines))
|
||||
f.close()
|
||||
current_boxes = []
|
||||
current_name = None
|
||||
for line in lines:
|
||||
if state == 0 and '--' in line:
|
||||
state = 1
|
||||
current_name = line
|
||||
continue
|
||||
if state == 1:
|
||||
state = 2
|
||||
continue
|
||||
|
||||
if state == 2 and '--' in line:
|
||||
state = 1
|
||||
boxes[current_name] = np.array(current_boxes).astype('float32')
|
||||
current_name = line
|
||||
current_boxes = []
|
||||
continue
|
||||
|
||||
if state == 2:
|
||||
box = [float(x) for x in line.split(' ')[:4]]
|
||||
current_boxes.append(box)
|
||||
continue
|
||||
|
||||
f = open(cache_file, 'wb')
|
||||
pickle.dump(boxes, f)
|
||||
f.close()
|
||||
return boxes
|
||||
|
||||
|
||||
def read_pred_file(filepath):
|
||||
|
||||
with open(filepath, 'r') as f:
|
||||
lines = f.readlines()
|
||||
img_file = lines[0].rstrip('\n\r')
|
||||
lines = lines[2:]
|
||||
|
||||
# b = lines[0].rstrip('\r\n').split(' ')[:-1]
|
||||
# c = float(b)
|
||||
# a = map(lambda x: [[float(a[0]), float(a[1]), float(a[2]), float(a[3]), float(a[4])] for a in x.rstrip('\r\n').split(' ')], lines)
|
||||
boxes = []
|
||||
for line in lines:
|
||||
line = line.rstrip('\r\n').split(' ')
|
||||
if line[0] == '':
|
||||
continue
|
||||
# a = float(line[4])
|
||||
boxes.append([float(line[0]), float(line[1]), float(line[2]), float(line[3]), float(line[4])])
|
||||
boxes = np.array(boxes)
|
||||
# boxes = np.array(list(map(lambda x: [float(a) for a in x.rstrip('\r\n').split(' ')], lines))).astype('float')
|
||||
return img_file.split('/')[-1], boxes
|
||||
|
||||
|
||||
def get_preds(pred_dir):
|
||||
events = os.listdir(pred_dir)
|
||||
boxes = dict()
|
||||
pbar = tqdm.tqdm(events)
|
||||
|
||||
for event in pbar:
|
||||
pbar.set_description('Reading Predictions ')
|
||||
event_dir = os.path.join(pred_dir, event)
|
||||
event_images = os.listdir(event_dir)
|
||||
current_event = dict()
|
||||
for imgtxt in event_images:
|
||||
imgname, _boxes = read_pred_file(os.path.join(event_dir, imgtxt))
|
||||
current_event[imgname.rstrip('.jpg')] = _boxes
|
||||
boxes[event] = current_event
|
||||
return boxes
|
||||
|
||||
|
||||
def norm_score(pred):
|
||||
""" norm score
|
||||
pred {key: [[x1,y1,x2,y2,s]]}
|
||||
"""
|
||||
|
||||
max_score = 0
|
||||
min_score = 1
|
||||
|
||||
for _, k in pred.items():
|
||||
for _, v in k.items():
|
||||
if len(v) == 0:
|
||||
continue
|
||||
_min = np.min(v[:, -1])
|
||||
_max = np.max(v[:, -1])
|
||||
max_score = max(_max, max_score)
|
||||
min_score = min(_min, min_score)
|
||||
|
||||
diff = max_score - min_score
|
||||
for _, k in pred.items():
|
||||
for _, v in k.items():
|
||||
if len(v) == 0:
|
||||
continue
|
||||
v[:, -1] = (v[:, -1] - min_score)/diff
|
||||
|
||||
|
||||
def image_eval(pred, gt, ignore, iou_thresh):
|
||||
""" single image evaluation
|
||||
pred: Nx5
|
||||
gt: Nx4
|
||||
ignore:
|
||||
"""
|
||||
|
||||
_pred = pred.copy()
|
||||
_gt = gt.copy()
|
||||
pred_recall = np.zeros(_pred.shape[0])
|
||||
recall_list = np.zeros(_gt.shape[0])
|
||||
proposal_list = np.ones(_pred.shape[0])
|
||||
|
||||
_pred[:, 2] = _pred[:, 2] + _pred[:, 0]
|
||||
_pred[:, 3] = _pred[:, 3] + _pred[:, 1]
|
||||
_gt[:, 2] = _gt[:, 2] + _gt[:, 0]
|
||||
_gt[:, 3] = _gt[:, 3] + _gt[:, 1]
|
||||
|
||||
overlaps = bbox_overlaps(_pred[:, :4], _gt)
|
||||
|
||||
for h in range(_pred.shape[0]):
|
||||
|
||||
gt_overlap = overlaps[h]
|
||||
max_overlap, max_idx = gt_overlap.max(), gt_overlap.argmax()
|
||||
if max_overlap >= iou_thresh:
|
||||
if ignore[max_idx] == 0:
|
||||
recall_list[max_idx] = -1
|
||||
proposal_list[h] = -1
|
||||
elif recall_list[max_idx] == 0:
|
||||
recall_list[max_idx] = 1
|
||||
|
||||
r_keep_index = np.where(recall_list == 1)[0]
|
||||
pred_recall[h] = len(r_keep_index)
|
||||
return pred_recall, proposal_list
|
||||
|
||||
|
||||
def img_pr_info(thresh_num, pred_info, proposal_list, pred_recall):
|
||||
pr_info = np.zeros((thresh_num, 2)).astype('float')
|
||||
for t in range(thresh_num):
|
||||
|
||||
thresh = 1 - (t+1)/thresh_num
|
||||
r_index = np.where(pred_info[:, 4] >= thresh)[0]
|
||||
if len(r_index) == 0:
|
||||
pr_info[t, 0] = 0
|
||||
pr_info[t, 1] = 0
|
||||
else:
|
||||
r_index = r_index[-1]
|
||||
p_index = np.where(proposal_list[:r_index+1] == 1)[0]
|
||||
pr_info[t, 0] = len(p_index)
|
||||
pr_info[t, 1] = pred_recall[r_index]
|
||||
return pr_info
|
||||
|
||||
|
||||
def dataset_pr_info(thresh_num, pr_curve, count_face):
|
||||
_pr_curve = np.zeros((thresh_num, 2))
|
||||
for i in range(thresh_num):
|
||||
_pr_curve[i, 0] = pr_curve[i, 1] / pr_curve[i, 0]
|
||||
_pr_curve[i, 1] = pr_curve[i, 1] / count_face
|
||||
return _pr_curve
|
||||
|
||||
|
||||
def voc_ap(rec, prec):
|
||||
|
||||
# correct AP calculation
|
||||
# first append sentinel values at the end
|
||||
mrec = np.concatenate(([0.], rec, [1.]))
|
||||
mpre = np.concatenate(([0.], prec, [0.]))
|
||||
|
||||
# compute the precision envelope
|
||||
for i in range(mpre.size - 1, 0, -1):
|
||||
mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])
|
||||
|
||||
# to calculate area under PR curve, look for points
|
||||
# where X axis (recall) changes value
|
||||
i = np.where(mrec[1:] != mrec[:-1])[0]
|
||||
|
||||
# and sum (\Delta recall) * prec
|
||||
ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])
|
||||
return ap
|
||||
|
||||
|
||||
def evaluation(pred, gt_path, iou_thresh=0.5):
|
||||
pred = get_preds(pred)
|
||||
norm_score(pred)
|
||||
facebox_list, event_list, file_list, hard_gt_list, medium_gt_list, easy_gt_list = get_gt_boxes(gt_path)
|
||||
event_num = len(event_list)
|
||||
thresh_num = 1000
|
||||
settings = ['easy', 'medium', 'hard']
|
||||
setting_gts = [easy_gt_list, medium_gt_list, hard_gt_list]
|
||||
aps = []
|
||||
for setting_id in range(3):
|
||||
# different setting
|
||||
gt_list = setting_gts[setting_id]
|
||||
count_face = 0
|
||||
pr_curve = np.zeros((thresh_num, 2)).astype('float')
|
||||
# [hard, medium, easy]
|
||||
pbar = tqdm.tqdm(range(event_num))
|
||||
for i in pbar:
|
||||
pbar.set_description('Processing {}'.format(settings[setting_id]))
|
||||
event_name = str(event_list[i][0][0])
|
||||
img_list = file_list[i][0]
|
||||
pred_list = pred[event_name]
|
||||
sub_gt_list = gt_list[i][0]
|
||||
# img_pr_info_list = np.zeros((len(img_list), thresh_num, 2))
|
||||
gt_bbx_list = facebox_list[i][0]
|
||||
|
||||
for j in range(len(img_list)):
|
||||
pred_info = pred_list[str(img_list[j][0][0])]
|
||||
|
||||
gt_boxes = gt_bbx_list[j][0].astype('float')
|
||||
keep_index = sub_gt_list[j][0]
|
||||
count_face += len(keep_index)
|
||||
|
||||
if len(gt_boxes) == 0 or len(pred_info) == 0:
|
||||
continue
|
||||
ignore = np.zeros(gt_boxes.shape[0])
|
||||
if len(keep_index) != 0:
|
||||
ignore[keep_index-1] = 1
|
||||
pred_recall, proposal_list = image_eval(pred_info, gt_boxes, ignore, iou_thresh)
|
||||
|
||||
_img_pr_info = img_pr_info(thresh_num, pred_info, proposal_list, pred_recall)
|
||||
|
||||
pr_curve += _img_pr_info
|
||||
pr_curve = dataset_pr_info(thresh_num, pr_curve, count_face)
|
||||
|
||||
propose = pr_curve[:, 0]
|
||||
recall = pr_curve[:, 1]
|
||||
|
||||
ap = voc_ap(recall, propose)
|
||||
aps.append(ap)
|
||||
|
||||
print("==================== Results ====================")
|
||||
print("Easy Val AP: {}".format(aps[0]))
|
||||
print("Medium Val AP: {}".format(aps[1]))
|
||||
print("Hard Val AP: {}".format(aps[2]))
|
||||
print("=================================================")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-p', '--pred', default="./widerface_txt/")
|
||||
parser.add_argument('-g', '--gt', default='./ground_truth/')
|
||||
|
||||
args = parser.parse_args()
|
||||
evaluation(args.pred, args.gt)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
13
widerface_evaluate/setup.py
Normal file
13
widerface_evaluate/setup.py
Normal file
@ -0,0 +1,13 @@
|
||||
"""
|
||||
WiderFace evaluation code
|
||||
author: wondervictor
|
||||
mail: tianhengcheng@gmail.com
|
||||
copyright@wondervictor
|
||||
"""
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
from Cython.Build import cythonize
|
||||
import numpy
|
||||
|
||||
package = Extension('bbox', ['box_overlaps.pyx'], include_dirs=[numpy.get_include()])
|
||||
setup(ext_modules=cythonize([package]))
|
Reference in New Issue
Block a user