code: def analyze_per_img_dets(
      confusion_matrix,
      gts,
      result,
      score_thr=0,
      tp_iou_thr=0.5,
      nms_iou_thr=None,
  ):
      """Analyze detection results on each image.
  
      Args:
          confusion_matrix (ndarray): The confusion matrix,
              has shape (num_classes + 1, num_classes + 1).
          gt_bboxes (ndarray): Ground truth bboxes, has shape (num_gt, 4).
          gt_labels (ndarray): Ground truth labels, has shape (num_gt).
          result (ndarray): Detection results, has shape
              (num_classes, num_bboxes, 5).
          score_thr (float): Score threshold to filter bboxes.
              Default: 0.
          tp_iou_thr (float): IoU threshold to be considered as matched.
              Default: 0.5.
          nms_iou_thr (float|optional): nms IoU threshold, the detection results
              have done nms in the detector, only applied when users want to
              change the nms IoU threshold. Default: None.
      """
      true_positives = np.zeros(len(gts))
      gt_bboxes = []
      gt_labels = []
      for gt in gts:
          gt_bboxes.append(gt["bbox"])
          gt_labels.append(gt["bbox_label"])
  
      gt_bboxes = np.array(gt_bboxes)
      gt_labels = np.array(gt_labels)
  
      unique_label = np.unique(result["labels"].numpy())
  
      for det_label in unique_label:
          mask = result["labels"] == det_label
          det_bboxes = result["bboxes"][mask].numpy()
          det_scores = result["scores"][mask].numpy()
  
          if nms_iou_thr:
              det_bboxes, _ = nms(
                  det_bboxes,
                  det_scores,
                  nms_iou_thr,
                  score_threshold=score_thr,
              )
          ious = bbox_overlaps(det_bboxes[:, :4], gt_bboxes)
          for i, score in enumerate(det_scores):
              det_match = 0
              if score >= score_thr:
                  for j, gt_label in enumerate(gt_labels):
                      if ious[i, j] >= tp_iou_thr:
                          det_match += 1
                          if gt_label == det_label:
                              true_positives[j] += 1  # TP
                          confusion_matrix[gt_label, det_label] += 1
                  if det_match == 0:  # BG FP
                      confusion_matrix[-1, det_label] += 1
      for num_tp, gt_label in zip(true_positives, gt_labels):
          if num_tp == 0:  # FN
              confusion_matrix[gt_label, -1] += 1
  
file_path: mmdetection/tools/analysis_tools/confusion_matrix.py
imports: import argparse
  import os
  import matplotlib.pyplot
  import numpy
  from matplotlib.ticker import MultipleLocator
  from mmcv.ops import nms
  from mmengine import Config, DictAction
  from mmengine.fileio import load
  from mmengine.registry import init_default_scope
  from mmengine.utils import ProgressBar
  from mmdet.evaluation import bbox_overlaps
  from mmdet.registry import DATASETS
  from mmdet.utils import replace_cfg_vals, update_data_root
label_desc: Use image node_id 3 for calling a global function with example usage: analyze_per_img_dets(confusion_matrix, gts, result, score_thr, tp_iou_thr, nms_iou_thr) without return types