Edge detection#

sobel() returns the gradient magnitude of an image, spatial_gradient() its signed x and y derivatives (or second derivatives), laplacian() a second-derivative response and canny() a thin binary edge map. spatial_gradient3d() differentiates volumes.

Functions#

kornia.filters.canny(input, low_threshold=0.1, high_threshold=0.2, kernel_size=(5, 5), sigma=(1, 1), hysteresis=True, eps=1e-6)[source]#

Find the edges of an image with the Canny detector.

magnitude, edges = canny(input) blurs a grayscale or RGB image, thins the Sobel gradient magnitude by non-maximum suppression, and thresholds it with hysteresis into an edge map.

_images/canny.png
Convention:
  • A 3-channel input is converted with rgb_to_grayscale(), which reads channel 0 as red. Both outputs are (B, 1, H, W), in the input’s dtype for a floating input and in float32 for an integer one, which is processed in float32 on its own scale.

  • The image is blurred with gaussian_blur2d(input, kernel_size, sigma); the Convention block on gaussian_blur2d() gives the order of both pairs, and kernel_size=1 skips the blur. The magnitude is \(\sqrt{g_x^2 + g_y^2 + \epsilon}\) of the unnormalised Sobel gradient spatial_gradient(blurred, normalized=False): an axis-aligned ramp of signed slope s gives \(\sqrt{(8 s)^2 + \epsilon}\) at an interior pixel, about 8 * abs(s). This is about eight times what sobel() returns by default, up to the eps inside the square root. On an image in \([0, 1]\) it is not bounded by 1: a unit step reaches 4 without the blur and about 2.59 with the default one. Float16 and bfloat16 inputs are computed in float32, with enclosing autocast disabled; only the final magnitude and edges are rounded to the input dtype.

  • The thresholds compare against that magnitude and are strict: a pixel is weak above low_threshold and strong above high_threshold. Filtering maps them onto OpenCV and scikit-image.

  • Non-maximum suppression compares each pixel with its two neighbours along the gradient direction, rounded to a multiple of 45 degrees. As in OpenCV’s cv2.Canny, along a horizontal (vertical) gradient a pixel must be strictly greater than its left (upper) neighbour and greater than or equal to its right (lower) one, so of two pixels of equal magnitude across a step edge the left (upper) one is kept. Along a diagonal it must be strictly greater than both. A pixel with zero gradient is never kept. The returned magnitude is taken after this step, so it is zero off the edge ridges; it is differentiable with respect to the input, and the edge map is not.

  • Hysteresis keeps a weak pixel connected to a strong one through any of its 8 neighbours, repeated until nothing changes, and returns edges of 0 and 1. Under torch.export the loop is a torch.while_loop, so the exported graph also repeats until nothing changes, for any input; the dynamo ONNX exporter writes it as an ONNX Loop from torch 2.11 and cannot export it before. Under torch.jit.trace, and so the legacy TorchScript ONNX exporter (torch.onnx.export(..., dynamo=False)), the loop is a scripted function that the trace calls, so the traced graph also repeats until nothing changes, for any input, and the exporter writes it as an ONNX Loop. A trace cannot record a loop whose condition depends on the data on its own: if TorchScript cannot compile the function, a RuntimeWarning says so and the traced graph repeats the rounds the traced image needed on every input, which cuts a longer edge chain short without an error.

  • An integer image is promoted to float32 before the grayscale conversion, so a 3-channel integer image is converted with the floating weights rather than rounded to an integer grey.

Parameters:
  • input (Tensor) – image with shape (B, C, H, W), with C equal to 1, or to 3 for an RGB image, which is converted to grayscale.

  • low_threshold (float, optional) – lower threshold for the hysteresis procedure, in the units of the magnitude above. It must be positive and at most high_threshold. Default: 0.1

  • high_threshold (float, optional) – upper threshold for the hysteresis procedure, in the same units. It has no upper bound. Default: 0.2

  • kernel_size (tuple[int, int] | int, optional) – Gaussian blur window (kH, kW) in pixels, or one integer for both. Default: (5, 5)

  • sigma (tuple[float, float] | Tensor, optional) – Gaussian blur standard deviation (sigma_y, sigma_x) in pixels, as in gaussian_blur2d(). Default: (1, 1)

  • hysteresis (bool, optional) – if True, keep a weak edge only when it connects to a strong one. If False, the edge map holds 0, 0.5 for weak edges and 1 for strong edges. Default: True

  • eps (float, optional) – added under the square root of the magnitude so that the gradient of a flat pixel is finite. Default: 1e-6

Return type:

