class_docstr: Implement TPS transform.
  
  This was partially adapted from https://github.com/ayumiymk/aster.pytorch
  
  Args:
      output_image_size (tuple[int, int]): The size of the output image.
          Defaults to (32, 128).
      num_control_points (int): The number of control points. Defaults to 20.
      margins (tuple[float, float]): The margins for control points to the
          top and down side of the image. Defaults to [0.05, 0.05].
class_docstr_tok: ["Implement","TPS","transform",".","This","was","partially","adapted","from","https",":","\/\/github.com\/ayumiymk\/aster.pytorch","Args",":","output_image_size","(","tuple","[","int",",","int","]",")",":","The","size","of","the","output","image",".","Defaults","to","(","32",",","128",")",".","num_control_points","(","int",")",":","The","number","of","control","points",".","Defaults","to","20.","margins","(","tuple","[","float",",","float","]",")",":","The","margins","for","control","points","to","the","top","and","down","side","of","the","image",".","Defaults","to","[","0.05",",","0.05","]","."]
code: def __init__(
      self,
      output_image_size: Tuple[int, int] = (32, 100),
      num_control_points: int = 20,
      margins: Tuple[float, float] = [0.05, 0.05],
  ) -> None:
      super().__init__()
      self.output_image_size = output_image_size
      self.num_control_points = num_control_points
      self.margins = margins
      self.target_height, self.target_width = output_image_size
  
      # build output control points
      target_control_points = self._build_output_control_points(
          num_control_points, margins
      )
      N = num_control_points
  
      # create padded kernel matrix
      forward_kernel = torch.zeros(N + 3, N + 3)
      target_control_partial_repr = self._compute_partial_repr(
          target_control_points, target_control_points
      )
      forward_kernel[:N, :N].copy_(target_control_partial_repr)
      forward_kernel[:N, -3].fill_(1)
      forward_kernel[-3, :N].fill_(1)
      forward_kernel[:N, -2:].copy_(target_control_points)
      forward_kernel[-2:, :N].copy_(
          target_control_points.transpose(0, 1)
      )
  
      # compute inverse matrix
      inverse_kernel = torch.inverse(forward_kernel).contiguous()
  
      # create target coordinate matrix
      HW = self.target_height * self.target_width
      tgt_coord = list(
          itertools.product(
              range(self.target_height), range(self.target_width)
          )
      )
      tgt_coord = torch.Tensor(tgt_coord)
      Y, X = tgt_coord.split(1, dim=1)
      Y = Y / (self.target_height - 1)
      X = X / (self.target_width - 1)
      tgt_coord = torch.cat([X, Y], dim=1)
      tgt_coord_partial_repr = self._compute_partial_repr(
          tgt_coord, target_control_points
      )
      tgt_coord_repr = torch.cat(
          [tgt_coord_partial_repr, torch.ones(HW, 1), tgt_coord], dim=1
      )
  
      # register precomputed matrices
      self.register_buffer("inverse_kernel", inverse_kernel)
      self.register_buffer("padding_matrix", torch.zeros(3, 2))
      self.register_buffer("target_coordinate_repr", tgt_coord_repr)
      self.register_buffer(
          "target_control_points", target_control_points
      )
  
imports: import itertools
  from typing import Dict, List, Optional, Tuple, Union
  import numpy
  import torch
  import torch.nn
  import torch.nn.functional
  from mmcv.cnn import ConvModule
  from mmocr.registry import MODELS
  from .base import BasePreprocessor
label_desc: Use image node_id 1 to create a new TPStransform object from inherited base classes: nn with example: obj = TPStransform(output_image_size, num_control_points, margins)