code: def forward(self, input, source_control_points):
      assert source_control_points.ndimension() == 3
      assert source_control_points.shape[1] == self.num_control_points
      assert source_control_points.shape[2] == 2
      batch_size = paddle.shape(source_control_points)[0]
  
      padding_matrix = paddle.expand(
          self.padding_matrix, shape=[batch_size, 3, 2]
      )
      Y = paddle.concat(
          [
              source_control_points.astype(padding_matrix.dtype),
              padding_matrix,
          ],
          1,
      )
      mapping_matrix = paddle.matmul(self.inverse_kernel, Y)
      source_coordinate = paddle.matmul(
          self.target_coordinate_repr, mapping_matrix
      )
  
      grid = paddle.reshape(
          source_coordinate,
          shape=[-1, self.target_height, self.target_width, 2],
      )
      grid = paddle.clip(
          grid, 0, 1
      )  # the source_control_points may be out of [0, 1].
      # the input to grid_sample is normalized [-1, 1], but what we get is [0, 1]
      grid = 2.0 * grid - 1.0
      output_maps = grid_sample(input, grid, canvas=None)
      return output_maps, source_coordinate
  
file_path: PaddleOCR/ppocr/modeling/transforms/tps_spatial_transformer.py
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import math
  import paddle
  from paddle import nn, ParamAttr
  from paddle.nn import functional
  import numpy
  import itertools
label_desc: Use image node_id 2 for calling the TPSSpatialTransformer obj's underlying member method code with example usage: obj.forward(input, source_control_points) and returns: output_maps,  source_coordinate