tuple[Tensor, Tensor]

Returns:

  • the gradient magnitude after non-maximum suppression, with shape (B, 1, H, W).

  • the edge map, with shape (B, 1, H, W), holding 0 and 1 with hysteresis=True.

Note

See a working example here.

See also

  • Canny: the module form.

  • sobel(): the gradient magnitude alone, without blur, suppression or thresholds.

Example

>>> input = torch.rand(5, 3, 4, 4)
>>> magnitude, edges = canny(input)  # 5x3x4x4
>>> magnitude.shape
torch.Size([5, 1, 4, 4])
>>> edges.shape
torch.Size([5, 1, 4, 4])
kornia.filters.laplacian(input, kernel_size, border_type='reflect', normalized=True)[source]#

Filter an image with a Laplacian kernel, a second-derivative operator.

out = laplacian(input, kernel_size) filters each channel of a (B, C, H, W) image with the Laplacian kernel of that size and returns the response in the same shape.

_images/laplacian.png
Convention:
  • The kernel is get_laplacian_kernel2d(kernel_size), correlated with each channel as by filter2d(); see the Convention blocks on get_laplacian_kernel2d() for the stencil, its sign and kernel_size, and on filter2d() for the border modes.

  • For floating inputs, normalized=True, the default, divides the kernel by its absolute sum \(2 (kH \cdot kW - 1)\), 16 for size 3. Unlike normalized in spatial_gradient(), this does not give derivative units: size 3 returns \(3 \nabla^2 / 16\). Filtering compares both scales with scipy and OpenCV.

  • An integer or bool input is filtered in float32 and the output is float32, as in filter2d().

Parameters:
  • input (Tensor) – image with shape (B, C, H, W).

  • kernel_size (tuple[int, int] | int) – kernel (kH, kW) in pixels, or one integer for both; each odd and positive, and at least 3 along one axis.

  • border_type (str, optional) – padding mode, 'constant', 'reflect', 'replicate' or 'circular', case-insensitive. Default: "reflect"

  • normalized (bool, optional) – if True, the L1 norm of the kernel is 1. Default: True

Note

Ordinary eager CPU execution uses convolution for extreme input ranges. Captured graphs and function transforms retain their selected arithmetic, so with normalized=False an input near its dtype’s maximum can overflow to NaN under torch.compile or torch.vmap where eager CPU execution returns a finite response.

Return type:

Tensor

Returns:

the Laplacian response with shape (B, C, H, W).

Raises:

BaseError – if a size is even or not positive, if kernel_size is a sequence of other than 2 sizes, or if it is 1 or (1, 1): a \(1 \times 1\) kernel is all zeros, and its normalized form is 0 / 0.

Note

See a working example here.

See also

Examples

>>> input = torch.rand(2, 4, 5, 5)
>>> output = laplacian(input, 3)
>>> output.shape
torch.Size([2, 4, 5, 5])
kornia.filters.sobel(input, normalized=True, eps=1e-6)[source]#

Compute the Sobel gradient magnitude of each channel.

out = sobel(input) returns the gradient magnitude of each channel of a (B, C, H, W) image as an edge-strength map of the same shape.

_images/sobel.png
Convention:
  • The output is \(\sqrt{g_x^2 + g_y^2 + \epsilon}\) per channel, where \((g_x, g_y)\) is spatial_gradient(input, 'sobel', normalized=normalized); see the Convention block on spatial_gradient(). Away from the replicated border, an axis-aligned ramp of slope s gives \(\sqrt{s^2 + \epsilon}\), about \(|s|\), with the default normalized=True; normalized=False gives about \(8 |s|\), the scale canny() thresholds.

  • eps sits inside the square root, so a flat region returns \(\sqrt{\epsilon}\), 1e-3 by default, not 0.

Parameters:
  • input (Tensor) – image with shape (B, C, H, W).

  • normalized (bool, optional) – if True, the L1 norm of each Sobel kernel is 1. Default: True

  • eps (float, optional) – added under the square root so that the gradient of a flat pixel is finite. Default: 1e-6

Return type:

Tensor

Returns:

the gradient magnitudes, with shape (B, C, H, W).

Note

See a working example here.

See also

  • Sobel: the module form.

  • spatial_gradient(): the signed x and y derivatives, on an added axis.

  • canny(): thins the edges by non-maximum suppression and thresholds them.

  • gradient(): a morphological edge strength, the dilation minus the erosion.

Example

>>> input = torch.rand(1, 3, 4, 4)
>>> output = sobel(input)  # 1x3x4x4
>>> output.shape
torch.Size([1, 3, 4, 4])
kornia.filters.spatial_gradient(input, mode='sobel', order=1, normalized=True)[source]#

