kornia.geometry.conversions#

Angles#

kornia.geometry.conversions.rad2deg(tensor)[source]#

Convert angles from radians to degrees.

Convention:
  • the input is in radians and the output in degrees; the conversion is elementwise and preserves shape, device and float dtype

Parameters:

tensor (Tensor) – torch.Tensor of arbitrary shape.

Return type:

Tensor

Returns:

torch.Tensor with same shape as input.

Example

>>> input = torch.tensor(3.1415926535)
>>> rad2deg(input)
tensor(180.)
kornia.geometry.conversions.deg2rad(tensor)[source]#

Convert angles from degrees to radians.

Convention:
  • the input is in degrees and the output in radians; it performs the opposite conversion to rad2deg()

Parameters:

tensor (Tensor) – torch.Tensor of arbitrary shape.

Return type:

Tensor

Returns:

tensor with same shape as input.

Examples

>>> input = torch.tensor(180.)
>>> deg2rad(input)
tensor(3.1416)
kornia.geometry.conversions.pol2cart(rho, phi)[source]#

Convert polar coordinates to cartesian coordinates.

Convention:
  • the arguments are (rho, phi) and the return is the tuple (x, y), with x = rho * cos(phi) and y = rho * sin(phi): rho = 5, phi = atan2(4, 3) gives (3., 4.)

  • phi is in radians (rho = 2, phi = pi / 6 gives (1.7321, 1.)) and follows the angle convention documented on cart2pol()

Parameters:
  • rho (Tensor) – torch.Tensor of arbitrary shape.

  • phi (Tensor) – torch.Tensor of same arbitrary shape.

Returns:

torch.Tensor with same shape as input. - y: torch.Tensor with same shape as input.

Return type:

  • x

Example

>>> rho = torch.rand(1, 3, 3)
>>> phi = torch.rand(1, 3, 3)
>>> x, y = pol2cart(rho, phi)
kornia.geometry.conversions.cart2pol(x, y, eps=1.0e-8)[source]#

Convert cartesian coordinates to polar coordinates.

Convention:
  • the arguments are (x, y) and the return is the tuple (rho, phi): (3., 4.) gives (5., 0.9273)

  • phi is atan2(y, x) in radians, so it lies in \([-\pi, \pi]\), is 0 on the +x axis and grows toward +y (x = 0, y = 1 gives phi = 1.5708). Both endpoints are attained on the -x axis, where the sign of y’s zero decides between -pi and +pi

  • kornia’s image y axis points down, so a growing phi turns clockwise as displayed. The relation to the other 2-D angle op is the opposite sense: applying angle_to_rotation_matrix(theta) to a nonzero point decreases that point’s phi by theta degrees modulo \(2\pi\) — the result is re-wrapped into \([-\pi, \pi]\), so a point at phi = -170 degrees rotated by theta = 30 returns phi = +160, not -200. At the origin phi carries no direction and the relation does not apply

  • rho is the Euclidean radius sqrt(x ** 2 + y ** 2). eps only protects the gradient near the origin and does not perturb the returned radius

Note

eps defines the neighborhood around the origin where the gradient is stabilized. It does not change the forward value of rho.

Parameters:
  • x (Tensor) – torch.Tensor of arbitrary shape.

  • y (Tensor) – torch.Tensor of same arbitrary shape.

  • eps (float, optional) – squared-radius threshold used to stabilize the gradient near the origin. It does not perturb the forward value of rho. The default 1e-8 underflows in float16. Default: 1.0e-8

Returns:

torch.Tensor with same shape as input. - phi: torch.Tensor with same shape as input.

Return type:

  • rho

Example

>>> x = torch.rand(1, 3, 3)
>>> y = torch.rand(1, 3, 3)
>>> rho, phi = cart2pol(x, y)
kornia.geometry.conversions.angle_to_rotation_matrix(angle)[source]#

Create a rotation matrix out of angles in degrees.

Convention:
  • angle is in degrees, shape \((*)\) in and \((*, 2, 2)\) out

  • the matrix is [[cos, sin], [-sin, cos]] with det = +1; for angle = 30 it is [[0.8660, 0.5000], [-0.5000, 0.8660]]

  • applied on the left to a column vector (x, y), angle = 30 maps (1., 0.) to (0.8660, -0.5000) — counter-clockwise as displayed under kornia’s y-down image axes. In the raw coordinate plane this is the opposite sense to cart2pol(), whose Convention block spells out the modulo-\(2\pi\) relation between the two ops

Parameters:

angle (Tensor) – tensor of angles in degrees, any shape \((*)\).

Return type:

Tensor

Returns:

tensor of rotation matrices with shape \((*, 2, 2)\).

Example

>>> input = torch.rand(1, 3)  # Nx3
>>> output = angle_to_rotation_matrix(input)  # Nx3x2x2

Coordinates#

kornia.geometry.conversions.convert_points_from_homogeneous(points, eps=1e-8)[source]#

Convert points from homogeneous to Euclidean space.

Convention:
  • the last component of the last dimension is the homogeneous coordinate w and is dropped from the output

  • requires rank \(\geq 2\): a bare \((D,)\) point raises ValueError

  • when abs(w) > eps the remaining components are divided by exactly w, with its sign kept ([[2., 4., -2.]] gives [[-1., -2.]])

  • when abs(w) <= eps (default eps = 1e-8; the test is a strict >) the numerator is instead returned unchanged, following OpenCV: [[2., 4., 0.]] gives [[2., 4.]]

  • eps only picks the branch, it never enters the division, so the result carries no bias from it. It is compared against w in the working dtype

Parameters:
  • points (Tensor) – the points to be transformed of shape \((*, N, D)\).

  • eps (float, optional) – threshold on abs(w) below which the point is returned unchanged instead of divided, to avoid division by zero. Default: 1e-8

Return type:

Tensor

Returns:

the points in Euclidean space \((*, N, D-1)\).

Examples

>>> input = torch.tensor([[0., 0., 1.]])
>>> convert_points_from_homogeneous(input)
tensor([[0., 0.]])
kornia.geometry.conversions.convert_points_to_homogeneous(points)[source]#

Convert points from Euclidean to homogeneous space.

Convention:
  • appends the constant 1 as the last component of the last dimension, which is where convert_points_from_homogeneous() expects it

  • requires rank \(\geq 2\): a bare \((D,)\) point raises ValueError

  • the input dtype is preserved, integer dtypes included

Parameters:

points (Tensor) – the points to be transformed with shape \((*, N, D)\).

Return type:

Tensor

Returns:

the points in homogeneous coordinates \((*, N, D+1)\).

Examples

>>> input = torch.tensor([[0., 0.]])
>>> convert_points_to_homogeneous(input)
tensor([[0., 0., 1.]])
kornia.geometry.conversions.convert_affinematrix_to_homography(A)[source]#

Convert batch of affine matrices.

Convention:
  • appends the row [0, 0, 1] at the bottom and copies the \(2 \times 3\) block verbatim; the input tensor is not modified

  • the rank is enforced to be exactly 3, so batching is mandatory: an unbatched \((2, 3)\) matrix raises ValueError

Parameters:

A (Tensor) – the affine matrix with shape \((B,2,3)\).

Return type:

Tensor

Returns:

the homography matrix with shape of \((B,3,3)\).

Examples

>>> A = torch.tensor([[[1., 0., 0.],
...                    [0., 1., 0.]]])
>>> convert_affinematrix_to_homography(A)
tensor([[[1., 0., 0.],
         [0., 1., 0.],
         [0., 0., 1.]]])
kornia.geometry.conversions.convert_affinematrix_to_homography3d(A)[source]#

Convert batch of 3d affine matrices.

Convention:
Parameters:

A (Tensor) – the affine matrix with shape \((B,3,4)\).

Return type:

Tensor

Returns:

the homography matrix with shape of \((B,4,4)\).

Examples

>>> A = torch.tensor([[[1., 0., 0., 0.],
...                    [0., 1., 0., 0.],
...                    [0., 0., 1., 0.]]])
>>> convert_affinematrix_to_homography3d(A)
tensor([[[1., 0., 0., 0.],
         [0., 1., 0., 0.],
         [0., 0., 1., 0.],
         [0., 0., 0., 1.]]])
kornia.geometry.conversions.denormalize_pixel_coordinates(pixel_coordinates, height, width, eps=1e-8)[source]#

Denormalize pixel coordinates.

The input is assumed to be -1 if on extreme left, 1 if on extreme right (x = w-1).

Convention:

See Conventions & Pitfalls for the library-wide pixel-centre, normalized-coordinate and align_corners conventions.

  • the inverse of normalize_pixel_coordinates(), x = (width - 1) * (x_norm + 1) / 2, with the same (x, y) component order and the same (pixel_coordinates, height, width) positional order

  • singleton axes, non-positive-size validation, tracing assumptions, and the deprecated eps parameter follow the normalization function

Parameters:
  • pixel_coordinates (Tensor) – the normalized grid coordinates. Shape can be \((*, 2)\).

  • height (int) – the maximum height in the y-axis.

  • width (int) – the maximum width in the x-axis.

  • eps (float, optional) – deprecated compatibility parameter. It is ignored. Default: 1e-8

