code: # nuScenes dev-kit.
  # Code written by Oscar Beijbom, 2019.
  
  import json
  from typing import Dict, Tuple
  
  import numpy as np
  import tqdm
  from pyquaternion import Quaternion
  
  from nuscenes import NuScenes
  from nuscenes.eval.common.data_classes import EvalBoxes
  from nuscenes.eval.detection.data_classes import DetectionBox
  from nuscenes.eval.detection.utils import category_to_detection_name
  from nuscenes.eval.tracking.data_classes import TrackingBox
  from nuscenes.eval.tracking.utils import category_to_tracking_name
  from nuscenes.utils.data_classes import Box
  from nuscenes.utils.geometry_utils import points_in_box
  from nuscenes.utils.splits import create_splits_scenes
  
  
  def load_prediction(result_path: str, max_boxes_per_sample: int, box_cls, verbose: bool = False) \
          -> Tuple[EvalBoxes, Dict]:
      """
      Loads object predictions from file.
      :param result_path: Path to the .json result file provided by the user.
      :param max_boxes_per_sample: Maximim number of boxes allowed per sample.
      :param box_cls: Type of box to load, e.g. DetectionBox or TrackingBox.
      :param verbose: Whether to print messages to stdout.
      :return: The deserialized results and meta data.
      """
  
      # Load from file and check that the format is correct.
      with open(result_path) as f:
          data = json.load(f)
      assert 'results' in data, 'Error: No field `results` in result file. Please note that the result format changed.' \
                                'See https://www.nuscenes.org/object-detection for more information.'
  
      # Deserialize results and get meta data.
      all_results = EvalBoxes.deserialize(data['results'], box_cls)
      meta = data['meta']
      if verbose:
          print("Loaded results from {}. Found detections for {} samples."
                .format(result_path, len(all_results.sample_tokens)))
  
      # Check that each sample has no more than x predicted boxes.
      for sample_token in all_results.sample_tokens:
          assert len(all_results.boxes[sample_token]) <= max_boxes_per_sample, \
              "Error: Only <= %d boxes per sample allowed!" % max_boxes_per_sample
  
      return all_results, meta
  
  
  def load_merge_from_pkl(nusc: NuScenes, pkl_path: str, box_cls, verbose: bool = False) -> EvalBoxes:
      # Init.
      if box_cls == DetectionBox:
          attribute_map = {a['token']: a['name'] for a in nusc.attribute}
  
      if verbose:
          print('Loading annotations for {} split from nuScenes version: {}'.format(pkl_path, nusc.version))
  
      import mmcv
      infos = mmcv.load(pkl_path)['infos']
      samples = []
      for info in infos:
          samples.append(nusc.get('sample', info['token']))
      all_annotations = EvalBoxes()
  
      # Load annotations and filter predictions and annotations.
      merge_map = dict(car='vehicle',
                       truck='vehicle',
                       bus='vehicle',
                       trailer='vehicle',
                       construction_vehicle='vehicle',
                       pedestrian='pedestrian',
                       motorcycle='bike',
                       bicycle='bike',
                       traffic_cone='traffic_boundary',
                       barrier='traffic_boundary')
      for sample in tqdm.tqdm(samples, leave=verbose):
          sample_token = sample['token']
          cam_token = sample['data']['CAM_FRONT']
          _, boxes_cam, _ = nusc.get_sample_data(cam_token)
          sample_annotation_tokens = [box.token for box in boxes_cam]
  
          # sample = nusc.get('sample', sample_token)
          # sample_annotation_tokens = sample['anns']
  
          sample_boxes = []
          for sample_annotation_token in sample_annotation_tokens:
  
              sample_annotation = nusc.get('sample_annotation', sample_annotation_token)
              if box_cls == DetectionBox:
                  # Get label name in detection task and filter unused labels.
                  detection_name = category_to_detection_name(sample_annotation['category_name'])
                  if detection_name is None:
                      continue
                  detection_name = merge_map[detection_name]
  
                  # Get attribute_name.
                  attr_tokens = sample_annotation['attribute_tokens']
                  attr_count = len(attr_tokens)
                  if attr_count == 0:
                      attribute_name = ''
                  elif attr_count == 1:
                      attribute_name = attribute_map[attr_tokens[0]]
                  else:
                      raise Exception('Error: GT annotations must not have more than one attribute!')
  
                  sample_boxes.append(
                      box_cls(
                          sample_token=sample_token,
                          translation=sample_annotation['translation'],
                          size=sample_annotation['size'],
                          rotation=sample_annotation['rotation'],
                          velocity=nusc.box_velocity(sample_annotation['token'])[:2],
                          num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
                          detection_name=detection_name,
                          detection_score=-1.0,  # GT samples do not have a score.
                          attribute_name=attribute_name
                      )
                  )
              else:
                  raise NotImplementedError('Error: Invalid box_cls %s!' % box_cls)
  
          all_annotations.add_boxes(sample_token, sample_boxes)
  
      if verbose:
          print("Loaded ground truth annotations for {} samples.".format(len(all_annotations.sample_tokens)))
  
      return all_annotations
  
  
  def load_pkl_front_cam(nusc: NuScenes, pkl_path: str, box_cls, verbose: bool = False) -> EvalBoxes:
      # Init.
      if box_cls == DetectionBox:
          attribute_map = {a['token']: a['name'] for a in nusc.attribute}
  
      if verbose:
          print('Loading annotations for {} split from nuScenes version: {}'.format(pkl_path, nusc.version))
  
      import mmcv
      infos = mmcv.load(pkl_path)['infos']
      samples = []
      for info in infos:
          samples.append(nusc.get('sample', info['token']))
  
      all_annotations = EvalBoxes()
  
      # Load annotations and filter predictions and annotations.
      for sample in tqdm.tqdm(samples, leave=verbose):
          sample_token = sample['token']
          cam_token = sample['data']['CAM_FRONT']
          _, boxes_cam, _ = nusc.get_sample_data(cam_token)
          sample_annotation_tokens = [box.token for box in boxes_cam]
  
          # sample = nusc.get('sample', sample_token)
          # sample_annotation_tokens = sample['anns']
  
          sample_boxes = []
          for sample_annotation_token in sample_annotation_tokens:
  
              sample_annotation = nusc.get('sample_annotation', sample_annotation_token)
              if box_cls == DetectionBox:
                  # Get label name in detection task and filter unused labels.
                  detection_name = category_to_detection_name(sample_annotation['category_name'])
                  if detection_name is None:
                      continue
  
                  # Get attribute_name.
                  attr_tokens = sample_annotation['attribute_tokens']
                  attr_count = len(attr_tokens)
                  if attr_count == 0:
                      attribute_name = ''
                  elif attr_count == 1:
                      attribute_name = attribute_map[attr_tokens[0]]
                  else:
                      raise Exception('Error: GT annotations must not have more than one attribute!')
  
                  sample_boxes.append(
                      box_cls(
                          sample_token=sample_token,
                          translation=sample_annotation['translation'],
                          size=sample_annotation['size'],
                          rotation=sample_annotation['rotation'],
                          velocity=nusc.box_velocity(sample_annotation['token'])[:2],
                          num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
                          detection_name=detection_name,
                          detection_score=-1.0,  # GT samples do not have a score.
                          attribute_name=attribute_name
                      )
                  )
              else:
                  raise NotImplementedError('Error: Invalid box_cls %s!' % box_cls)
  
          all_annotations.add_boxes(sample_token, sample_boxes)
  
      if verbose:
          print("Loaded ground truth annotations for {} samples.".format(len(all_annotations.sample_tokens)))
  
      return all_annotations
  
  
  def load_gt_front_cam(nusc: NuScenes, eval_split: str, box_cls, verbose: bool = False) -> EvalBoxes:
      # Init.
      if box_cls == DetectionBox:
          attribute_map = {a['token']: a['name'] for a in nusc.attribute}
  
      if verbose:
          print('Loading annotations for {} split from nuScenes version: {}'.format(eval_split, nusc.version))
  
      # Only keep samples from this split.
      splits = create_splits_scenes()
  
      # Check compatibility of split with nusc_version.
      version = nusc.version
      if eval_split in {'train', 'val', 'train_detect', 'train_track'}:
          assert version.endswith('trainval'), \
              'Error: Requested split {} which is not compatible with NuScenes version {}'.format(eval_split, version)
      elif eval_split in {'mini_train', 'mini_val'}:
          assert version.endswith('mini'), \
              'Error: Requested split {} which is not compatible with NuScenes version {}'.format(eval_split, version)
      elif eval_split == 'test':
          assert version.endswith('test'), \
              'Error: Requested split {} which is not compatible with NuScenes version {}'.format(eval_split, version)
      else:
          raise ValueError('Error: Requested split {} which this function cannot map to the correct NuScenes version.'
                           .format(eval_split))
  
      if eval_split == 'test':
          # Check that you aren't trying to cheat :).
          assert len(nusc.sample_annotation) > 0, \
              'Error: You are trying to evaluate on the test set but you do not have the annotations!'
  
      samples = []
      for sample in nusc.sample:
          scene_record = nusc.get('scene', sample['scene_token'])
          if scene_record['name'] in splits[eval_split]:
              samples.append(sample)
  
      all_annotations = EvalBoxes()
  
      # Load annotations and filter predictions and annotations.
      tracking_id_set = set()
      for sample in tqdm.tqdm(samples, leave=verbose):
          sample_token = sample['token']
          cam_token = sample['data']['CAM_FRONT']
          _, boxes_cam, _ = nusc.get_sample_data(cam_token)
          sample_annotation_tokens = [box.token for box in boxes_cam]
  
          # sample = nusc.get('sample', sample_token)
          # sample_annotation_tokens = sample['anns']
  
          sample_boxes = []
          for sample_annotation_token in sample_annotation_tokens:
  
              sample_annotation = nusc.get('sample_annotation', sample_annotation_token)
              if box_cls == DetectionBox:
                  # Get label name in detection task and filter unused labels.
                  detection_name = category_to_detection_name(sample_annotation['category_name'])
                  if detection_name is None:
                      continue
  
                  # Get attribute_name.
                  attr_tokens = sample_annotation['attribute_tokens']
                  attr_count = len(attr_tokens)
                  if attr_count == 0:
                      attribute_name = ''
                  elif attr_count == 1:
                      attribute_name = attribute_map[attr_tokens[0]]
                  else:
                      raise Exception('Error: GT annotations must not have more than one attribute!')
  
                  sample_boxes.append(
                      box_cls(
                          sample_token=sample_token,
                          translation=sample_annotation['translation'],
                          size=sample_annotation['size'],
                          rotation=sample_annotation['rotation'],
                          velocity=nusc.box_velocity(sample_annotation['token'])[:2],
                          num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
                          detection_name=detection_name,
                          detection_score=-1.0,  # GT samples do not have a score.
                          attribute_name=attribute_name
                      )
                  )
              elif box_cls == TrackingBox:
                  # Use nuScenes token as tracking id.
                  tracking_id = sample_annotation['instance_token']
                  tracking_id_set.add(tracking_id)
  
                  # Get label name in detection task and filter unused labels.
                  tracking_name = category_to_tracking_name(sample_annotation['category_name'])
                  if tracking_name is None:
                      continue
  
                  sample_boxes.append(
                      box_cls(
                          sample_token=sample_token,
                          translation=sample_annotation['translation'],
                          size=sample_annotation['size'],
                          rotation=sample_annotation['rotation'],
                          velocity=nusc.box_velocity(sample_annotation['token'])[:2],
                          num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
                          tracking_id=tracking_id,
                          tracking_name=tracking_name,
                          tracking_score=-1.0  # GT samples do not have a score.
                      )
                  )
              else:
                  raise NotImplementedError('Error: Invalid box_cls %s!' % box_cls)
  
          all_annotations.add_boxes(sample_token, sample_boxes)
  
      if verbose:
          print("Loaded ground truth annotations for {} samples.".format(len(all_annotations.sample_tokens)))
  
      return all_annotations
  
  
  def load_gt(nusc: NuScenes, eval_split: str, box_cls, verbose: bool = False) -> EvalBoxes:
      """
      Loads ground truth boxes from DB.
      :param nusc: A NuScenes instance.
      :param eval_split: The evaluation split for which we load GT boxes.
      :param box_cls: Type of box to load, e.g. DetectionBox or TrackingBox.
      :param verbose: Whether to print messages to stdout.
      :return: The GT boxes.
      """
      # Init.
      if box_cls == DetectionBox:
          attribute_map = {a['token']: a['name'] for a in nusc.attribute}
  
      if verbose:
          print('Loading annotations for {} split from nuScenes version: {}'.format(eval_split, nusc.version))
      # Read out all sample_tokens in DB.
      sample_tokens_all = [s['token'] for s in nusc.sample]
      assert len(sample_tokens_all) > 0, "Error: Database has no samples!"
  
      # Only keep samples from this split.
      splits = create_splits_scenes()
  
      # Check compatibility of split with nusc_version.
      version = nusc.version
      if eval_split in {'train', 'val', 'train_detect', 'train_track'}:
          assert version.endswith('trainval'), \
              'Error: Requested split {} which is not compatible with NuScenes version {}'.format(eval_split, version)
      elif eval_split in {'mini_train', 'mini_val'}:
          assert version.endswith('mini'), \
              'Error: Requested split {} which is not compatible with NuScenes version {}'.format(eval_split, version)
      elif eval_split == 'test':
          assert version.endswith('test'), \
              'Error: Requested split {} which is not compatible with NuScenes version {}'.format(eval_split, version)
      else:
          raise ValueError('Error: Requested split {} which this function cannot map to the correct NuScenes version.'
                           .format(eval_split))
  
      if eval_split == 'test':
          # Check that you aren't trying to cheat :).
          assert len(nusc.sample_annotation) > 0, \
              'Error: You are trying to evaluate on the test set but you do not have the annotations!'
  
      sample_tokens = []
      for sample_token in sample_tokens_all:
          scene_token = nusc.get('sample', sample_token)['scene_token']
          scene_record = nusc.get('scene', scene_token)
          if scene_record['name'] in splits[eval_split]:
              sample_tokens.append(sample_token)
  
      all_annotations = EvalBoxes()
  
      # Load annotations and filter predictions and annotations.
      tracking_id_set = set()
      for sample_token in tqdm.tqdm(sample_tokens, leave=verbose):
  
          sample = nusc.get('sample', sample_token)
          sample_annotation_tokens = sample['anns']
  
          sample_boxes = []
          for sample_annotation_token in sample_annotation_tokens:
  
              sample_annotation = nusc.get('sample_annotation', sample_annotation_token)
              if box_cls == DetectionBox:
                  # Get label name in detection task and filter unused labels.
                  detection_name = category_to_detection_name(sample_annotation['category_name'])
                  if detection_name is None:
                      continue
  
                  # Get attribute_name.
                  attr_tokens = sample_annotation['attribute_tokens']
                  attr_count = len(attr_tokens)
                  if attr_count == 0:
                      attribute_name = ''
                  elif attr_count == 1:
                      attribute_name = attribute_map[attr_tokens[0]]
                  else:
                      raise Exception('Error: GT annotations must not have more than one attribute!')
  
                  sample_boxes.append(
                      box_cls(
                          sample_token=sample_token,
                          translation=sample_annotation['translation'],
                          size=sample_annotation['size'],
                          rotation=sample_annotation['rotation'],
                          velocity=nusc.box_velocity(sample_annotation['token'])[:2],
                          num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
                          detection_name=detection_name,
                          detection_score=-1.0,  # GT samples do not have a score.
                          attribute_name=attribute_name
                      )
                  )
              elif box_cls == TrackingBox:
                  # Use nuScenes token as tracking id.
                  tracking_id = sample_annotation['instance_token']
                  tracking_id_set.add(tracking_id)
  
                  # Get label name in detection task and filter unused labels.
                  tracking_name = category_to_tracking_name(sample_annotation['category_name'])
                  if tracking_name is None:
                      continue
  
                  sample_boxes.append(
                      box_cls(
                          sample_token=sample_token,
                          translation=sample_annotation['translation'],
                          size=sample_annotation['size'],
                          rotation=sample_annotation['rotation'],
                          velocity=nusc.box_velocity(sample_annotation['token'])[:2],
                          num_pts=sample_annotation['num_lidar_pts'] + sample_annotation['num_radar_pts'],
                          tracking_id=tracking_id,
                          tracking_name=tracking_name,
                          tracking_score=-1.0  # GT samples do not have a score.
                      )
                  )
              else:
                  raise NotImplementedError('Error: Invalid box_cls %s!' % box_cls)
  
          all_annotations.add_boxes(sample_token, sample_boxes)
  
      if verbose:
          print("Loaded ground truth annotations for {} samples.".format(len(all_annotations.sample_tokens)))
  
      return all_annotations
  
  
  def add_center_dist(nusc: NuScenes,
                      eval_boxes: EvalBoxes):
      """
      Adds the cylindrical (xy) center distance from ego vehicle to each box.
      :param nusc: The NuScenes instance.
      :param eval_boxes: A set of boxes, either GT or predictions.
      :return: eval_boxes augmented with center distances.
      """
      for sample_token in eval_boxes.sample_tokens:
          sample_rec = nusc.get('sample', sample_token)
          sd_record = nusc.get('sample_data', sample_rec['data']['LIDAR_TOP'])
          pose_record = nusc.get('ego_pose', sd_record['ego_pose_token'])
          cs_record = nusc.get('calibrated_sensor', sd_record['calibrated_sensor_token'])
          lidar2ego_rotation = cs_record['rotation']
          lidar2ego_translation = cs_record['translation']
          ego2global_rotation = pose_record['rotation']
          ego2global_translation = pose_record['translation']
  
          for box in eval_boxes[sample_token]:
              # Both boxes and ego pose are given in global coord system, so distance can be calculated directly.
              # Note that the z component of the ego pose is 0.
              center_ego = np.array(box.translation) - np.array(ego2global_translation)
              center_ego_tmp = np.dot(Quaternion(ego2global_rotation).inverse.rotation_matrix, center_ego)
              center_lidar_tmp = center_ego_tmp - np.array(lidar2ego_translation)
              center_lidar = np.dot(Quaternion(lidar2ego_rotation).inverse.rotation_matrix, center_lidar_tmp)
              
              if isinstance(box, DetectionBox) or isinstance(box, TrackingBox):
                  box.ego_translation = tuple(center_ego)
                  box.lidar_translation = tuple(center_lidar) 
              else:
                  raise NotImplementedError
  
      return eval_boxes
  
  
  def filter_eval_boxes(nusc: NuScenes,
                        eval_boxes: EvalBoxes,
                        max_dist: Dict[str, float],
                        verbose: bool = False) -> EvalBoxes:
      """
      Applies filtering to boxes. Distance, bike-racks and points per box.
      :param nusc: An instance of the NuScenes class.
      :param eval_boxes: An instance of the EvalBoxes class.
      :param max_dist: Maps the detection name to the eval distance threshold for that class.
      :param verbose: Whether to print to stdout.
      """
      # Retrieve box type for detectipn/tracking boxes.
      class_field = _get_box_class_field(eval_boxes)
  
      # Accumulators for number of filtered boxes.
      total, dist_filter, point_filter, bike_rack_filter = 0, 0, 0, 0
      for ind, sample_token in enumerate(eval_boxes.sample_tokens):
  
          # Filter on distance first.
          total += len(eval_boxes[sample_token])
          eval_boxes.boxes[sample_token] = [box for box in eval_boxes[sample_token] if
                                            box.ego_dist < max_dist[box.__getattribute__(class_field)]]
          dist_filter += len(eval_boxes[sample_token])
  
          # Then remove boxes with zero points in them. Eval boxes have -1 points by default.
          eval_boxes.boxes[sample_token] = [box for box in eval_boxes[sample_token] if not box.num_pts == 0]
          point_filter += len(eval_boxes[sample_token])
  
          # Perform bike-rack filtering.
          sample_anns = nusc.get('sample', sample_token)['anns']
          bikerack_recs = [nusc.get('sample_annotation', ann) for ann in sample_anns if
                           nusc.get('sample_annotation', ann)['category_name'] == 'static_object.bicycle_rack']
          bikerack_boxes = [Box(rec['translation'], rec['size'], Quaternion(rec['rotation'])) for rec in bikerack_recs]
          filtered_boxes = []
          for box in eval_boxes[sample_token]:
              if box.__getattribute__(class_field) in ['bicycle', 'motorcycle']:
                  in_a_bikerack = False
                  for bikerack_box in bikerack_boxes:
                      if np.sum(points_in_box(bikerack_box, np.expand_dims(np.array(box.translation), axis=1))) > 0:
                          in_a_bikerack = True
                  if not in_a_bikerack:
                      filtered_boxes.append(box)
              else:
                  filtered_boxes.append(box)
  
          eval_boxes.boxes[sample_token] = filtered_boxes
          bike_rack_filter += len(eval_boxes.boxes[sample_token])
  
      if verbose:
          print("=> Original number of boxes: %d" % total)
          print("=> After distance based filtering: %d" % dist_filter)
          print("=> After LIDAR points based filtering: %d" % point_filter)
          print("=> After bike rack filtering: %d" % bike_rack_filter)
  
      return eval_boxes
  
  
  def filter_half_boxes(nusc: NuScenes,
                        eval_boxes: EvalBoxes,
                        max_dist: Dict[str, float],
                        verbose: bool = False) -> EvalBoxes:
      """
      Applies filtering to boxes. Distance, bike-racks and points per box.
      :param nusc: An instance of the NuScenes class.
      :param eval_boxes: An instance of the EvalBoxes class.
      :param max_dist: Maps the detection name to the eval distance threshold for that class.
      :param verbose: Whether to print to stdout.
      """
      # Retrieve box type for detectipn/tracking boxes.
      class_field = _get_box_class_field(eval_boxes)
  
      # Accumulators for number of filtered boxes.
      total, dist_filter, point_filter, bike_rack_filter = 0, 0, 0, 0
      for ind, sample_token in enumerate(eval_boxes.sample_tokens):
  
          # Filter on distance first.
          total += len(eval_boxes[sample_token])
          eval_boxes.boxes[sample_token] = [box for box in eval_boxes[sample_token] if
                                            box.lidar_translation[1] > 0 and
                                            box.ego_dist < max_dist[box.__getattribute__(class_field)]]
          dist_filter += len(eval_boxes[sample_token])
  
          # Then remove boxes with zero points in them. Eval boxes have -1 points by default.
          eval_boxes.boxes[sample_token] = [box for box in eval_boxes[sample_token] if not box.num_pts == 0]
          point_filter += len(eval_boxes[sample_token])
  
          # Perform bike-rack filtering.
          sample_anns = nusc.get('sample', sample_token)['anns']
          bikerack_recs = [nusc.get('sample_annotation', ann) for ann in sample_anns if
                           nusc.get('sample_annotation', ann)['category_name'] == 'static_object.bicycle_rack']
          bikerack_boxes = [Box(rec['translation'], rec['size'], Quaternion(rec['rotation'])) for rec in bikerack_recs]
          filtered_boxes = []
          for box in eval_boxes[sample_token]:
              if box.__getattribute__(class_field) in ['bicycle', 'motorcycle']:
                  in_a_bikerack = False
                  for bikerack_box in bikerack_boxes:
                      if np.sum(points_in_box(bikerack_box, np.expand_dims(np.array(box.translation), axis=1))) > 0:
                          in_a_bikerack = True
                  if not in_a_bikerack:
                      filtered_boxes.append(box)
              else:
                  filtered_boxes.append(box)
  
          eval_boxes.boxes[sample_token] = filtered_boxes
          bike_rack_filter += len(eval_boxes.boxes[sample_token])
  
      if verbose:
          print("=> Original number of boxes: %d" % total)
          print("=> After distance based filtering: %d" % dist_filter)
          print("=> After LIDAR points based filtering: %d" % point_filter)
          print("=> After bike rack filtering: %d" % bike_rack_filter)
  
      return eval_boxes
  
  
  def _get_box_class_field(eval_boxes: EvalBoxes) -> str:
      """
      Retrieve the name of the class field in the boxes.
      This parses through all boxes until it finds a valid box.
      If there are no valid boxes, this function throws an exception.
      :param eval_boxes: The EvalBoxes used for evaluation.
      :return: The name of the class field in the boxes, e.g. detection_name or tracking_name.
      """
      assert len(eval_boxes.boxes) > 0
      box = None
      for val in eval_boxes.boxes.values():
          if len(val) > 0:
              box = val[0]
              break
      if isinstance(box, DetectionBox):
          class_field = 'detection_name'
      elif isinstance(box, TrackingBox):
          class_field = 'tracking_name'
      else:
          raise Exception('Error: Invalid box type: %s' % box)
  
      return class_field
  
apis: numpy.array