Compute the first or second image derivatives in x and y with Sobel or difference kernels.

out = spatial_gradient(input) returns the x and y derivatives of each channel of a (B, C, H, W) image, stacked on a new axis 2.

_images/spatial_gradient.png
Convention:
  • The derivatives are the kernels of get_spatial_gradient_kernel2d(), in that function’s order and with its sign; see its Convention block.

  • For order=1 with the default normalized=True, an axis-aligned linear ramp with slope s has response s in the corresponding channel away from the replicated border. With normalized=False, the Sobel response is 8 s. Filtering maps both scales onto OpenCV, scipy and scikit-image.

  • The border is always replicated, so for order=1 with normalized=True, a linear ramp’s derivative is half its slope on the first and last pixel along that axis. There is no border_type, unlike filter2d() and laplacian(), whose default is 'reflect'.

Parameters:
  • input (Tensor) – image with shape (B, C, H, W).

  • mode (str, optional) – kernel set, 'sobel' or 'diff', case-insensitive. Default: "sobel"

  • order (int, optional) – 1 for the first derivatives, 2 for the second. Default: 1

  • normalized (bool, optional) – if True, scale the kernels so that every output channel estimates the derivative itself away from the replicated border: exact on linear surfaces for order=1 and on quadratic surfaces for order=2. If False, return the raw kernel responses. Default: True

Return type:

Tensor

Returns:

the derivatives of the input feature map, with shape (B, C, 2, H, W) holding \((dx, dy)\) for order=1 and (B, C, 3, H, W) holding \((dxx, dxy, dyy)\) for order=2.

Note

See a working example here.

See also

Examples

>>> input = torch.rand(1, 3, 4, 4)
>>> output = spatial_gradient(input)  # 1x3x2x4x4
>>> output.shape
torch.Size([1, 3, 2, 4, 4])
kornia.filters.spatial_gradient3d(input, mode='diff', order=1)[source]#

Compute the first or second derivatives of a volume along x, y and depth with difference kernels.

out = spatial_gradient3d(input) returns the derivatives of each channel of a (B, C, D, H, W) volume, stacked on a new axis 2.

