def annotate(imgs: List[Union[str, np.ndarray]],  # pylint: disable=invalid-name
             box_storage_pointer: List[np.ndarray],
             callbackId: str = None):
  """Open the bounding box UI and prompt the user for input.

  Args:
    imgs: list[str | np.ndarray]
      List of locations from where to load the images from. If a np.ndarray is
      given, the array is interpretted as an image and sent to the frontend. If
      a str is given, the string is interpreted as a path and is read as a
      np.ndarray before being sent to the frontend.

    box_storage_pointer: list[np.ndarray]
      Destination list for bounding box arrays. Each array in this list
      corresponds to one of the images given in imgs. The array is a
      N x 4 array where N is the number of bounding boxes given by the user
      for that particular image. If there are no bounding boxes for an image,
      None is used instead of an empty array.

    callbackId: str, optional
      The ID for the callback function that communicates between the fontend
      and the backend. If no ID is given, a random UUID string is used instead.
  """

  # Set a random ID for the callback function
  if callbackId is None:
    callbackId = str(uuid.uuid1()).replace('-', '')

  def dictToList(input_bbox):  # pylint: disable=invalid-name
    """Convert bbox.

    This function converts the dictionary from the frontend (if the format
    {x, y, w, h} as shown in callbackFunction) into a list
    ([y_min, x_min, y_max, x_max])

    Args:
      input_bbox:

    Returns:
      A list with bbox coordinates in the form [ymin, xmin, ymax, xmax].
    """
    return (input_bbox['y'], input_bbox['x'], input_bbox['y'] + input_bbox['h'],
            input_bbox['x'] + input_bbox['w'])

  def callbackFunction(annotations: List[List[Dict[str, float]]]):  # pylint: disable=invalid-name
    """Callback function.

    This is the call back function to capture the data from the frontend and
    convert the data into a numpy array.

    Args:
      annotations: list[list[dict[str, float]]]
        The input of the call back function is a list of list of objects
        corresponding to the annotations. The format of annotations is shown
        below

        [
          // stuff for image 1
          [
            // stuff for rect 1
            {x, y, w, h},
            // stuff for rect 2
            {x, y, w, h},
            ...
          ],
          // stuff for image 2
          [
            // stuff for rect 1
            {x, y, w, h},
            // stuff for rect 2
            {x, y, w, h},
            ...
          ],
          ...
        ]
    """

    # reset the boxes list
    nonlocal box_storage_pointer
    boxes: List[np.ndarray] = box_storage_pointer
    boxes.clear()

    # load the new annotations into the boxes list
    for annotations_per_img in annotations:
      rectangles_as_arrays = [np.clip(dictToList(annotation), 0, 1)
                              for annotation in annotations_per_img]
      if rectangles_as_arrays:
        boxes.append(np.stack(rectangles_as_arrays))
      else:
        boxes.append(None)

    # output the annotations to the errorlog
    with output.redirect_to_element('#errorlog'):
      display('--boxes array populated--')

  output.register_callback(callbackId, callbackFunction)
  draw_bbox(imgs, callbackId)
Open the bounding box UI and prompt the user for input.

  Args:
    imgs: list[str | np.ndarray]
      List of locations from where to load the images from. If a np.ndarray is
      given, the array is interpretted as an image and sent to the frontend. If
      a str is given, the string is interpreted as a path and is read as a
      np.ndarray before being sent to the frontend.

    box_storage_pointer: list[np.ndarray]
      Destination list for bounding box arrays. Each array in this list
      corresponds to one of the images given in imgs. The array is a
      N x 4 array where N is the number of bounding boxes given by the user
      for that particular image. If there are no bounding boxes for an image,
      None is used instead of an empty array.

    callbackId: str, optional
      The ID for the callback function that communicates between the fontend
      and the backend. If no ID is given, a random UUID string is used instead.
  
inference_api/src/main/object_detection/utils/colab_utils.py