Return type:

Tensor

Returns:

the denormalized pixel coordinates with shape \((*, 2)\).

Examples

>>> coords = torch.tensor([[-1., -1.]])
>>> denormalize_pixel_coordinates(coords, 100, 50)
tensor([[0., 0.]])
kornia.geometry.conversions.normalize_pixel_coordinates(pixel_coordinates, height, width, eps=1e-8)[source]#

Map pixel coordinates so that the first and last pixel of each axis become -1 and 1.

Convention:

See Conventions & Pitfalls for the library-wide pixel-centre, normalized-coordinate and align_corners conventions.

  • pixel_coordinates is \((*, 2)\) in (x, y) order: x indexes columns and is scaled by width, y indexes rows and is scaled by height. The positional argument order is the other way round — (pixel_coordinates, height, width)

  • the mapping is corner-aligned, x_norm = 2 * x / (width - 1) - 1: for width = 4 the columns 0, 1, 3 map to -1, -1/3, +1. This is the align_corners=True convention; torch.nn.functional.grid_sample() at its default align_corners=False would instead place the first and last of those values at pixels -0.5 and 3.5 — half a pixel outside the image on each side — so align_corners=True must be passed explicitly when feeding this output to it. Note that kornia’s own remap() resolves align_corners=None to False

  • a singleton axis uses scale 1 and offset 0: its only valid pixel coordinate maps to the normalized centre, and the unit-scale extension keeps this function exactly invertible outside that lone valid coordinate. Zero and negative sizes raise ValueError

  • the output is not clamped: coordinates outside the image extrapolate linearly past [-1, 1], as the example below shows

Note

Legacy torch.jit.trace graphs assume positive runtime sizes because tracing cannot retain the Python ValueError validation. Eager, TorchScript, and torch.compile calls validate sizes.

Deprecated since version 0.9.0: eps no longer participates in the explicitly defined singleton-axis mapping. Passing a non-default value warns in eager mode; the parameter will be removed in a future breaking release.

Parameters:
  • pixel_coordinates (Tensor) – the grid with pixel coordinates. Shape can be \((*, 2)\).

  • height (int) – the maximum height in the y-axis.

  • width (int) – the maximum width in the x-axis.

  • eps (float, optional) – deprecated compatibility parameter. It is ignored. Default: 1e-8

Return type:

Tensor

Returns:

the normalized pixel coordinates with shape \((*, 2)\).

Examples

>>> coords = torch.tensor([[50., 100.]])
>>> normalize_pixel_coordinates(coords, 100, 50)
tensor([[1.0408, 1.0202]])
kornia.geometry.conversions.denormalize_pixel_coordinates3d(pixel_coordinates, depth, height, width, eps=1e-8)[source]#

Denormalize 3d pixel coordinates.

Convention:
  • the inverse of normalize_pixel_coordinates3d(), with the same (d, x, y) component order and the same (pixel_coordinates, depth, height, width) positional order: with depth=3, height=5, width=9 the input [0., 0., 0.] maps back to [1., 4., 2.]

  • singleton axes, non-positive-size validation, tracing assumptions, and the deprecated eps parameter follow the 2-D counterpart

Parameters:
  • pixel_coordinates (Tensor) – the normalized grid coordinates. Shape can be \((*, 3)\).

  • depth (int) – the maximum depth in the z-axis.

  • height (int) – the maximum height in the y-axis.

  • width (int) – the maximum width in the x-axis.

  • eps (float, optional) – deprecated compatibility parameter. It is ignored. Default: 1e-8

Return type:

Tensor

Returns:

the denormalized pixel coordinates.

kornia.geometry.conversions.normalize_pixel_coordinates3d(pixel_coordinates, depth, height, width, eps=1e-8)[source]#

Map 3d pixel coordinates so that the first and last sample of each axis become -1 and 1.

Convention:
  • pixel_coordinates is \((*, 3)\) in (d, x, y) order — depth first, then x, then y, not (x, y, z). This is the order create_meshgrid3d() produces. The three components are scaled by depth - 1, width - 1 and height - 1 respectively, so with depth=3, height=5, width=9 the coordinate [2., 8., 4.] maps to [1., 1., 1.]

  • the positional argument order is (pixel_coordinates, depth, height, width)

  • corner-aligned and unclamped in each axis exactly as in normalize_pixel_coordinates()

  • singleton axes, non-positive-size validation, tracing assumptions, and the deprecated eps parameter likewise follow that function

Parameters:
  • pixel_coordinates (Tensor) – the grid with pixel coordinates. Shape can be \((*, 3)\).

  • depth (int) – the maximum depth in the z-axis.

  • height (int) – the maximum height in the y-axis.

  • width (int) – the maximum width in the x-axis.

  • eps (float, optional) – deprecated compatibility parameter. It is ignored. Default: 1e-8

Return type:

Tensor

Returns:

the normalized pixel coordinates.

kornia.geometry.conversions.normalize_points_with_intrinsics(point_2d, camera_matrix)[source]#

Normalize points with intrinsics. Useful for conversion of keypoints to be used with essential matrix.

Convention:

See camera and world conventions for the camera intrinsics and pixel-centre conventions used by these coordinates.

  • point_2d is \((*, 2)\) in pixel (u, v) order and camera_matrix is a row-major pinhole \((*, 3, 3)\) with fx = K[0, 0], fy = K[1, 1], cx = K[0, 2], cy = K[1, 2]

  • the output is the calibrated camera coordinate x = (u - cx) / fx, y = (v - cy) / fy: with K = [[100., 0., 320.], [0., 200., 240.], [0., 0., 1.]] the pixel (420., 440.) maps to (1., 1.)

  • the skew entry K[0, 1] is ignored; only the two diagonal entries and the [:2, 2] column are read. This is a design choice: kornia’s functional pinhole model is zero-skew, like OpenCV’s, and project_points() reads K the same way. For a skewed K apply inv(K) directly

Parameters:
  • point_2d (Tensor) – tensor containing the 2d points in the image pixel coordinates. The shape of the tensor can be \((*, 2)\).

  • camera_matrix (Tensor) – tensor containing the intrinsics camera matrix. The tensor shape must be \((*, 3, 3)\).

Return type:

Tensor

Returns:

tensor of normalized camera coordinates \((x, y)\) with shape \((*, 2)\).

Example

>>> _ = torch.manual_seed(0)
>>> X = torch.rand(1, 2)
>>> K = torch.eye(3)[None]
>>> normalize_points_with_intrinsics(X, K)
tensor([[0.4963, 0.7682]])
kornia.geometry.conversions.denormalize_points_with_intrinsics(point_2d_norm, camera_matrix)[source]#

Denormalize points with intrinsics. Useful for converting normalized camera points back to pixels.

Convention:

See camera and world conventions for the camera intrinsics and pixel-centre conventions used by these coordinates.

Parameters:
  • point_2d_norm (Tensor) – tensor containing the 2d points in normalized camera coordinates. The shape of the tensor can be \((*, 2)\).

  • camera_matrix (Tensor) – tensor containing the intrinsics camera matrix. The tensor shape must be \((*, 3, 3)\).

Return type:

Tensor

Returns:

tensor of \((u, v)\) pixel coordinates with shape \((*, 2)\).

Example

>>> _ = torch.manual_seed(0)
>>> X = torch.rand(1, 2)
>>> K = torch.eye(3)[None]
>>> denormalize_points_with_intrinsics(X, K)
tensor([[0.4963, 0.7682]])

Homography#

kornia.geometry.conversions.normalize_homography(dst_pix_trans_src_pix, dsize_src, dsize_dst, align_corners=True)[source]#

Normalize a given homography in pixels to [-1, 1].

Convention:
  • the input maps source pixels to destination pixels and the output maps normalized source to normalized destination: exactly N_dst @ H @ inv(N_src) with N from normal_transform_pixel(), so dsize_src drives the right factor and dsize_dst the left one. A +2 pixel shift in x on a 5-wide image comes back as +1.0. denormalize_homography() computes the reverse inv(N_dst) @ H @ N_src

  • both dsize arguments are (height, width), while the matrix acts on (x, y, 1) column vectors

  • a \((3, 3)\) or \((B, 3, 3)\) matrix is accepted and batching is per sample; an unbatched input is returned as (1, 3, 3), and any other shape raises ValueError

  • in eager mode inv(N_src) is a closed-form adjugate built from torch.linalg.cross, so no cusolver is needed. A float16 or bfloat16 matrix is inverted in float32 and the result cast back: the float16 determinant of a large image’s normalization matrix is subnormal or zero, and some backends lack a bfloat16 cross kernel (MPS on torch 2.5.1). For any other dtype, a backend without a cross kernel for it raises here

Warning

An integer matrix is not rejected: the normalization matrices are cast to it and truncated, and the call then fails inside torch or returns nan, depending on the backend. Tracked in #3959.

Warning