Convention:
  • The derivatives are the kernels of get_spatial_gradient_kernel3d(), in that function’s order; see its Convention block for their units. There is no normalized argument: a floating input gives derivative units.

  • The border is always replicated, as in spatial_gradient().

  • An integer or bool input is differentiated in float32 and the output is float32, on the input’s scale, as filter3d() does (#5155).

Parameters:
  • input (Tensor) – volume with shape (B, C, D, H, W).

  • mode (str, optional) – kernel set, case-insensitive; only 'diff' is implemented. Default: "diff"

  • order (int, optional) – 1 for the first derivatives, 2 for the second. Default: 1

Return type:

Tensor

Returns:

the derivatives, with shape (B, C, 3, D, H, W) holding \((dx, dy, dz)\) for order=1 or (B, C, 6, D, H, W) holding \((dxx, dyy, dzz, dxy, dyz, dxz)\) for order=2.

See also

Examples

>>> input = torch.rand(1, 4, 2, 4, 4)
>>> output = spatial_gradient3d(input)
>>> output.shape
torch.Size([1, 4, 3, 2, 4, 4])

Modules#

class kornia.filters.Laplacian(kernel_size, border_type='reflect', normalized=True)[source]#

Filter an image with a stored Laplacian kernel size (module form).

lap = Laplacian(kernel_size) stores its arguments and checks the size, and out = lap(input) is laplacian() on a (B, C, H, W) image.

Convention:

See the Convention block on laplacian().

Parameters:
  • kernel_size (tuple[int, int] | int) – kernel (kH, kW) in pixels, or one integer for both, as in laplacian().

  • border_type (str, optional) – padding mode, as in laplacian(). Default: "reflect"

  • normalized (bool, optional) – if True, the L1 norm of the kernel is 1. Default: True

Raises:

BaseError – if a size is even or not positive, if kernel_size is a sequence of other than 2 sizes, or if it is 1 or (1, 1). The size is checked when the module is built, as laplacian() checks it when called.

Shape:
  • Input: (B, C, H, W)

  • Output: (B, C, H, W)

See also

Examples

>>> input = torch.rand(2, 4, 5, 5)
>>> laplace = Laplacian(5)
>>> output = laplace(input)
>>> output.shape
torch.Size([2, 4, 5, 5])
class kornia.filters.Sobel(normalized=True, eps=1e-6)[source]#

Compute the Sobel gradient magnitude of each channel (module form).

edges = Sobel(normalized, eps) stores its arguments, and out = edges(input) is sobel() on a (B, C, H, W) image.

Convention:

See the Convention block on sobel().

Parameters:
  • normalized (bool, optional) – if True, the L1 norm of each Sobel kernel is 1. Default: True

  • eps (float, optional) – added under the square root, as in sobel(). Default: 1e-6

Shape:
  • Input: (B, C, H, W)

  • Output: (B, C, H, W)

See also

  • sobel(): the one-call form.

  • SpatialGradient: the signed x and y derivatives, on an added axis.

  • Canny: thins the edges by non-maximum suppression and thresholds them.

Examples

>>> input = torch.rand(1, 3, 4, 4)
>>> output = Sobel()(input)  # 1x3x4x4
class kornia.filters.Canny(low_threshold=0.1, high_threshold=0.2, kernel_size=(5, 5), sigma=(1, 1), hysteresis=True, eps=1e-6)[source]#

Find the edges of an image with stored Canny settings (module form).

detector = Canny(low_threshold, high_threshold, ...) stores its arguments and checks the thresholds, and magnitude, edges = detector(input) is canny() on a (B, C, H, W) image.

Convention:

See the Convention block on canny().

Parameters:
  • low_threshold (float, optional) – lower hysteresis threshold, in the units of the unnormalised Sobel magnitude. It must be positive and at most high_threshold. Default: 0.1

  • high_threshold (float, optional) – upper hysteresis threshold, in the same units. It has no upper bound. Default: 0.2

  • kernel_size (tuple[int, int] | int, optional) – Gaussian blur window (kH, kW) in pixels, or one integer for both. Default: (5, 5)

  • sigma (tuple[float, float] | Tensor, optional) – Gaussian blur standard deviation (sigma_y, sigma_x) in pixels. Default: (1, 1)

  • hysteresis (bool, optional) – whether to keep a weak edge only when it connects to a strong one, as in canny(). Default: True

  • eps (float, optional) – added under the square root of the magnitude, as in canny(). Default: 1e-6

Shape:
  • Input: (B, C, H, W), with C equal to 1, or to 3 for an RGB image.

  • Output: the magnitude and the edge map, each (B, 1, H, W).

See also

  • canny(): the one-call form.

  • Sobel: the gradient magnitude alone.

Example

>>> input = torch.rand(5, 3, 4, 4)
>>> magnitude, edges = Canny()(input)  # 5x3x4x4
>>> magnitude.shape
torch.Size([5, 1, 4, 4])
>>> edges.shape
torch.Size([5, 1, 4, 4])
class kornia.filters.SpatialGradient(mode='sobel', order=1, normalized=True)[source]#

Compute the first or second image derivatives with a stored kernel set and order (module form).

grad = SpatialGradient(mode, order, normalized) stores its arguments, and out = grad(input) is spatial_gradient() on a (B, C, H, W) image.

Convention:

See the Convention block on spatial_gradient().

Parameters:
  • mode (str, optional) – kernel set, 'sobel' or 'diff', case-insensitive. Default: "sobel"

  • order (int, optional) – 1 for the first derivatives, 2 for the second. Default: 1

  • normalized (bool, optional) – whether to scale the kernels to derivative units, as in spatial_gradient(). Default: True

Shape:
  • Input: (B, C, H, W)

  • Output: (B, C, 2, H, W) for order=1 and (B, C, 3, H, W) for order=2

See also

Examples

>>> input = torch.rand(1, 3, 4, 4)
>>> output = SpatialGradient()(input)  # 1x3x2x4x4
class kornia.filters.SpatialGradient3d(mode='diff', order=1)[source]#

Compute the first or second derivatives of a volume with a stored kernel set and order (module form).

grad = SpatialGradient3d(mode, order) stores its arguments, and out = grad(input) is spatial_gradient3d() on a (B, C, D, H, W) volume.

Convention:

See the Convention block on spatial_gradient3d().

Parameters:
  • mode (str, optional) – kernel set, case-insensitive; only 'diff' is implemented. Default: "diff"

  • order (int, optional) – 1 for the first derivatives, 2 for the second. Default: 1

Shape:
  • Input: (B, C, D, H, W)

  • Output: (B, C, 3, D, H, W) for order=1 and (B, C, 6, D, H, W) for order=2

See also

Examples

>>> input = torch.rand(1, 4, 2, 4, 4)
>>> output = SpatialGradient3d()(input)
>>> output.shape
torch.Size([1, 4, 3, 2, 4, 4])