Source code for gluoncv.utils.metrics.heatmap_accuracy

"""Accuracy metric for heatmap prediction."""
# pylint: disable=assignment-from-no-return
import numpy as np
try:
    from mxnet.metric import EvalMetric, check_label_shapes
except ImportError:
    from mxnet.gluon.metric import EvalMetric, check_label_shapes


[docs]class HeatmapAccuracy(EvalMetric): """Computes heatmap accuracy for keypoint Parameters ---------- axis : int, default=1 The axis that represents classes name : str Name of this metric instance for display. output_names : list of str, or None Name of predictions that should be used when updating with update_dict. By default include all predictions. label_names : list of str, or None Name of labels that should be used when updating with update_dict. By default include all labels. ignore_labels : int or iterable of integers, optional If provided as not None, will ignore these labels during update. Examples -------- >>> predicts = [mx.nd.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])] >>> labels = [mx.nd.array([0, 1, 1])] >>> acc = mx.metric.Accuracy() >>> acc.update(preds = predicts, labels = labels) >>> print acc.get() ('accuracy', 0.6666666666666666) """ def __init__(self, axis=1, name='heatmap_accuracy', hm_type='gaussian', threshold=0.5, output_names=None, label_names=None, ignore_labels=None): super(HeatmapAccuracy, self).__init__( name, axis=axis, output_names=output_names, label_names=label_names) self.axis = axis self.ignore_labels = np.array(ignore_labels).flatten() self.sum_metric = 0 self.num_inst = 0 self.hm_type = hm_type self.threshold = threshold def _calc_dists(self, preds, target, normalize): preds = preds.astype(np.float32) target = target.astype(np.float32) dists = np.zeros((preds.shape[1], preds.shape[0])) for n in range(preds.shape[0]): for c in range(preds.shape[1]): if target[n, c, 0] > 1 and target[n, c, 1] > 1: normed_preds = preds[n, c, :] / normalize[n] normed_targets = target[n, c, :] / normalize[n] dists[c, n] = np.linalg.norm(normed_preds - normed_targets) else: dists[c, n] = -1 return dists def _dist_acc(self, dists): dist_cal = np.not_equal(dists, -1) num_dist_cal = dist_cal.sum() if num_dist_cal > 0: return np.less(dists[dist_cal], self.threshold).sum() * 1.0 / num_dist_cal else: return -1
[docs] def update(self, labels, preds): """Updates the internal evaluation result. Parameters ---------- labels : list of `NDArray` The labels of the data with class indices as values, one per sample. preds : list of `NDArray` Prediction values for samples. Each prediction value can either be the class index, or a vector of likelihoods for all classes. """ from ...data.transforms.pose import get_max_pred labels, preds = check_label_shapes(labels, preds, True) num_joints = preds[0].shape[1] for label, pred in zip(labels, preds): norm = 1.0 h = pred.shape[2] w = pred.shape[3] if self.hm_type == 'gaussian': pred, _ = get_max_pred(pred) label, _ = get_max_pred(label) norm = np.ones((pred.shape[0], 2)) * np.array([h, w]) / 10 pred = pred.asnumpy() label = label.asnumpy() dists = self._calc_dists(pred, label, norm) acc = 0 sum_acc = 0 cnt = 0 for i in range(num_joints): acc = self._dist_acc(dists[i]) if acc >= 0: sum_acc += acc cnt += 1 self.sum_metric += sum_acc self.num_inst += cnt