The \([-1, 1]\) frames follow this function’s own align_corners, which defaults to True (corner-aligned) — not the False that torch.nn.functional.grid_sample() and homography_warp() default to. The value passed here must match the grid_sample call that consumes the result; a mismatch applies a spurious sub-pixel scale and shift. warp_perspective(), warp_affine() and homography_warp() forward their own flag, so they are consistent under both settings; see #3904. normalize_homography3d() still has no align_corners parameter and is corner-aligned unconditionally, so warp_affine3d() keeps this mismatch at align_corners=False. Tracked in #4503.

Parameters:
  • dst_pix_trans_src_pix (Tensor) – homography/ies from source to destination to be normalized. \((B, 3, 3)\), or an unbatched \((3, 3)\), which is promoted to \((1, 3, 3)\)

  • dsize_src (tuple[int, int]) – size of the source image (height, width).

  • dsize_dst (tuple[int, int]) – size of the destination image (height, width).

  • align_corners (bool, optional) – which torch.nn.functional.grid_sample() convention the normalized \([-1, 1]\) coordinates follow, forwarded to normal_transform_pixel(). Must match the align_corners of the grid_sample call that ultimately consumes the result, otherwise the warp picks up a spurious sub-pixel scale and shift. Default: True

Return type:

Tensor

Returns:

the normalized homography of shape \((B, 3, 3)\).

kornia.geometry.conversions.denormalize_homography(dst_pix_trans_src_pix, dsize_src, dsize_dst, align_corners=True)[source]#

De-normalize a given homography in pixels from [-1, 1] to actual height and width.

