| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- """
- 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)
|