class_docstr: Refer to 'https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/evalInstanceLevelSemanticLabeling.py'
  Calculate the matching results of each image, each class, each IoU, and then get the final
  matching results of each class and each IoU of dataset. Base on the matching results, the AP
  and mAP can be calculated.
  we need two vectors for each class and for each overlap
  The first vector (y_true) is binary and is 1, where the ground truth says true,
  and is 0 otherwise.
  The second vector (y_score) is float [0...1] and represents the confidence of
  the prediction.
  We represent the following cases as:
                                        | y_true |   y_score
    gt instance with matched prediction |    1   | confidence
    gt instance w/o  matched prediction |    1   |     0.0
           false positive prediction    |    0   | confidence
  The current implementation makes only sense for an overlap threshold >= 0.5,
  since only then, a single prediction can either be ignored or matched, but
  never both. Further, it can never match to two gt instances.
  For matching, we vary the overlap and do the following steps:
    1.) remove all predictions that satisfy the overlap criterion with an ignore region (either void or *group)
    2.) remove matches that do not satisfy the overlap
    3.) mark non-matched predictions as false positive
  In the processing, 0 represent the first class of 'thing'. So the label will less 1 than the dataset.
  
  Args:
      num_classes (int): The unique number of target classes. Exclude background class, labeled 0 usually.
      overlaps (float|list, optional): The threshold of IoU. Default: 0.5.
      thing_list (list|None, optional): Thing class, only calculate AP for the thing class. Default: None.
docstr: calculate ap for every classes
code: def cal_ap(self):
      """
      calculate ap for every classes
      """
      self.ap = [0] * self.num_classes
      self.ap_overlap = [
          [0] * len(self.overlaps) for _ in range(self.num_classes)
      ]
      for i in range(self.num_classes):
          if i not in self.thing_list:
              continue
          for j in range(len(self.overlaps)):
              y_true = self.y_true[i][j]
              y_score = self.y_score[i][j]
              if len(y_true) == 0:
                  self.ap_overlap[i][j] = 0
                  continue
              score_argsort = np.argsort(y_score)
              y_score_sorted = y_score[score_argsort]
              y_true_sorted = y_true[score_argsort]
              y_true_sorted_cumsum = np.cumsum(y_true_sorted)
  
              # unique thresholds
              thresholds, unique_indices = np.unique(
                  y_score_sorted, return_index=True
              )
  
              # since we need to add an artificial point to the precision-recall curve
              # increase its length by 1
              nb_pr = len(unique_indices) + 1
  
              # calculate precision and recall
              nb_examples = len(y_score_sorted)
              nb_true_exampels = y_true_sorted_cumsum[-1]
              precision = np.zeros(nb_pr)
              recall = np.zeros(nb_pr)
  
              # deal with the first point
              # only thing we need to do, is to append a zero to the cumsum at the end.
              # an index of -1 uses that zero then
              y_true_sorted_cumsum = np.append(y_true_sorted_cumsum, 0)
  
              # deal with remaining
              for idx_res, idx_scores in enumerate(unique_indices):
                  cumsum = y_true_sorted_cumsum[idx_scores - 1]
                  tp = nb_true_exampels - cumsum
                  fp = nb_examples - idx_scores - tp
                  fn = cumsum + self.hard_fns[i][j]
                  p = float(tp) / (tp + fp)
                  r = float(tp) / (tp + fn)
                  precision[idx_res] = p
                  recall[idx_res] = r
  
              # add first point in curve
              precision[-1] = 1.0
              # In some calculation，make precision the max after this point in curve.
              # precision = [np.max(precision[:i+1]) for i in range(len(precision))]
              recall[-1] = 0.0
  
              # compute average of precision-recall curve
              # integration is performed via zero order, or equivalently step-wise integration
              # first compute the widths of each step:
              # use a convolution with appropriate kernel, manually deal with the boundaries first
              recall_for_conv = np.copy(recall)
              recall_for_conv = np.append(
                  recall_for_conv[0], recall_for_conv
              )
              recall_for_conv = np.append(recall_for_conv, 0.0)
  
              step_widths = np.convolve(
                  recall_for_conv, [-0.5, 0, 0.5], "valid"
              )
  
              # integrate is now simply a dot product
              ap_current = np.dot(precision, step_widths)
              self.ap_overlap[i][j] = ap_current
  
      ap = [np.average(i) for i in self.ap_overlap]
      self.ap = ap
  
      return ap
  
file_path: PaddleSeg/contrib/PanopticDeepLab/utils/evaluation/instance.py
imports: from collections import defaultdict, OrderedDict
  import numpy
label_desc: Use image node_id 4 for calling the InstanceEvaluator obj's underlying member method code with example usage: obj.cal_ap() and returns: ap