code: def _gen_affine_grid(
      theta: Tensor,
      w: int,
      h: int,
      ow: int,
      oh: int,
  ) -> Tensor:
      # https://github.com/pytorch/pytorch/blob/74b65c32be68b15dc7c9e8bb62459efbfbde33d8/aten/src/ATen/native/
      # AffineGridGenerator.cpp#L18
      # Difference with AffineGridGenerator is that:
      # 1) we normalize grid values after applying theta
      # 2) we can normalize by other image size, such that it covers "extend" option like in PIL.Image.rotate
  
      d = 0.5
      base_grid = torch.empty(
          1, oh, ow, 3, dtype=theta.dtype, device=theta.device
      )
      x_grid = torch.linspace(
          -ow * 0.5 + d, ow * 0.5 + d - 1, steps=ow, device=theta.device
      )
      base_grid[..., 0].copy_(x_grid)
      y_grid = torch.linspace(
          -oh * 0.5 + d, oh * 0.5 + d - 1, steps=oh, device=theta.device
      ).unsqueeze_(-1)
      base_grid[..., 1].copy_(y_grid)
      base_grid[..., 2].fill_(1)
  
      rescaled_theta = theta.transpose(1, 2) / torch.tensor(
          [0.5 * w, 0.5 * h], dtype=theta.dtype, device=theta.device
      )
      output_grid = base_grid.view(1, oh * ow, 3).bmm(rescaled_theta)
      return output_grid.view(1, oh, ow, 2)
  
imports: import warnings
  from typing import List, Optional, Tuple, Union
  import torch
  from torch import Tensor
  from torch.nn.functional import conv2d, grid_sample, interpolate, pad
label_desc: Use image node_id 29 for calling a global function with example usage: _gen_affine_grid(theta, w, h, ow, oh) and returns: output_grid