def get_affine_matrix2d(translations: torch.Tensor, center: torch.Tensor, scale: torch.Tensor, angle: torch.Tensor, sx: Optional[torch.Tensor] = None, sy: Optional[torch.Tensor] = None) -> torch.Tensor: r"""Composes affine matrix Bx3x3 from the components Returns: torch.Tensor: params to be passed to the affine transformation. """ transform: torch.Tensor = get_rotation_matrix2d(center, -angle, scale) transform[..., 2] += translations # tx/ty # pad transform to get Bx3x3 transform_h = convert_affinematrix_to_homography(transform) if sx is not None: x, y = torch.split(center, 1, dim=-1) x = x.view(-1) y = y.view(-1) sx_tan = torch.tan(sx) # type: ignore sy_tan = torch.tan(sy) # type: ignore zeros = torch.zeros_like(sx) # type: ignore ones = torch.ones_like(sx) # type: ignore shear_mat = torch.stack([ones, -sx_tan, sx_tan * x, # type: ignore # noqa: E241 -sy_tan, ones + sx_tan * sy_tan, sy_tan * (-sx_tan * x + y)], # noqa: E241 dim=-1).view(-1, 2, 3) shear_mat = convert_affinematrix_to_homography(shear_mat) transform_h = transform_h @ shear_mat return transform_h
def invert_affine_transform(matrix: torch.Tensor) -> torch.Tensor: r"""Invert an affine transformation. The function computes an inverse affine transformation represented by 2×3 matrix: .. math:: \begin{bmatrix} a_{11} & a_{12} & b_{1} \\ a_{21} & a_{22} & b_{2} \\ \end{bmatrix} The result is also a 2×3 matrix of the same type as M. Args: matrix: original affine transform. The tensor must be in the shape of :math:`(B, 2, 3)`. Return: the reverse affine transform with shape :math:`(B, 2, 3)`. .. note:: This function is often used in conjunction with :func:`warp_affine`. """ if not isinstance(matrix, torch.Tensor): raise TypeError(f"Input matrix type is not a torch.Tensor. Got {type(matrix)}") if not (len(matrix.shape) == 3 and matrix.shape[-2:] == (2, 3)): raise ValueError(f"Input matrix must be a Bx2x3 tensor. Got {matrix.shape}") matrix_tmp: torch.Tensor = convert_affinematrix_to_homography(matrix) matrix_inv: torch.Tensor = torch.inverse(matrix_tmp) return matrix_inv[..., :2, :3]
def invert_affine_transform(matrix: torch.Tensor) -> torch.Tensor: r"""Inverts an affine transformation. The function computes an inverse affine transformation represented by 2×3 matrix: .. math:: \begin{bmatrix} a_{11} & a_{12} & b_{1} \\ a_{21} & a_{22} & b_{2} \\ \end{bmatrix} The result is also a 2×3 matrix of the same type as M. Args: matrix (torch.Tensor): original affine transform. The tensor must be in the shape of (B, 2, 3). Return: torch.Tensor: the reverse affine transform. """ if not torch.is_tensor(matrix): raise TypeError("Input matrix type is not a torch.Tensor. Got {}" .format(type(matrix))) if not (len(matrix.shape) == 3 and matrix.shape[-2:] == (2, 3)): raise ValueError("Input matrix must be a Bx2x3 tensor. Got {}" .format(matrix.shape)) matrix_tmp: torch.Tensor = convert_affinematrix_to_homography(matrix) matrix_inv: torch.Tensor = torch.inverse(matrix_tmp) return matrix_inv[..., :2, :3]
def get_affine_matrix2d( translations: torch.Tensor, center: torch.Tensor, scale: torch.Tensor, angle: torch.Tensor, sx: Optional[torch.Tensor] = None, sy: Optional[torch.Tensor] = None, ) -> torch.Tensor: r"""Composes affine matrix from the components. Args: translations: tensor containing the translation vector with shape :math:`(B, 2)`. center: tensor containing the center vector with shape :math:`(B, 2)`. scale: tensor containing the scale factor with shape :math:`(B, 2)`. angle: tensor of angles in degrees :math:`(B)`. sx: tensor containing the shear factor in the x-direction with shape :math:`(B)`. sy: tensor containing the shear factor in the y-direction with shape :math:`(B)`. Returns: the affine transformation matrix :math:`(B, 3, 3)`. .. note:: This function is often used in conjuntion with :func:`warp_affine`, :func:`warp_perspective`. """ transform: torch.Tensor = get_rotation_matrix2d(center, -angle, scale) transform[..., 2] += translations # tx/ty # pad transform to get Bx3x3 transform_h = convert_affinematrix_to_homography(transform) if any(s is not None for s in [sx, sy]): shear_mat = get_shear_matrix2d(center, sx, sy) transform_h = transform_h @ shear_mat return transform_h
def warp_affine(src: torch.Tensor, M: torch.Tensor, dsize: Tuple[int, int], flags: str = 'bilinear', padding_mode: str = 'zeros', align_corners: bool = False) -> torch.Tensor: r"""Applies an affine transformation to a tensor. The function warp_affine transforms the source tensor using the specified matrix: .. math:: \text{dst}(x, y) = \text{src} \left( M_{11} x + M_{12} y + M_{13} , M_{21} x + M_{22} y + M_{23} \right ) Args: src (torch.Tensor): input tensor of shape :math:`(B, C, H, W)`. M (torch.Tensor): affine transformation of shape :math:`(B, 2, 3)`. dsize (Tuple[int, int]): size of the output image (height, width). mode (str): interpolation mode to calculate output values 'bilinear' | 'nearest'. Default: 'bilinear'. padding_mode (str): padding mode for outside grid values 'zeros' | 'border' | 'reflection'. Default: 'zeros'. align_corners (bool): mode for grid_generation. Default: False. Returns: torch.Tensor: the warped tensor with shape :math:`(B, C, H, W)`. .. note:: See a working example `here <https://kornia.readthedocs.io/en/latest/ tutorials/warp_affine.html>`__. """ if not isinstance(src, torch.Tensor): raise TypeError("Input src type is not a torch.Tensor. Got {}" .format(type(src))) if not isinstance(M, torch.Tensor): raise TypeError("Input M type is not a torch.Tensor. Got {}" .format(type(M))) if not len(src.shape) == 4: raise ValueError("Input src must be a BxCxHxW tensor. Got {}" .format(src.shape)) if not (len(M.shape) == 3 or M.shape[-2:] == (2, 3)): raise ValueError("Input M must be a Bx2x3 tensor. Got {}" .format(M.shape)) B, C, H, W = src.size() dsize_src = (H, W) out_size = dsize # we generate a 3x3 transformation matrix from 2x3 affine M_3x3: torch.Tensor = convert_affinematrix_to_homography(M) dst_norm_trans_src_norm: torch.Tensor = normalize_homography( M_3x3, dsize_src, out_size) src_norm_trans_dst_norm = torch.inverse(dst_norm_trans_src_norm) grid = F.affine_grid(src_norm_trans_dst_norm[:, :2, :], [B, C, out_size[0], out_size[1]], align_corners=align_corners) return F.grid_sample(src, grid, align_corners=align_corners, mode=flags, padding_mode=padding_mode)
def get_affine_matrix2d(translations: torch.Tensor, center: torch.Tensor, scale: torch.Tensor, angle: torch.Tensor, sx: Optional[torch.Tensor] = None, sy: Optional[torch.Tensor] = None) -> torch.Tensor: r"""Composes affine matrix from the components. Args: translations (torch.Tensor): tensor containing the translation vector with shape :math:`(B, 2)`. center (torch.Tensor): tensor containing the center vector with shape :math:`(B, 2)`. scale (torch.Tensor): tensor containing the scale factor with shape :math:`(B, 2)`. sx (torch.Tensor, optional): tensor containing the shear factor in the x-direction with shape :math:`(B)`. sy (torch.Tensor, optional): tensor containing the shear factor in the y-direction with shape :math:`(B)`. Returns: torch.Tensor: the affine transformation matrix :math:`(B, 2, 3)`. """ transform: torch.Tensor = get_rotation_matrix2d(center, -angle, scale) transform[..., 2] += translations # tx/ty # pad transform to get Bx3x3 transform_h = convert_affinematrix_to_homography(transform) if any([s is not None for s in [sx, sy]]): shear_mat = get_shear_matrix2d(center, sx, sy) transform_h = transform_h @ shear_mat return transform_h
def get_shear_matrix2d(center: torch.Tensor, sx: Optional[torch.Tensor] = None, sy: Optional[torch.Tensor] = None): r"""Composes shear matrix Bx4x4 from the components. Note: Ordered shearing, shear x-axis then y-axis. .. math:: \begin{bmatrix} 1 & b \\ a & ab + 1 \\ \end{bmatrix} Args: center: shearing center coordinates of (x, y). sx: shearing degree along x axis. sy: shearing degree along y axis. Returns: params to be passed to the affine transformation with shape :math:`(B, 3, 3)`. Examples: >>> rng = torch.manual_seed(0) >>> sx = torch.randn(1) >>> sx tensor([1.5410]) >>> center = torch.tensor([[0., 0.]]) # Bx2 >>> get_shear_matrix2d(center, sx=sx) tensor([[[ 1.0000, -33.5468, 0.0000], [ -0.0000, 1.0000, 0.0000], [ 0.0000, 0.0000, 1.0000]]]) .. note:: This function is often used in conjuntion with :func:`warp_affine`, :func:`warp_perspective`. """ sx = torch.tensor([0.0]).repeat(center.size(0)) if sx is None else sx sy = torch.tensor([0.0]).repeat(center.size(0)) if sy is None else sy x, y = torch.split(center, 1, dim=-1) x, y = x.view(-1), y.view(-1) sx_tan = torch.tan(sx) # type: ignore sy_tan = torch.tan(sy) # type: ignore ones = torch.ones_like(sx) # type: ignore shear_mat = torch.stack( [ ones, -sx_tan, sx_tan * y, # type: ignore # noqa: E241 -sy_tan, ones + sx_tan * sy_tan, sy_tan * (sx_tan * y + x), # noqa: E241 ], dim=-1, ).view(-1, 2, 3) shear_mat = convert_affinematrix_to_homography(shear_mat) return shear_mat
def forward(self) -> torch.Tensor: r"""Single-batch similarity transform". Returns: Similarity with shape :math:`(1, 3, 3)`""" rot = self.scale * angle_to_rotation_matrix(self.rot) out = convert_affinematrix_to_homography( torch.cat([rot, self.shift], dim=2)) return out
def warp_affine( src: torch.Tensor, M: torch.Tensor, dsize: Tuple[int, int], mode: str = 'bilinear', padding_mode: str = 'zeros', align_corners: Optional[bool] = None, ) -> torch.Tensor: r"""Applies an affine transformation to a tensor. .. image:: _static/img/warp_affine.png The function warp_affine transforms the source tensor using the specified matrix: .. math:: \text{dst}(x, y) = \text{src} \left( M_{11} x + M_{12} y + M_{13} , M_{21} x + M_{22} y + M_{23} \right ) Args: src: input tensor of shape :math:`(B, C, H, W)`. M: affine transformation of shape :math:`(B, 2, 3)`. dsize: size of the output image (height, width). mode: interpolation mode to calculate output values ``'bilinear'`` | ``'nearest'``. padding_mode (str): padding mode for outside grid values ``'zeros'`` | ``'border'`` | ``'reflection'``. align_corners : mode for grid_generation. Returns: the warped tensor with shape :math:`(B, C, H, W)`. Example: >>> img = torch.rand(1, 4, 5, 6) >>> A = torch.eye(2, 3)[None] >>> out = warp_affine(img, A, (4, 2), align_corners=True) >>> print(out.shape) torch.Size([1, 4, 4, 2]) .. note:: This function is often used in conjuntion with :func:`get_rotation_matrix2d`, :func:`get_shear_matrix2d`, :func:`get_affine_matrix2d`, :func:`invert_affine_transform`. .. note:: See a working example `here <https://kornia.readthedocs.io/en/latest/ tutorials/warp_affine.html>`__. """ if not isinstance(src, torch.Tensor): raise TypeError("Input src type is not a torch.Tensor. Got {}".format( type(src))) if not isinstance(M, torch.Tensor): raise TypeError("Input M type is not a torch.Tensor. Got {}".format( type(M))) if not len(src.shape) == 4: raise ValueError("Input src must be a BxCxHxW tensor. Got {}".format( src.shape)) if not (len(M.shape) == 3 or M.shape[-2:] == (2, 3)): raise ValueError("Input M must be a Bx2x3 tensor. Got {}".format( M.shape)) # TODO: remove the statement below in kornia v0.6 if align_corners is None: message: str = ( "The align_corners default value has been changed. By default now is set True " "in order to match cv2.warpAffine. In case you want to keep your previous " "behaviour set it to False. This warning will disappear in kornia > v0.6." ) warnings.warn(message) # set default value for align corners align_corners = True B, C, H, W = src.size() # we generate a 3x3 transformation matrix from 2x3 affine M_3x3: torch.Tensor = convert_affinematrix_to_homography(M) dst_norm_trans_src_norm: torch.Tensor = normalize_homography( M_3x3, (H, W), dsize) # src_norm_trans_dst_norm = torch.inverse(dst_norm_trans_src_norm) src_norm_trans_dst_norm = _torch_inverse_cast(dst_norm_trans_src_norm) grid = F.affine_grid(src_norm_trans_dst_norm[:, :2, :], [B, C, dsize[0], dsize[1]], align_corners=align_corners) return F.grid_sample(src, grid, align_corners=align_corners, mode=mode, padding_mode=padding_mode)
def warp_affine( src: torch.Tensor, M: torch.Tensor, dsize: Tuple[int, int], mode: str = 'bilinear', padding_mode: str = 'zeros', align_corners: bool = True, ) -> torch.Tensor: r"""Apply an affine transformation to a tensor. .. image:: _static/img/warp_affine.png The function warp_affine transforms the source tensor using the specified matrix: .. math:: \text{dst}(x, y) = \text{src} \left( M_{11} x + M_{12} y + M_{13} , M_{21} x + M_{22} y + M_{23} \right ) Args: src: input tensor of shape :math:`(B, C, H, W)`. M: affine transformation of shape :math:`(B, 2, 3)`. dsize: size of the output image (height, width). mode: interpolation mode to calculate output values ``'bilinear'`` | ``'nearest'``. padding_mode (str): padding mode for outside grid values ``'zeros'`` | ``'border'`` | ``'reflection'``. align_corners : mode for grid_generation. Returns: the warped tensor with shape :math:`(B, C, H, W)`. .. note:: This function is often used in conjunction with :func:`get_rotation_matrix2d`, :func:`get_shear_matrix2d`, :func:`get_affine_matrix2d`, :func:`invert_affine_transform`. .. note:: See a working example `here <https://kornia-tutorials.readthedocs.io/en/latest/ rotate_affine.html>`__. Example: >>> img = torch.rand(1, 4, 5, 6) >>> A = torch.eye(2, 3)[None] >>> out = warp_affine(img, A, (4, 2), align_corners=True) >>> print(out.shape) torch.Size([1, 4, 4, 2]) """ if not isinstance(src, torch.Tensor): raise TypeError(f"Input src type is not a torch.Tensor. Got {type(src)}") if not isinstance(M, torch.Tensor): raise TypeError(f"Input M type is not a torch.Tensor. Got {type(M)}") if not len(src.shape) == 4: raise ValueError(f"Input src must be a BxCxHxW tensor. Got {src.shape}") if not (len(M.shape) == 3 or M.shape[-2:] == (2, 3)): raise ValueError(f"Input M must be a Bx2x3 tensor. Got {M.shape}") B, C, H, W = src.size() # we generate a 3x3 transformation matrix from 2x3 affine M_3x3: torch.Tensor = convert_affinematrix_to_homography(M) dst_norm_trans_src_norm: torch.Tensor = normalize_homography(M_3x3, (H, W), dsize) # src_norm_trans_dst_norm = torch.inverse(dst_norm_trans_src_norm) src_norm_trans_dst_norm = _torch_inverse_cast(dst_norm_trans_src_norm) grid = F.affine_grid(src_norm_trans_dst_norm[:, :2, :], [B, C, dsize[0], dsize[1]], align_corners=align_corners) return F.grid_sample(src, grid, align_corners=align_corners, mode=mode, padding_mode=padding_mode)