Convention:
  • the mirror of normalize_homography(): the composition is inv(N_dst) @ H @ N_src, so the input maps normalized source to normalized destination and the output maps source pixels to destination pixels. The dsize roles, the (x, y, 1) column vectors, per-sample batching, the align_corners-selected frames and the shape guard are as documented there, including its integer-input (#3959) warning

  • inv(N_dst) goes through torch.linalg.inv in eager mode (so cusolver on cuda), not through the cross-based adjugate

  • the two functions invert each other to a small multiple of the working dtype’s eps times the largest entry, not bitwise in general

Parameters:
  • dst_pix_trans_src_pix (Tensor) – homography/ies from source to destination to be denormalized. \((B, 3, 3)\), or an unbatched \((3, 3)\), which is promoted to \((1, 3, 3)\)

  • dsize_src (tuple[int, int]) – size of the source image (height, width).

  • dsize_dst (tuple[int, int]) – size of the destination image (height, width).

  • align_corners (bool, optional) – which torch.nn.functional.grid_sample() convention the incoming normalized coordinates follow. Must match the value used to produce them, so that this function inverts normalize_homography(). Default: True

Return type:

Tensor

Returns:

the denormalized homography of shape \((B, 3, 3)\).

kornia.geometry.conversions.normalize_homography3d(dst_pix_trans_src_pix, dsize_src, dsize_dst)[source]#

Normalize a given homography in pixels to [-1, 1].

Convention:
  • the 3-D counterpart of normalize_homography(): same composition N_dst @ H @ inv(N_src), same source-to-destination direction re-expressed in normalized frames, same dsize_src on the right and dsize_dst on the left, and corner-aligned frames — with normal_transform_pixel3d() in place of the 2-D helper. Unlike the 2-D function it has no align_corners parameter, so its frames are corner-aligned unconditionally. It is not the 2-D function with wider matrices, though: the shapes and — least visibly — the inversion routine differ, as the bullets below record

  • it inverts N_src with torch.linalg.inv (so cusolver on cuda, and a float32 upcast for float16/bfloat16), where the 2-D function uses a closed-form adjugate in the working dtype; the 2-D and 3-D routes therefore need not agree bitwise

  • the matrices are \((B, 4, 4)\) and act on (x, y, z, 1) column vectors, while both dsize arguments are (depth, height, width) triples

  • denormalize_homography3d() computes the reverse inv(N_dst) @ H @ N_src

  • the shape guard is the 2-D function’s for 4x4 matrices: \((4, 4)\) (returned as (1, 4, 4)) or \((B, 4, 4)\), anything else raises ValueError

Parameters:
  • dst_pix_trans_src_pix (Tensor) – homography/ies from source to destination to be normalized. \((B, 4, 4)\), or an unbatched \((4, 4)\), which is promoted to \((1, 4, 4)\)

  • dsize_src (tuple[int, int, int]) – size of the source image (depth, height, width).

  • dsize_dst (tuple[int, int, int]) – size of the destination image (depth, height, width).

Return type:

Tensor

Returns:

the normalized homography.

Shape:

Output: \((B, 4, 4)\)

kornia.geometry.conversions.denormalize_homography3d(dst_pix_trans_src_pix, dsize_src, dsize_dst)[source]#

De-normalize a given 3D homography from [-1, 1] to actual depth, height and width.

Convention:
  • the mirror of normalize_homography3d(): the composition is inv(N_dst) @ H @ N_src, so the input maps normalized source to normalized destination and the output maps source voxels to destination voxels. The (depth, height, width) dsize roles, the (x, y, z, 1) column vectors, per-sample batching, the corner-aligned frames and the shape guard are as documented there

  • inv(N_dst) goes through torch.linalg.inv (so cusolver on cuda, and a float32 upcast for float16/bfloat16), the same routine normalize_homography3d() uses for inv(N_src)

  • the two functions invert each other to a small multiple of the working dtype’s eps times the largest entry, not bitwise in general

  • only real floating point homographies are accepted (float16, bfloat16, float32, float64): an integer, bool or complex dst_pix_trans_src_pix raises TypeCheckError (also a TypeError) instead of being promoted, so cast it first. Integers would truncate the normalization matrices (the 2-D function still does, #3959), and complex dtypes would be inverted in float32 (_torch_inverse_cast), losing complex128 precision

Parameters:
  • dst_pix_trans_src_pix (Tensor) – homography/ies from source to destination to be denormalized. \((B, 4, 4)\), or an unbatched \((4, 4)\), which is promoted to \((1, 4, 4)\)

  • dsize_src (tuple[int, int, int]) – size of the source image (depth, height, width).

  • dsize_dst (tuple[int, int, int]) – size of the destination image (depth, height, width).

Return type:

Tensor

Returns:

the denormalized homography.

Shape:

Output: \((B, 4, 4)\)

kornia.geometry.conversions.normal_transform_pixel(height, width, eps=1e-14, device=None, dtype=None, align_corners=True)[source]#

Compute the normalization matrix from image size in pixels to [-1, 1].

Convention:
  • the result is a single matrix of shape \((1, 3, 3)\) — never batched — acting on homogeneous (x, y, 1) column vectors, with x indexing columns and scaled by width and y indexing rows and scaled by height. The positional argument order is the other way round, (height, width)

  • the mapping is corner-aligned by default: scale 2 / (size - 1), offset -1, so the pixel centres 0 and size - 1 map to exactly -1 and +1. align_corners=False selects the half-pixel mapping instead; see the align_corners bullet below. normal_transform_pixel(4, 5) has rows [0.5, 0.0, -1.0] and [0.0, 0.6667, -1.0], and sends the pixels (0, 0), (4, 3) and (2, 1.5) to (-1, -1), (1, 1) and (0, 0)

  • for sizes >= 2 this is the same map as normalize_pixel_coordinates(). In float32/float64 the two routes agree to the working dtype’s rounding; in float16/bfloat16 they can differ by about one eps, because the matrix is applied through a matmul while the helper rounds elementwise

  • the convention is selected by align_corners (True by default). False is the half-pixel mapping with scale 2 / size and offset 1 / size - 1, so that \(\pm 1\) fall on the outer pixel edges. normalize_homography() and denormalize_homography() forward their own align_corners here, while normal_transform_pixel3d() is still corner-aligned unconditionally; see the convention warning there

  • a singleton axis maps its only pixel to the centre of the normalized range: under align_corners=True with scale 1 and offset 0, which keeps the matrix invertible for homography composition. Zero and negative sizes raise ValueError

  • with dtype=None the matrix takes torch.get_default_dtype() (float32 unless the default was changed); an integer or bool dtype raises ValueError, since the fractional scales would truncate to 0, for every size and even under disable_checks(). Every floating and complex dtype is accepted

Note

Legacy torch.jit.trace graphs assume positive runtime sizes because tracing cannot retain the Python ValueError validation. Eager, TorchScript, and torch.compile calls validate sizes.

Deprecated since version 0.9.0: eps no longer participates in the explicitly defined singleton-axis mapping. Passing a non-default value warns in eager mode; the parameter will be removed in a future breaking release.

Parameters:
  • height (int) – image height.

  • width (int) – image width.

  • eps (float, optional) – deprecated compatibility parameter. It is ignored. Default: 1e-14

  • device (Optional[device], optional) – device to place the result on. Default: None

  • dtype (Optional[dtype], optional) – dtype of the result. None means torch.get_default_dtype(). Default: None

  • align_corners (bool, optional) – which torch.nn.functional.grid_sample() convention to normalize to. True maps pixel centers \([0, size-1]\) to \([-1, 1]\); False uses the half-pixel mapping \(x_{norm} = (2x + 1) / W - 1\), where \(\pm 1\) are the outer pixel edges. Default: True

Return type:

Tensor

Returns:

normalized transform with shape \((1, 3, 3)\).

Example

>>> normal_transform_pixel(4, 5)
tensor([[[ 0.5000,  0.0000, -1.0000],
         [ 0.0000,  0.6667, -1.0000],
         [ 0.0000,  0.0000,  1.0000]]])
kornia.geometry.conversions.normal_transform_pixel3d(depth, height, width, eps=1e-14, device=None, dtype=None)[source]#

Compute the normalization matrix from image size in pixels to [-1, 1].

Convention:
  • the 3-D counterpart of normal_transform_pixel(): same corner-aligned 2 / (size - 1) scaling with offset -1, same dtype=None / torch.get_default_dtype() rule, and likewise never batched. Unlike the 2-D function it has no align_corners parameter, so it applies that scaling unconditionally. Singleton axes use the same invertible centre mapping, and zero or negative sizes raise ValueError. Only the lines below differ

  • the result has shape \((1, 4, 4)\) and acts on homogeneous (x, y, z, 1) column vectors with x scaled by width, y by height and z by depth, while the positional argument order is (depth, height, width): normal_transform_pixel3d(9, 5, 3) has diagonal [1.0, 0.5, 0.25] and sends (0, 0, 0) to (-1, -1, -1) and the far corner (2, 4, 8) to (1, 1, 1)

  • that (x, y, z) slot order is a permutation of normalize_pixel_coordinates3d(), which reads its input as (d, x, y). The two produce the same three numbers in different slots — with depth=9, height=5, width=3 the point d=7, x=2, y=1 gives [0.75, 1.0, -0.5] through the coordinate helper and [1.0, -0.5, 0.75] through this matrix — so a grid built for one silently permutes axes when fed to the other

  • an integer or bool dtype raises ValueError, as in the 2-D function

Note

Legacy torch.jit.trace graphs assume positive runtime sizes for the same reason as the 2-D counterpart.

Deprecated since version 0.9.0: eps is ignored and follows the deprecation policy of the 2-D counterpart.

Parameters:
  • depth (int) – image depth.

  • height (int) – image height.

  • width (int) – image width.

  • eps (float, optional) – deprecated compatibility parameter. It is ignored. Default: 1e-14

  • device (Optional[device], optional) – device to place the result on. Default: None

  • dtype (Optional[dtype], optional) – dtype of the result. None means torch.get_default_dtype(). Default: None

Return type:

Tensor

Returns:

normalized transform with shape \((1, 4, 4)\).

Example

>>> normal_transform_pixel3d(2, 4, 5)
tensor([[[ 0.5000,  0.0000,  0.0000, -1.0000],
         [ 0.0000,  0.6667,  0.0000, -1.0000],
         [ 0.0000,  0.0000,  2.0000, -1.0000],
         [ 0.0000,  0.0000,  0.0000,  1.0000]]])

Quaternion#

kornia.geometry.conversions.quaternion_to_axis_angle(quaternion)[source]#

Convert quaternion vector to axis angle of rotation in radians.

The quaternion should be in (w, x, y, z) format.

Adapted from ceres C++ library: ceres-solver/include/ceres/rotation.h

Convention:
  • the input is (w, x, y, z), real part first (see quaternion_to_rotation_matrix()), and the output is the rotation axis scaled by the angle in radians

  • for w != 0 the double cover is collapsed: q and -q return the same vector, the representative with |theta| <= pi

  • at exactly w = 0 — a half turn, and 0. is exactly representable — the collapse does not happen and the two return exact negations: [0., 1., 0., 0.] gives [pi, 0., 0.] while [-0., -1., 0., 0.] gives [-pi, 0., 0.], the same rotation written the other way round

  • the input need not be unit: scaling q by a power of two returns a bit-identical vector, and other scales move it by the working dtype’s rounding, as long as the squares of the components neither overflow nor underflow

  • quaternion_to_angle_axis is the deprecated alias of this function since 0.7.0

  • differentiable at the identity quaternion. The sqrt whose derivative is unbounded there never sees its zero radicand, so the gradient is the analytic limit of the surrounding map rather than nan. The guard is elementwise, and away from the identity it moves no forward bit

Parameters:

quaternion (Tensor) – tensor with quaternions.

Return type:

Tensor

Returns:

tensor with axis angle of rotation.

Shape:
  • Input: \((*, 4)\) where * means, any number of dimensions

  • Output: \((*, 3)\)

Example

>>> quaternion = torch.tensor((1., 0., 0., 0.))
>>> quaternion_to_axis_angle(quaternion)
tensor([0., 0., 0.])
kornia.geometry.conversions.quaternion_to_rotation_matrix(quaternion)[source]#

Convert a quaternion to a rotation matrix.

The quaternion should be in (w, x, y, z) format.

Convention:
  • the quaternion is (w, x, y, z), real part first, shape \((*, 4)\) in and \((*, 3, 3)\) out. [1., 0., 0., 0.] gives the identity and [0., 0., 0., 1.] gives [[-1., 0., 0.], [0., -1., 0.], [0., 0., 1.]], a half turn about +z — so a caller passing (x, y, z, w) gets a valid-looking rotation matrix and no error

  • q and -q are the same rotation (double cover) and return bit-identical matrices

  • the input is normalised internally with normalize_quaternion(), so quaternion_to_rotation_matrix(2 * q) is bitwise quaternion_to_rotation_matrix(q); other rescalings move the matrix by the working dtype’s rounding, as long as the norm of the rescaled quaternion neither overflows nor falls below the normalisation floor eps = 1e-12 (see that function’s warning)

  • quaternion_to_axis_angle(), quaternion_exp_to_log(), and euler_from_quaternion() are scale-safe too

  • applied on the left to a column vector, +theta about +z maps x_hat to y_hat (right-hand rule)

  • the output dtype follows the input at every shape

Warning

The zero quaternion, which is not a rotation, returns the identity matrix rather than raising. Tracked in #3952.

Parameters:

quaternion (Tensor) – a tensor containing a quaternion to be converted. The tensor can be of shape \((*, 4)\).

Return type:

Tensor

Returns:

the rotation matrix of shape \((*, 3, 3)\).

Example

>>> quaternion = torch.tensor((0., 0., 0., 1.))
>>> quaternion_to_rotation_matrix(quaternion)
tensor([[-1.,  0.,  0.],
        [ 0., -1.,  0.],
        [ 0.,  0.,  1.]])
kornia.geometry.conversions.quaternion_log_to_exp(quaternion, eps=1.0e-8)[source]#

Apply exponential map to log quaternion.

Convention:
  • the input is a 3-vector of shape \((*, 3)\), not a quaternion; the output is the quaternion of shape \((*, 4)\) in (w, x, y, z) order (see quaternion_to_rotation_matrix())

  • the map is w = cos(||v||) with vector part sin(||v||) * v / ||v||, and every finite v returns a unit quaternion up to the working dtype’s rounding: ||v|| is computed as s * ||v / s|| with s = max |v_i|, so it cannot overflow, and float16/bfloat16 are computed in float32, so the zero vector returns [1., 0., 0., 0.] at every dtype

  • pi/2 < ||v|| < 3 * pi/2 lands in the w < 0 half of the double cover — at ||v|| = 2 the real part is -0.4161468365471424. w is cos(||v||), so the sign turns over again beyond that interval rather than staying negative

  • v is therefore half the axis-angle vector: the result agrees with axis_angle_to_quaternion(2 * v) to the rounding of the working dtype, but not bit-for-bit: [0.15, 0.2, 0.25] already disagrees at bfloat16, and in float64 most random vectors do too

  • for a unit q, quaternion_log_to_exp(quaternion_exp_to_log(q)) returns q up to rounding, except that the pure-real [-1., 0., 0., 0.] comes back as [1., 0., 0., 0.] — the other half of the same rotation

Parameters:
  • quaternion (Tensor) – the log quaternion, a tensor of shape \((*, 3)\).

  • eps (float, optional) – a small number for clamping. Default: 1.0e-8

Return type:

Tensor

Returns:

the quaternion exponential map of shape \((*, 4)\).

Example

>>> quaternion = torch.tensor((0., 0., 0.))
>>> quaternion_log_to_exp(quaternion, eps=torch.finfo(quaternion.dtype).eps)
tensor([1., 0., 0., 0.])
kornia.geometry.conversions.quaternion_exp_to_log(quaternion, eps=1.0e-8)[source]#

Apply the log map to a quaternion.

The quaternion should be in (w, x, y, z) format.

Convention:
  • the input is the quaternion of shape \((*, 4)\) in (w, x, y, z) order and the output is a 3-vector of shape \((*, 3)\), the argument quaternion_log_to_exp() takes

  • for a unit q on the w >= 0 half of the double cover the result is quaternion_to_axis_angle(q) / 2. On the w < 0 half the two part company: this function keeps the sign of w, with the angle atan2(||v||, w) in [0, pi], while quaternion_to_axis_angle() collapses the double cover. For the q whose log is v, quaternion_exp_to_log(-q) is -(pi - ||v||) * v / ||v||

  • quaternion_exp_to_log(quaternion_log_to_exp(v)) returns v for 0 < ||v|| < pi and the wrapped ||v|| - 2 * pi above pi (pi + 0.5 comes back as -2.6415926535897936). At exactly ||v|| = pi in float64 it collapses to nearly 0: cos(pi) rounds to -1 and the vector part falls under the eps clamp. Both are properties of the map, not defects

  • float16/bfloat16 are computed in float32, so the identity returns [0., 0., 0.] at every dtype, and the gradient is finite at w = +-1, the identity included

  • the map is scale-invariant: a non-unit quaternion produces the same log as its normalisation

Parameters:
  • quaternion (Tensor) – a tensor containing a quaternion to be converted. The tensor can be of shape \((*, 4)\).

  • eps (float, optional) – a small number for clamping. Default: 1.0e-8

Return type:

Tensor

Returns:

the quaternion log map of shape \((*, 3)\).

Example

>>> quaternion = torch.tensor((1., 0., 0., 0.))
>>> quaternion_exp_to_log(quaternion, eps=torch.finfo(quaternion.dtype).eps)
tensor([0., 0., 0.])
kornia.geometry.conversions.normalize_quaternion(quaternion, eps=1.0e-12)[source]#

Normalize a quaternion.

The quaternion should be in (x, y, z, w) or (w, x, y, z) format.

Convention:
  • it is an L2 normalisation of the last axis and nothing else, which is why the (x, y, z, w) or (w, x, y, z) phrasing above is accurate here: [4., 3., 2., 1.] normalises to the reversal of what [1., 2., 3., 4.] gives

  • the sign is preserved — normalize_quaternion(-q) is exactly -normalize_quaternion(q), there is no w >= 0 canonicalisation

Warning

When ||q|| < eps the output is not a unit quaternion and no error is raised: with the default eps = 1e-12, [1e-13, 0., 0., 0.] returns [0.1, 0., 0., 0.] and zeros(4) returns zeros(4). Tracked in #3952. With eps=0.0 the zero quaternion returns nan. In float16 a positive eps is floored at the smallest subnormal.

Parameters:
  • quaternion (Tensor) – a tensor containing a quaternion to be normalized. The tensor can be of shape \((*, 4)\).

  • eps (float, optional) – floor on the norm used as the divisor; see the warning above. Default: 1.0e-12

Return type:

Tensor

Returns:

the normalized quaternion of shape \((*, 4)\).

Example

>>> quaternion = torch.tensor((1., 0., 1., 0.))
>>> normalize_quaternion(quaternion)
tensor([0.7071, 0.0000, 0.7071, 0.0000])

Vector#

kornia.geometry.conversions.vector_to_skew_symmetric_matrix(vec)[source]#

Convert a vector to a skew symmetric matrix.

A vector \((v1, v2, v3)\) has a corresponding skew-symmetric matrix, which is of the form:

\[\begin{split}\begin{bmatrix} 0 & -v3 & v2 \\ v3 & 0 & -v1 \\ -v2 & v1 & 0\end{bmatrix}\end{split}\]
Convention:
  • the matrix acts on the left as the first cross-product factor: vector_to_skew_symmetric_matrix(v) @ x equals cross(v, x), not its negation — for v = (1., 2., 3.) and x = (4., 5., 6.) both are (-3., 6., -3.)

  • accepts \((3,)\) or \((B, 3)\) only; any higher rank raises ValueError

Parameters:

vec (Tensor) – tensor of shape \((3,)\) or \((B, 3)\).

Return type:

Tensor

Returns:

tensor of shape \((3, 3)\) or \((B, 3, 3)\) respectively.

Example

>>> vec = torch.tensor([1.0, 2.0, 3.0])
>>> vector_to_skew_symmetric_matrix(vec)
tensor([[ 0., -3.,  2.],
        [ 3.,  0., -1.],
        [-2.,  1.,  0.]])

Rotation Matrix#

kornia.geometry.conversions.rotation_matrix_to_axis_angle(rotation_matrix, check_rotation=False)[source]#

Convert 3x3 rotation matrix to Rodrigues vector in radians.

Convention:
  • any number of leading batch dimensions is accepted: \((3, 3)\) gives \((3,)\) and \((2, 5, 3, 3)\) gives \((2, 5, 3)\)

  • the output is the rotation axis scaled by the angle in radians, the parametrization axis_angle_to_rotation_matrix() consumes, at every rank: the \((3,)\) and \((2, 5, 3)\) results above can be fed straight back

  • the round trip through axis_angle_to_rotation_matrix() is accurate to roundoff, except that the low-angle Taylor branch (theta <= 1e-3) leaves a truncation error of up to about 2e-10

  • by default the input is not checked for being a rotation matrix: zeros(3, 3) returns [0., 0., 3.1416] and 2 * eye(3) returns [0., 0., 0.]. Pass check_rotation=True to reject such an input instead

  • rotation_matrix_to_angle_axis is the deprecated alias of this function since 0.7.0

Warning

By default a reflection (det = -1) is returned as a rotation: diag(-1, 1, 1) gives [0., 0., 0.], the identity. Pass check_rotation=True to raise a ValueError instead.

Parameters:
  • rotation_matrix (Tensor) – rotation matrix of shape \((*, 3, 3)\).

  • check_rotation (bool, optional) – if True, raise ValueError unless every input is a rotation: max|R @ R^T - I| within 100 eps of the dtype of rotation_matrix (16 eps for float16 and bfloat16) and det(R) > 0. Defaults to False (unchecked). Default: False

Return type:

Tensor

Returns:

Rodrigues vector transformation of shape \((*, 3)\).

Example

>>> input = torch.tensor([[1., 0., 0.],
...                       [0., 1., 0.],
...                       [0., 0., 1.]])
>>> rotation_matrix_to_axis_angle(input)
tensor([0., 0., 0.])
>>> input = torch.tensor([[1., 0., 0.],
...                       [0., 0., -1.],
...                       [0., 1., 0.]])
>>> rotation_matrix_to_axis_angle(input)
tensor([1.5708, 0.0000, 0.0000])
kornia.geometry.conversions.rotation_matrix_to_quaternion(rotation_matrix, eps=1.0e-8, check_rotation=False)[source]#

Convert 3x3 rotation matrix to 4d quaternion vector.

The quaternion vector has components in (w, x, y, z) format.

Convention:
  • the returned components are (w, x, y, z), real part first — the layout, and its silent misreading, are spelled out on quaternion_to_rotation_matrix()

  • the sign is not canonicalised to w >= 0. For trace(R) > 0 the returned w is non-negative, but for trace(R) <= 0 it is the dominant of x, y, z that is forced non-negative and w may be negative: 170 degrees about (1, 2, -3)/sqrt(14) (trace = -0.9696) returns [-0.0872, -0.2662, -0.5325, 0.7987]

  • by default the input is not checked for being a rotation matrix; see the degenerate inputs listed on rotation_matrix_to_axis_angle(). Pass check_rotation=True to reject such an input instead

Parameters:
  • rotation_matrix (Tensor) – the rotation matrix to convert with shape \((*, 3, 3)\).

  • eps (float, optional) – floor on each square-root radicand. It is never added to the radicand: a radicand above eps is used as is, and one at or below it (for example the negative radicand of a discarded branch on a slightly non-orthogonal input) gives 2 * sqrt(eps) with a zero gradient. For an exact rotation matrix the selected radicand is at least 1, so eps does not change the result. The divisions are separately guarded against a zero denominator. Default: 1.0e-8

  • check_rotation (bool, optional) – if True, raise ValueError unless every input is a rotation: max|R @ R^T - I| within 100 eps of the dtype of rotation_matrix (16 eps for float16 and bfloat16) and det(R) > 0. Defaults to False (unchecked). Default: False

Return type:

Tensor

Returns:

the rotation in quaternion with shape \((*, 4)\).

Example

>>> input = torch.tensor([[1., 0., 0.],
...                       [0., 1., 0.],
...                       [0., 0., 1.]])
>>> rotation_matrix_to_quaternion(input)
tensor([1., 0., 0., 0.])

Axis Angle#

kornia.geometry.conversions.axis_angle_to_quaternion(axis_angle)[source]#

Convert an axis angle to a quaternion.

The quaternion vector has components in (w, x, y, z) format.

Adapted from ceres C++ library: ceres-solver/include/ceres/rotation.h

Convention:
  • the input is the rotation axis scaled by the angle in radians, shape \((*, 3)\); the output is (w, x, y, z) with w = cos(theta / 2), shape \((*, 4)\) (see quaternion_to_rotation_matrix() for the layout)

  • nothing is canonicalised, so theta > pi returns the w < 0 half of the double cover: [2 * pi, 0., 0.] gives [-1., 0., 0., 0.]

  • the round trip with quaternion_to_axis_angle() holds to roundoff for 0 <= theta <= pi

  • angle_axis_to_quaternion is the deprecated alias of this function since 0.7.0

  • an integral or boolean input is promoted, not rejected: the sqrt on the way in already produces a floating tensor, and the output is allocated from that rather than from the input, so the result comes back at the ambient default floating dtype

  • differentiable at the zero rotation. The sqrt whose derivative is unbounded there never sees its zero radicand, so the gradient is the analytic limit of the surrounding map rather than nan. The guard is elementwise, and away from zero it moves no forward bit

Parameters:

axis_angle (Tensor) – tensor with axis angle in radians.

Return type:

Tensor

Returns:

tensor with quaternion.

Shape:
  • Input: \((*, 3)\) where * means, any number of dimensions

  • Output: \((*, 4)\)

Example

>>> axis_angle = torch.tensor((0., 1., 0.))
>>> axis_angle_to_quaternion(axis_angle)
tensor([0.8776, 0.0000, 0.4794, 0.0000])
kornia.geometry.conversions.axis_angle_to_rotation_matrix(axis_angle)[source]#

Convert 3d vector of axis-angle rotation to 3x3 rotation matrix.

Convention:
  • any number of leading batch dimensions is accepted: \((3,)\) gives \((3, 3)\) and \((2, 5, 3)\) gives \((2, 5, 3, 3)\)

  • the input is the rotation axis scaled by the angle, in radians: [[0., 0., pi/2]] is a quarter turn about +z, while [[0., 0., 90.]] is 90 radians about +z and returns a matrix whose leading entry is cos(90) = -0.4481. The 2-D op angle_to_rotation_matrix() reads degrees instead

  • applied on the left to a column vector, +theta about +z maps x_hat to y_hat (right-hand rule): [[0., 0., 0.6]] sends (1., 0., 0.) to (0.8253, 0.5646, 0.). The quaternion route quaternion_to_rotation_matrix() turns the same way

  • angle_axis_to_rotation_matrix is the deprecated alias of this function since 0.7.0; it emits a DeprecationWarning and forwards to this function, returning an equal result

  • differentiable at the identity rotation. The sqrt whose derivative is unbounded there never sees its zero radicand, so the gradient is the analytic limit of the surrounding map rather than nan. The guard is elementwise, and away from the identity it moves no forward bit

Parameters:

axis_angle (Tensor) – tensor of 3d vector of axis-angle rotations in radians with shape \((*, 3)\).

Return type:

Tensor

Returns:

tensor of rotation matrices of shape \((*, 3, 3)\).

Example

>>> input = torch.tensor([[0., 0., 0.]])
>>> axis_angle_to_rotation_matrix(input)
tensor([[[1., ...0., 0.],
         [0., 1., ...0.],
         [...0., 0., 1.]]])
>>> input = torch.tensor([[1.5708, 0., 0.]])
>>> axis_angle_to_rotation_matrix(input)
tensor([[[ 1.0000e+00,  0.0000e+00,  0.0000e+00],
         [ 0.0000e+00, -3.6200e-06, -1.0000e+00],
         [ 0.0000e+00,  1.0000e+00, -3.6200e-06]]])

Euler Angles#

kornia.geometry.conversions.quaternion_from_euler(roll, pitch, yaw)[source]#

Convert Euler angles to quaternion coefficients.

Convention:
  • roll, pitch and yaw are in radians and turn about x, y and z respectively; the rotation they compose to is R = Rz(yaw) @ Ry(pitch) @ Rx(roll) — extrinsic X-Y-Z about the fixed axes, equivalently intrinsic Z-Y’-X’’. The order matters: at (0.3, 0.7, 1.1) the returned quaternion’s matrix differs from Rx @ Ry @ Rz by 0.64, from Ry @ Rz @ Rx by 0.55 and from Rx @ Rz @ Ry by 0.25

  • the three arguments must have exactly equal shapes; broadcastable shapes such as (2,) and (1,) raise BaseError

  • the return is a tuple of four separate tensors (w, x, y, z), not a \((*, 4)\) tensor: (0.3, 0.7, 1.1) gives [0.8186, -0.0575, 0.3624, 0.4418]

Parameters:
  • roll (Tensor) – the roll euler angle.

  • pitch (Tensor) – the pitch euler angle.

  • yaw (Tensor) – the yaw euler angle.

Return type:

tuple[Tensor, Tensor, Tensor, Tensor]

Returns:

A tuple with quaternion coefficients in order of wxyz.

kornia.geometry.conversions.euler_from_quaternion(w, x, y, z)[source]#

Convert a quaternion coefficients to Euler angles.

Convention:
  • the four quaternion coefficients are passed as separate tensors in (w, x, y, z) order, not as one \((*, 4)\) tensor, and their shapes must be exactly equal: broadcastable shapes such as (2,) and (1,) raise BaseError

  • the return is a tuple of three tensors (roll, pitch, yaw) in radians, with roll about x, pitch about y and yaw about z; the composition they stand for is documented on quaternion_from_euler()

  • away from |pitch| = pi/2 it returns the triple of the input rotation folded into roll, yaw in [-pi, pi] — the interval is closed at both ends: they come from atan2, which reaches ±pi with the endpoint’s sign decided by signed zeros, so w = -0.0, x = 1.0, y = 0.0, z = -0.0 returns roll exactly -pi while the +0.0 twin returns +pi — and pitch in [-pi/2, pi/2], which inside those ranges is the input itself: the float64 round trip of (0.3, 0.7, 1.1) through quaternion_from_euler() returns to 2.2e-16, while (0.2, 2.5, 0.3) comes back as (-2.9416, 0.6416, -2.8416) — a different triple for the same rotation, to 1.1e-16

  • at |pitch| = pi/2 (gimbal lock) roll and yaw are not individually determined, only yaw - roll at +pi/2 and yaw + roll at -pi/2 are, so the input triple cannot be recovered. It returns pitch exactly ±pi/2, roll exactly 0 and that one combination in yaw, wrapped into [-pi, pi], which reproduces the rotation: the round trip through quaternion_from_euler() is exact to rounding, and q and -q return the same triple. Gimbal lock is detected as cos(pitch) < 2 * sqrt(eps) for the dtype’s eps (6.25e-2 for float16, 1.77e-1 for bfloat16, 6.9e-4 for float32, 3.0e-8 for float64), so a pitch that close to ±pi/2 is also snapped to the pole and the returned rotation is off by at most its distance to it

  • the input is normalised with normalize_quaternion() first, so a rescaled quaternion returns the same triple

Note

The gradient is finite everywhere, including exactly at gimbal lock. There roll and pitch are constants, so their gradient is exactly 0 and all of it is in yaw: for the quaternion of (roll, pitch, yaw) = (0.1, pi/2, 0.2) the gradient of roll + pitch + yaw with respect to (w, x, y, z) is (-0.141, -2.82, 0, 0). Within 2 * sqrt(eps) of the pole it is the gradient of that constant-pitch triple, not of the plain formulas.

Parameters:
  • w (Tensor) – quaternion \(q_w\) coefficient.

  • x (Tensor) – quaternion \(q_x\) coefficient.

  • y (Tensor) – quaternion \(q_y\) coefficient.

  • z (Tensor) – quaternion \(q_z\) coefficient.

Return type:

tuple[Tensor, Tensor, Tensor]

Returns:

A tuple with euler angles`roll`, pitch, yaw.

Pose#

kornia.geometry.conversions.Rt_to_matrix4x4(R, t)[source]#

Combine 3x3 rotation matrix R and 3x1 translation vector t into 4x4 extrinsics.

Convention:
  • t goes in the last column and the appended bottom row is [0, 0, 0, 1], so the result acts on homogeneous column vectors as x_out = R @ x_in + t: with t = (1, 2, 3), M @ (0, 0, 0, 1) returns t itself and M @ (1, 0, 0, 1) returns R[:, 0] + t

  • the function is frame-agnostic — it packs whatever (R, t) it is handed and does not check that R is a rotation. Under the camera-to-world reading the rest of this family uses (see camtoworld_to_worldtocam_Rt()), t is the camera centre in world coordinates, whereas the world-to-camera form’s -R^T t is a translation and not a centre. Which of the two you are packing is your bookkeeping, not this function’s

  • shapes are strict: exactly \((B, 3, 3)\) and \((B, 3, 1)\). An unbatched (3, 3), a (B, 3) translation, a (B, 1, 3) translation and extra leading dimensions each raise ShapeError; R and t must also carry the same batch size: a mismatched pair raises ShapeError too

  • matrix4x4_to_Rt() is the inverse: Rt -> 4x4 -> Rt is bitwise, while 4x4 -> Rt -> 4x4 is bitwise only when the bottom row is already [0, 0, 0, 1], because matrix4x4_to_Rt() drops it

Note

R and t must promote to a floating point or complex dtype; an integer or boolean pair raises ValueError naming its dtype. Mixed integer/floating inputs keep torch’s promotion. The integer-safe _4x4 frame functions and camtoworld_to_worldtocam_Rt keep their existing behavior; the broader dtype consistency question is tracked in #3959.

Parameters:
  • R (Tensor) – Rotation matrix, \((B, 3, 3).\)

  • t (Tensor) – Translation matrix \((B, 3, 1)\).

Return type:

Tensor

Returns:

the extrinsics \((B, 4, 4)\).

Example

>>> R, t = torch.eye(3)[None], torch.ones(3).reshape(1, 3, 1)
>>> Rt_to_matrix4x4(R, t)
tensor([[[1., 0., 0., 1.],
         [0., 1., 0., 1.],
         [0., 0., 1., 1.],
         [0., 0., 0., 1.]]])
kornia.geometry.conversions.matrix4x4_to_Rt(extrinsics)[source]#

Convert 4x4 extrinsics into 3x3 rotation matrix R and 3x1 translation vector t.

Convention:
  • the inverse of Rt_to_matrix4x4(), with the same layout — R is the top-left 3x3 block and t the first three rows of the last column — and the same frame-agnosticism. Only the lines below differ

  • the input shape is strictly \((B, 4, 4)\); an unbatched (4, 4), a (B, 3, 3) and extra leading dimensions each raise ShapeError

  • the bottom row is ignored: a projective 4x4 is silently truncated to its rigid part, so replacing the bottom row with [9, 9, 9, 9] leaves R and t bit-identical, and re-packing through Rt_to_matrix4x4() restores the canonical [0, 0, 0, 1] rather than what was passed in

  • the returned R and t are views of extrinsics, not copies: R.mul_(0.) zeroes the caller’s rotation block in place. Clone before mutating either output

Parameters:

extrinsics (Tensor) – pose matrix \((B, 4, 4)\).

Returns:

Rotation matrix, \((B, 3, 3).\) t: Translation matrix \((B, 3, 1)\).

Return type:

R

Example

>>> ext = torch.eye(4)[None]
>>> matrix4x4_to_Rt(ext)
(tensor([[[1., 0., 0.],
         [0., 1., 0.],
         [0., 0., 1.]]]), tensor([[[0.],
         [0.],
         [0.]]]))
kornia.geometry.conversions.worldtocam_to_camtoworld_Rt(R, t, check_rotation=False)[source]#

Convert worldtocam frame used in Colmap to camtoworld.

Convention:

See camera and world conventions for camera-to-world versus world-to-camera extrinsics and camera-centre semantics.

  • the same function as camtoworld_to_worldtocam_Rt(), which carries the canonical block: for a proper rotation (R^T, -R^T @ t) is its own inverse, so one name serves both directions and the choice is documentation for the reader

  • read the other way round here: the input t is the world-to-camera translation and the returned -R^T t is the camera centre in world coordinates

  • the shapes and the rotation warning are as documented there

Parameters:
  • R (Tensor) – Rotation matrix, \((B, 3, 3).\)

  • t (Tensor) – Translation matrix \((B, 3, 1)\).

  • check_rotation (bool, optional) – if True, raise ValueError unless every R is a rotation: max|R @ R^T - I| within 100 eps of the dtype of R (16 eps for float16 and bfloat16) and det(R) > 0. Defaults to False (unchecked). Default: False

Returns:

Rotation matrix, \((B, 3, 3).\) tinv: Translation matrix \((B, 3, 1)\).

Return type:

Rinv

Example

>>> R, t = torch.eye(3)[None], torch.ones(3).reshape(1, 3, 1)
>>> worldtocam_to_camtoworld_Rt(R, t)
(tensor([[[1., 0., 0.],
         [0., 1., 0.],
         [0., 0., 1.]]]), tensor([[[-1.],
         [-1.],
         [-1.]]]))
kornia.geometry.conversions.camtoworld_to_worldtocam_Rt(R, t, check_rotation=False)[source]#

Convert camtoworld to worldtocam frame used in Colmap.

See long-url: https://colmap.github.io/format.html#output-format

Convention:

See camera and world conventions for camera-to-world versus world-to-camera extrinsics and camera-centre semantics.

  • the returned pair is exactly (R^T, -R^T @ t) — the rigid inverse, computed by transposition and never by a matrix inverse, so for a proper rotation it inverts the pose to the working dtype’s rounding

  • what t means on each side: the input is a camera-to-world pose, so its t is the camera centre in world coordinates (the 4x4 sends the origin to t); the returned -R^T t is the world-to-camera translation of Colmap’s images.txt (the returned 4x4 sends the camera centre to the origin)

  • worldtocam_to_camtoworld_Rt() is the same function under the other name: applying either one twice returns R exactly and t to rounding

  • the shapes are \((B, 3, 3)\) and \((B, 3, 1)\), and R and t must carry the same batch size: a mismatched pair raises ShapeError

Warning

R is assumed to be a rotation and by default this is not checked, so for a non-orthogonal R the result is a transpose and not an inverse, silently: R = [[1, 0.5, 0], [0, 1, 0], [0, 0, 2]] gives max|M_inv @ M - I| = 3.0. Pass check_rotation=True to raise a ValueError instead, which also rejects reflections (det(R) < 0).

Parameters:
  • R (Tensor) – Rotation matrix, \((B, 3, 3).\)

  • t (Tensor) – Translation matrix \((B, 3, 1)\).

  • check_rotation (bool, optional) – if True, raise ValueError unless every R is a rotation: max|R @ R^T - I| within 100 eps of the dtype of R (16 eps for float16 and bfloat16) and det(R) > 0. Defaults to False (unchecked). Default: False

Returns:

Rotation matrix, \((B, 3, 3).\) tinv: Translation matrix \((B, 3, 1)\).

Return type:

Rinv

Example

>>> R, t = torch.eye(3)[None], torch.ones(3).reshape(1, 3, 1)
>>> camtoworld_to_worldtocam_Rt(R, t)
(tensor([[[1., 0., 0.],
         [0., 1., 0.],
         [0., 0., 1.]]]), tensor([[[-1.],
         [-1.],
         [-1.]]]))
kornia.geometry.conversions.camtoworld_graphics_to_vision_4x4(extrinsics_graphics)[source]#

Convert a camera-to-world pose from the graphics frame (e.g. OpenGL) to the vision frame (e.g. OpenCV).

I.e. flips y and z axis. Graphics convention: [+x, +y, +z] == [right, up, backwards]. Vision convention: [+x, +y, +z] == [right, down, forwards].

Convention:

See camera and world conventions for the graphics/vision camera frames and camera-to-world extrinsics convention.

  • the input is a camera-to-world pose \((B, 4, 4)\): its 3x3 block maps camera axes into the world and its last column is the camera centre in world coordinates

  • the operation is exactly extrinsics @ diag(1, -1, -1, 1) — a right multiplication, i.e. a change of the camera-side basis. The translation column is untouched. Left-multiplying by the same matrix would negate two of its components instead, and feeding a world-to-camera matrix in flips the wrong side and leaves the translation in the old frame: a silent error, not an exception

  • graphics (OpenGL) camera frame: +x right, +y up, +z backwards — the camera looks down -z. Vision (OpenCV) camera frame: +x right, +y down, +z forwards — the camera looks down +z. On the identity camera-to-world pose, camera +y maps to world +y before the conversion and to world -y after it, and likewise for +z

  • the flip preserves the determinant of the 3x3 block, and with it the handedness: diag(1, -1, -1) has determinant +1, so a proper rotation stays proper. Nothing here checks that the input is a rotation

  • the flip is an involution, and all four graphics/vision frame functions compute the identical map: camtoworld_vision_to_graphics_4x4() returns the same matrix as this one on the same input, and applying either twice returns the input. The two names document the caller’s intent, not different arithmetic

  • the shape is strictly \((B, 4, 4)\); the _Rt variants take and return \((B, 3, 3)\) and \((B, 3, 1)\) and agree with this path bitwise

Warning

Integer input is not guarded: this function returns int64 for an int64 matrix (on backends with integer batched matmul), while camtoworld_graphics_to_vision_Rt() raises ValueError naming the unsupported dtype. Tracked in #3959; see Rt_to_matrix4x4().

Parameters:

extrinsics_graphics (Tensor) – pose matrix \((B, 4, 4)\).

Returns:

pose matrix \((B, 4, 4)\).

Return type:

extrinsics

Example

>>> ext = torch.eye(4)[None]
>>> camtoworld_graphics_to_vision_4x4(ext)
tensor([[[ 1.,  0.,  0.,  0.],
         [ 0., -1.,  0.,  0.],
         [ 0.,  0., -1.,  0.],
         [ 0.,  0.,  0.,  1.]]])
kornia.geometry.conversions.camtoworld_vision_to_graphics_4x4(extrinsics_vision)[source]#

Convert a camera-to-world pose from the vision frame (e.g. OpenCV) to the graphics frame (e.g. OpenGL).

I.e. flips y and z axis. Graphics convention: [+x, +y, +z] == [right, up, backwards]. Vision convention: [+x, +y, +z] == [right, down, forwards].

Convention:

See camera and world conventions for the graphics/vision camera frames and camera-to-world extrinsics convention.

  • the same map as camtoworld_graphics_to_vision_4x4(), which carries the canonical block: diag(1, -1, -1, 1) is its own inverse, so vision-to-graphics and graphics-to-vision are the identical function and return bitwise equal matrices on the same input. The name records which direction the caller means

  • everything else — the right multiplication, the untouched translation column, the two frame definitions, the preserved determinant, the strict \((B, 4, 4)\) shape and the integer-dtype warning — is as documented there

Parameters:

extrinsics_vision (Tensor) – pose matrix \((B, 4, 4)\).

Returns:

pose matrix \((B, 4, 4)\).

Return type:

extrinsics

Example

>>> ext = torch.eye(4)[None]
>>> camtoworld_vision_to_graphics_4x4(ext)
tensor([[[ 1.,  0.,  0.,  0.],
         [ 0., -1.,  0.,  0.],
         [ 0.,  0., -1.,  0.],
         [ 0.,  0.,  0.,  1.]]])
kornia.geometry.conversions.camtoworld_graphics_to_vision_Rt(R, t)[source]#

Convert a camera-to-world pose from the graphics frame (e.g. OpenGL) to the vision frame (e.g. OpenCV).

I.e. flips y and z axis. Graphics convention: [+x, +y, +z] == [right, up, backwards]. Vision convention: [+x, +y, +z] == [right, down, forwards].

Convention:

See camera and world conventions for the graphics/vision camera frames and camera-to-world extrinsics convention.

  • the split-argument form of camtoworld_graphics_to_vision_4x4(), which documents the flip, the two camera frames, the involution and the preserved handedness. It packs (R, t) with Rt_to_matrix4x4(), applies that function and splits the result again, so the two paths agree bitwise. Only the lines below differ

  • the shapes are strictly \((B, 3, 3)\) and \((B, 3, 1)\) in and out, with the same-batch-size rule and the integer-input ValueError of Rt_to_matrix4x4()

  • t is returned unchanged; only R has its second and third columns negated

Parameters:
  • R (Tensor) – Rotation matrix, \((B, 3, 3).\)

  • t (Tensor) – Translation matrix \((B, 3, 1)\).

Returns:

Rotation matrix, \((B, 3, 3).\) t: Translation matrix \((B, 3, 1)\).

Return type:

R

Example

>>> R, t = torch.eye(3)[None], torch.ones(3).reshape(1, 3, 1)
>>> camtoworld_graphics_to_vision_Rt(R, t)
(tensor([[[ 1.,  0.,  0.],
         [ 0., -1.,  0.],
         [ 0.,  0., -1.]]]), tensor([[[1.],
         [1.],
         [1.]]]))
kornia.geometry.conversions.camtoworld_vision_to_graphics_Rt(R, t)[source]#

Convert a camera-to-world pose from the vision frame (e.g. OpenCV) to the graphics frame (e.g. OpenGL).

I.e. flips y and z axis. Graphics convention: [+x, +y, +z] == [right, up, backwards]. Vision convention: [+x, +y, +z] == [right, down, forwards].

Convention:

See camera and world conventions for the graphics/vision camera frames and camera-to-world extrinsics convention.

Parameters:
  • R (Tensor) – Rotation matrix, \((B, 3, 3).\)

  • t (Tensor) – Translation matrix \((B, 3, 1)\).

Returns:

Rotation matrix, \((B, 3, 3).\) t: Translation matrix \((B, 3, 1)\).

Return type:

R

Example

>>> R, t = torch.eye(3)[None], torch.ones(3).reshape(1, 3, 1)
>>> camtoworld_vision_to_graphics_Rt(R, t)
(tensor([[[ 1.,  0.,  0.],
         [ 0., -1.,  0.],
         [ 0.,  0., -1.]]]), tensor([[[1.],
         [1.],
         [1.]]]))
kornia.geometry.conversions.ARKitQTVecs_to_ColmapQTVecs(qvec, tvec)[source]#

Convert an Apple ARKit camera-to-world screen pose to the world-to-camera pose expected by Colmap.

Both poses in quaternion representation.

Convention:

See camera and world conventions for the camera frames and extrinsics conventions composed by this conversion.

  • input: qvec \((B, 4)\) in (w, x, y, z) order — [1., 0., 0., 0.] is the identity — and tvec \((B, 3, 1)\), read as a camera-to-world pose in the graphics frame (+y up, -z forward), so tvec is the camera centre in world coordinates

  • output: (q, t) in the same layouts, a world-to-camera pose in the vision frame (+y down, +z forward) — Colmap’s images.txt QW QX QY QZ TX TY TZ. The pipeline is quaternion_to_rotation_matrix(), camtoworld_graphics_to_vision_Rt(), camtoworld_to_worldtocam_Rt(), then rotation_matrix_to_quaternion()

  • ARKit’s simd_quatf exposes .vector as (ix, iy, iz, r), i.e. xyzw: reorder it to wxyz first, since this function accepts either order silently

  • the input quaternion need not be unit and q and -q give the same output, as long as ||q|| stays above the eps = 1e-12 normalisation floor and its squared norm does not overflow (about 1.8e19 in float32); outside that range the result is the zero-quaternion answer of the warning below or a wrong pose

  • the sign of the output quaternion is not canonical: it is the representative rotation_matrix_to_quaternion()’s branch produces, so [0.5, 0.5, 0.5, 0.5] with t = (1, 2, 3) returns q = [-0.5, -0.5, -0.5, 0.5] and t = (-2, 3, 1). Compare rotations, never raw components

  • worked literal: q = [0., 1., 0., 1.], t = (1, 1, 1) gives q = [0.7071, 0., 0.7071, 0.] and t = (-1, -1, 1), the example below

  • ColmapQTVecs_to_ARKitQTVecs() computes the reverse conversion

Warning

The zero quaternion is not rejected: torch.zeros(1, 4) with t = (1, 1, 1) returns q = [0., 1., 0., 0.], t = (-1, 1, 1), the same answer as the identity input. Tracked in #3952.

Parameters:
  • qvec (Tensor) – ARKit rotation quaternion \((B, 4)\), [w, x, y, z] format.

  • tvec (Tensor) – translation vector \((B, 3, 1)\), [x, y, z]

Returns:

Colmap rotation quaternion \((B, 4)\), [w, x, y, z] format. tvec: translation vector \((B, 3, 1)\), [x, y, z]

Return type:

qvec

Example

>>> q, t = torch.tensor([0, 1, 0, 1.])[None], torch.ones(3).reshape(1, 3, 1)
>>> ARKitQTVecs_to_ColmapQTVecs(q, t)
(tensor([[0.7071, 0.0000, 0.7071, 0.0000]]), tensor([[[-1.0000],
         [-1.0000],
         [ 1.0000]]]))
kornia.geometry.conversions.ColmapQTVecs_to_ARKitQTVecs(qvec, tvec)[source]#

Convert a Colmap world-to-camera pose to an Apple ARKit camera-to-world screen pose.

Both poses in quaternion representation.

Convention:

See camera and world conventions for the camera frames and extrinsics conventions composed by this conversion.

  • the reverse of ARKitQTVecs_to_ColmapQTVecs(), with the same layouts and the frames exchanged

  • input: qvec \((B, 4)\) in (w, x, y, z) order — [1., 0., 0., 0.] is the identity — and tvec \((B, 3, 1)\), read as a world-to-camera pose in the vision frame (+y down, +z forward) — Colmap’s images.txt QW QX QY QZ TX TY TZ

  • output: (q, t) in the same layouts, a camera-to-world pose in the graphics frame (+y up, -z forward), so t is the camera centre in world coordinates. The pipeline is quaternion_to_rotation_matrix(), worldtocam_to_camtoworld_Rt(), camtoworld_vision_to_graphics_Rt(), then rotation_matrix_to_quaternion()

  • the output is (w, x, y, z); ARKit’s simd_quatf stores (ix, iy, iz, r), i.e. xyzw, so reorder it before handing it to ARKit

  • the input quaternion need not be unit and q and -q give the same output, within the same normalisation range as ARKitQTVecs_to_ColmapQTVecs()

  • only real floating point qvec and tvec are accepted: an integer, bool or complex one raises TypeCheckError (also a TypeError) naming the argument, so cast it first

  • the sign of the output quaternion is not canonical: a round trip through both functions returns the input rotation, but not necessarily the input quaternion’s sign. Compare rotations, never raw components

  • worked literal: the reverse of the ARKitQTVecs_to_ColmapQTVecs() example, q = [0.7071, 0., 0.7071, 0.], t = (-1, -1, 1), gives q = [0., 0.7071, 0., 0.7071] and t = (1, 1, 1), the unit form of that example’s input; see the example below

Warning

The zero quaternion is not rejected: torch.zeros(1, 4) with t = (1, 1, 1) returns q = [0., 1., 0., 0.], t = (-1, -1, -1), the same answer as the identity input. Tracked in #3952.

Parameters:
  • qvec (Tensor) – Colmap rotation quaternion \((B, 4)\), [w, x, y, z] format.

  • tvec (Tensor) – translation vector \((B, 3, 1)\), [x, y, z]

Returns:

ARKit rotation quaternion \((B, 4)\), [w, x, y, z] format. tvec: translation vector \((B, 3, 1)\), [x, y, z]

Return type:

qvec

Example

>>> q, t = torch.tensor([0, 1, 0, 1.])[None], torch.ones(3).reshape(1, 3, 1)
>>> ColmapQTVecs_to_ARKitQTVecs(*ARKitQTVecs_to_ColmapQTVecs(q, t))
(tensor([[0.0000, 0.7071, 0.0000, 0.7071]]), tensor([[[1.0000],
         [1.0000],
         [1.0000]]]))