code: def rotate_boxes(
      loc_preds: np.ndarray,
      angle: float,
      orig_shape: Tuple[int, int],
      min_angle: float = 1.0,
      target_shape: Optional[Tuple[int, int]] = None,
  ) -> np.ndarray:
      """Rotate a batch of straight bounding boxes (xmin, ymin, xmax, ymax, c) or rotated bounding boxes
      (4, 2) of an angle, if angle > min_angle, around the center of the page.
      If target_shape is specified, the boxes are remapped to the target shape after the rotation. This
      is done to remove the padding that is created by rotate_page(expand=True)
  
      Args:
      ----
          loc_preds: (N, 5) or (N, 4, 2) array of RELATIVE boxes
          angle: angle between -90 and +90 degrees
          orig_shape: shape of the origin image
          min_angle: minimum angle to rotate boxes
          target_shape: shape of the destination image
  
      Returns:
      -------
          A batch of rotated boxes (N, 4, 2): or a batch of straight bounding boxes
      """
      # Change format of the boxes to rotated boxes
      _boxes = loc_preds.copy()
      if _boxes.ndim == 2:
          _boxes = np.stack(
              [
                  _boxes[:, [0, 1]],
                  _boxes[:, [2, 1]],
                  _boxes[:, [2, 3]],
                  _boxes[:, [0, 3]],
              ],
              axis=1,
          )
      # If small angle, return boxes (no rotation)
      if abs(angle) < min_angle or abs(angle) > 90 - min_angle:
          return _boxes
      # Compute rotation matrix
      angle_rad = (
          angle * np.pi / 180.0
      )  # compute radian angle for np functions
      rotation_mat = np.array(
          [
              [np.cos(angle_rad), -np.sin(angle_rad)],
              [np.sin(angle_rad), np.cos(angle_rad)],
          ],
          dtype=_boxes.dtype,
      )
      # Rotate absolute points
      points: np.ndarray = np.stack(
          (
              _boxes[:, :, 0] * orig_shape[1],
              _boxes[:, :, 1] * orig_shape[0],
          ),
          axis=-1,
      )
      image_center = (orig_shape[1] / 2, orig_shape[0] / 2)
      rotated_points = image_center + np.matmul(
          points - image_center, rotation_mat
      )
      rotated_boxes: np.ndarray = np.stack(
          (
              rotated_points[:, :, 0] / orig_shape[1],
              rotated_points[:, :, 1] / orig_shape[0],
          ),
          axis=-1,
      )
  
      # Apply a mask if requested
      if target_shape is not None:
          rotated_boxes = remap_boxes(
              rotated_boxes,
              orig_shape=orig_shape,
              dest_shape=target_shape,
          )
  
      return rotated_boxes
  
file_path: doctr/doctr/utils/geometry.py
imports: from copy import deepcopy
  from math import ceil
  from typing import List, Optional, Tuple, Union
  import cv2
  import numpy
  from .common_types import BoundingBox, Polygon4P
label_desc: Use image node_id 9 for calling a global function with example usage: rotate_boxes(loc_preds, angle, orig_shape, min_angle, target_shape) and returns: rotated_boxes, _boxes