|
|
|
import numpy as np |
|
|
|
import mmocr.utils as utils |
|
from . import utils as eval_utils |
|
|
|
|
|
def compute_recall_precision(gt_polys, pred_polys): |
|
"""Compute the recall and the precision matrices between gt and predicted |
|
polygons. |
|
|
|
Args: |
|
gt_polys (list[Polygon]): List of gt polygons. |
|
pred_polys (list[Polygon]): List of predicted polygons. |
|
|
|
Returns: |
|
recall (ndarray): Recall matrix of size gt_num x det_num. |
|
precision (ndarray): Precision matrix of size gt_num x det_num. |
|
""" |
|
assert isinstance(gt_polys, list) |
|
assert isinstance(pred_polys, list) |
|
|
|
gt_num = len(gt_polys) |
|
det_num = len(pred_polys) |
|
sz = [gt_num, det_num] |
|
|
|
recall = np.zeros(sz) |
|
precision = np.zeros(sz) |
|
|
|
|
|
for gt_id in range(gt_num): |
|
for pred_id in range(det_num): |
|
gt = gt_polys[gt_id] |
|
det = pred_polys[pred_id] |
|
|
|
inter_area = eval_utils.poly_intersection(det, gt) |
|
gt_area = gt.area |
|
det_area = det.area |
|
if gt_area != 0: |
|
recall[gt_id, pred_id] = inter_area / gt_area |
|
if det_area != 0: |
|
precision[gt_id, pred_id] = inter_area / det_area |
|
|
|
return recall, precision |
|
|
|
|
|
def eval_hmean_ic13(det_boxes, |
|
gt_boxes, |
|
gt_ignored_boxes, |
|
precision_thr=0.4, |
|
recall_thr=0.8, |
|
center_dist_thr=1.0, |
|
one2one_score=1., |
|
one2many_score=0.8, |
|
many2one_score=1.): |
|
"""Evaluate hmean of text detection using the icdar2013 standard. |
|
|
|
Args: |
|
det_boxes (list[list[list[float]]]): List of arrays of shape (n, 2k). |
|
Each element is the det_boxes for one img. k>=4. |
|
gt_boxes (list[list[list[float]]]): List of arrays of shape (m, 2k). |
|
Each element is the gt_boxes for one img. k>=4. |
|
gt_ignored_boxes (list[list[list[float]]]): List of arrays of |
|
(l, 2k). Each element is the ignored gt_boxes for one img. k>=4. |
|
precision_thr (float): Precision threshold of the iou of one |
|
(gt_box, det_box) pair. |
|
recall_thr (float): Recall threshold of the iou of one |
|
(gt_box, det_box) pair. |
|
center_dist_thr (float): Distance threshold of one (gt_box, det_box) |
|
center point pair. |
|
one2one_score (float): Reward when one gt matches one det_box. |
|
one2many_score (float): Reward when one gt matches many det_boxes. |
|
many2one_score (float): Reward when many gts match one det_box. |
|
|
|
Returns: |
|
hmean (tuple[dict]): Tuple of dicts which encodes the hmean for |
|
the dataset and all images. |
|
""" |
|
assert utils.is_3dlist(det_boxes) |
|
assert utils.is_3dlist(gt_boxes) |
|
assert utils.is_3dlist(gt_ignored_boxes) |
|
|
|
assert 0 <= precision_thr <= 1 |
|
assert 0 <= recall_thr <= 1 |
|
assert center_dist_thr > 0 |
|
assert 0 <= one2one_score <= 1 |
|
assert 0 <= one2many_score <= 1 |
|
assert 0 <= many2one_score <= 1 |
|
|
|
img_num = len(det_boxes) |
|
assert img_num == len(gt_boxes) |
|
assert img_num == len(gt_ignored_boxes) |
|
|
|
dataset_gt_num = 0 |
|
dataset_pred_num = 0 |
|
dataset_hit_recall = 0.0 |
|
dataset_hit_prec = 0.0 |
|
|
|
img_results = [] |
|
|
|
for i in range(img_num): |
|
gt = gt_boxes[i] |
|
gt_ignored = gt_ignored_boxes[i] |
|
pred = det_boxes[i] |
|
|
|
gt_num = len(gt) |
|
ignored_num = len(gt_ignored) |
|
pred_num = len(pred) |
|
|
|
accum_recall = 0. |
|
accum_precision = 0. |
|
|
|
gt_points = gt + gt_ignored |
|
gt_polys = [eval_utils.points2polygon(p) for p in gt_points] |
|
gt_ignored_index = [gt_num + i for i in range(len(gt_ignored))] |
|
gt_num = len(gt_polys) |
|
|
|
pred_polys, pred_points, pred_ignored_index = eval_utils.ignore_pred( |
|
pred, gt_ignored_index, gt_polys, precision_thr) |
|
|
|
if pred_num > 0 and gt_num > 0: |
|
|
|
gt_hit = np.zeros(gt_num, np.int8).tolist() |
|
pred_hit = np.zeros(pred_num, np.int8).tolist() |
|
|
|
|
|
|
|
recall_mat, precision_mat = compute_recall_precision( |
|
gt_polys, pred_polys) |
|
|
|
|
|
for gt_id in range(gt_num): |
|
for pred_id in range(pred_num): |
|
if (gt_hit[gt_id] != 0 or pred_hit[pred_id] != 0 |
|
or gt_id in gt_ignored_index |
|
or pred_id in pred_ignored_index): |
|
continue |
|
match = eval_utils.one2one_match_ic13( |
|
gt_id, pred_id, recall_mat, precision_mat, recall_thr, |
|
precision_thr) |
|
|
|
if match: |
|
gt_point = np.array(gt_points[gt_id]) |
|
det_point = np.array(pred_points[pred_id]) |
|
|
|
norm_dist = eval_utils.box_center_distance( |
|
det_point, gt_point) |
|
norm_dist /= eval_utils.box_diag( |
|
det_point) + eval_utils.box_diag(gt_point) |
|
norm_dist *= 2.0 |
|
|
|
if norm_dist < center_dist_thr: |
|
gt_hit[gt_id] = 1 |
|
pred_hit[pred_id] = 1 |
|
accum_recall += one2one_score |
|
accum_precision += one2one_score |
|
|
|
|
|
for gt_id in range(gt_num): |
|
if gt_id in gt_ignored_index: |
|
continue |
|
match, match_det_set = eval_utils.one2many_match_ic13( |
|
gt_id, recall_mat, precision_mat, recall_thr, |
|
precision_thr, gt_hit, pred_hit, pred_ignored_index) |
|
|
|
if match: |
|
gt_hit[gt_id] = 1 |
|
accum_recall += one2many_score |
|
accum_precision += one2many_score * len(match_det_set) |
|
for pred_id in match_det_set: |
|
pred_hit[pred_id] = 1 |
|
|
|
|
|
|
|
|
|
for pred_id in range(pred_num): |
|
if pred_id in pred_ignored_index: |
|
continue |
|
|
|
match, match_gt_set = eval_utils.many2one_match_ic13( |
|
pred_id, recall_mat, precision_mat, recall_thr, |
|
precision_thr, gt_hit, pred_hit, gt_ignored_index) |
|
|
|
if match: |
|
pred_hit[pred_id] = 1 |
|
accum_recall += many2one_score * len(match_gt_set) |
|
accum_precision += many2one_score |
|
for gt_id in match_gt_set: |
|
gt_hit[gt_id] = 1 |
|
|
|
gt_care_number = gt_num - ignored_num |
|
pred_care_number = pred_num - len(pred_ignored_index) |
|
|
|
r, p, h = eval_utils.compute_hmean(accum_recall, accum_precision, |
|
gt_care_number, pred_care_number) |
|
|
|
img_results.append({'recall': r, 'precision': p, 'hmean': h}) |
|
|
|
dataset_gt_num += gt_care_number |
|
dataset_pred_num += pred_care_number |
|
dataset_hit_recall += accum_recall |
|
dataset_hit_prec += accum_precision |
|
|
|
total_r, total_p, total_h = eval_utils.compute_hmean( |
|
dataset_hit_recall, dataset_hit_prec, dataset_gt_num, dataset_pred_num) |
|
|
|
dataset_results = { |
|
'num_gts': dataset_gt_num, |
|
'num_dets': dataset_pred_num, |
|
'num_recall': dataset_hit_recall, |
|
'num_precision': dataset_hit_prec, |
|
'recall': total_r, |
|
'precision': total_p, |
|
'hmean': total_h |
|
} |
|
|
|
return dataset_results, img_results |
|
|