code: def perspective_keypoint(
      keypoint: KeypointInternalType,
      height: int,
      width: int,
      matrix: np.ndarray,
      max_width: int,
      max_height: int,
      keep_size: bool,
  ) -> KeypointInternalType:
      x, y, angle, scale = keypoint
  
      keypoint_vector = np.array([x, y], dtype=np.float32).reshape(
          [1, 1, 2]
      )
  
      x, y = cv2.perspectiveTransform(keypoint_vector, matrix)[0, 0]
      angle += rotation2DMatrixToEulerAngles(matrix[:2, :2], y_up=True)
  
      scale_x = np.sign(matrix[0, 0]) * np.sqrt(
          matrix[0, 0] ** 2 + matrix[0, 1] ** 2
      )
      scale_y = np.sign(matrix[1, 1]) * np.sqrt(
          matrix[1, 0] ** 2 + matrix[1, 1] ** 2
      )
      scale *= max(scale_x, scale_y)
  
      if keep_size:
          scale_x = width / max_width
          scale_y = height / max_height
          return keypoint_scale((x, y, angle, scale), scale_x, scale_y)
  
      return x, y, angle, scale
  
imports: import math
  from typing import List, Optional, Sequence, Tuple, Union
  import cv2
  import numpy
  import skimage.transform
  from scipy.ndimage import gaussian_filter
  from albumentations.augmentations.utils import _maybe_process_in_chunks, angle_2pi_range, clipped, preserve_channel_dim, preserve_shape
  from ...None import random_utils
  from ...core.bbox_utils import denormalize_bbox, normalize_bbox
  from ...core.transforms_interface import BoxInternalType, FillValueType, ImageColorType, KeypointInternalType
label_desc: Use image node_id 20 for calling a global function with example usage: perspective_keypoint(keypoint, height, width, matrix, max_width, max_height, keep_size) and returns: x,  y,  angle,  scale, keypoint_scale