code: def rotated_img_tensor(
      img: tf.Tensor, angle: float, expand: bool = False
  ) -> tf.Tensor:
      """Rotate image around the center, interpolation=NEAREST, pad with 0 (black)
  
      Args:
      ----
          img: image to rotate
          angle: angle in degrees. +: counter-clockwise, -: clockwise
          expand: whether the image should be padded before the rotation
  
      Returns:
      -------
          the rotated image (tensor)
      """
      # Compute the expanded padding
      h_crop, w_crop = 0, 0
      if expand:
          exp_h, exp_w = compute_expanded_shape(img.shape[:-1], angle)
          h_diff, w_diff = int(math.ceil(exp_h - img.shape[0])), int(
              math.ceil(exp_w - img.shape[1])
          )
          h_pad, w_pad = max(h_diff, 0), max(w_diff, 0)
          exp_img = tf.pad(
              img,
              tf.constant(
                  [
                      [h_pad // 2, h_pad - h_pad // 2],
                      [w_pad // 2, w_pad - w_pad // 2],
                      [0, 0],
                  ]
              ),
          )
          h_crop, w_crop = int(
              round(max(exp_img.shape[0] - exp_h, 0))
          ), int(round(min(exp_img.shape[1] - exp_w, 0)))
      else:
          exp_img = img
  
      # Compute the rotation matrix
      height, width = tf.cast(
          tf.shape(exp_img)[0], tf.float32
      ), tf.cast(tf.shape(exp_img)[1], tf.float32)
      cos_angle, sin_angle = tf.math.cos(
          angle * math.pi / 180.0
      ), tf.math.sin(angle * math.pi / 180.0)
      x_offset = (
          (width - 1)
          - (cos_angle * (width - 1) - sin_angle * (height - 1))
      ) / 2.0
      y_offset = (
          (height - 1)
          - (sin_angle * (width - 1) + cos_angle * (height - 1))
      ) / 2.0
  
      rotation_matrix = tf.convert_to_tensor(
          [
              cos_angle,
              -sin_angle,
              x_offset,
              sin_angle,
              cos_angle,
              y_offset,
              0.0,
              0.0,
          ],
          dtype=tf.float32,
      )
      # Rotate the image
      rotated_img = tf.squeeze(
          tf.raw_ops.ImageProjectiveTransformV3(
              images=exp_img[
                  None
              ],  # Add a batch dimension for compatibility with ImageProjectiveTransformV3
              transforms=rotation_matrix[
                  None
              ],  # Add a batch dimension for compatibility with ImageProjectiveTransformV3
              output_shape=tf.shape(exp_img)[:2],
              interpolation="NEAREST",
              fill_mode="CONSTANT",
              fill_value=tf.constant(0.0, dtype=tf.float32),
          )
      )
      # Crop the rest
      if h_crop > 0 or w_crop > 0:
          h_slice = (
              slice(h_crop // 2, -h_crop // 2)
              if h_crop > 0
              else slice(rotated_img.shape[0])
          )
          w_slice = (
              slice(-w_crop // 2, -w_crop // 2)
              if w_crop > 0
              else slice(rotated_img.shape[1])
          )
          rotated_img = rotated_img[h_slice, w_slice]
  
      return rotated_img
  
file_path: doctr/doctr/transforms/functional/tensorflow.py
imports: import math
  import random
  from copy import deepcopy
  from typing import Iterable, Optional, Tuple, Union
  import numpy
  import tensorflow
  from doctr.utils.geometry import compute_expanded_shape, rotate_abs_geoms
  from .base import create_shadow_mask, crop_boxes
label_desc: Use image node_id 2 for calling a global function with example usage: rotated_img_tensor(img, angle, expand) and returns: rotated_img