Equalization and histograms#

Equalization#

equalize(), equalize_clahe() and equalize3d() expect a floating-point image in [0, 1] and raise TypeCheckError (a TypeError) for an integer or boolean image, as the [0, 1] adjustment functions do; convert 8-bit data with image.float() / 255 first.

kornia.enhance.equalize(input)[source]#

Equalize the histogram of each channel of an image, as PIL’s ImageOps.equalize does.

out = equalize(input) equalizes each channel of each image in a (*, C, H, W) batch in \([0, 1]\) on its own.

_images/equalize.png
Convention:
  • Each pixel is counted in the lookup-table entry it reads, its 8-bit level (x * 255).long() in the input dtype, so a float32 or float64 image gives PIL.ImageOps.equalize of those levels. In float32 and float64 the 255 is raised by four float32 epsilons, relative (2**-21), so k / 255 reads level k even when its division was rounded a unit low. Dividing by 255 on CUDA leaves 24 of the 256 float64 levels a unit low. A continuous value moves up a level only within that margin below a level boundary.

  • The lookup step is the pixel count minus the count at the highest occupied level, integer-divided by 255. A channel whose step is 0, which includes every channel of up to 255 pixels, is returned unchanged.

  • A float32 image counts in float32, which is exact while a channel holds at most 2**24 pixels (a 4096 by 4096 image).

  • Empty leading dimensions are supported; channels and spatial dimensions must be non-empty.

Parameters:

input (Tensor) – image with shape (*, C, H, W).

Return type:

Tensor

Returns:

Equalized image with shape (*, C, H, W).

Note

An integer or boolean image raises TypeCheckError (a TypeError); convert 8-bit data with image.float() / 255 first. Values the 256-bin lookup cannot index, those whose level before truncation lies outside (-1, 256), raise a RuntimeError naming the range, during the call for a CPU image, even when checks are disabled, since the check keeps the lookup in bounds. On CUDA, and on MPS from torch 2.13, the check is torch._assert_async and raises by the next synchronization; on an older MPS release the condition is read on the host instead, one device sync per call, and is skipped there under torch.compile.

See also

Example

>>> x = torch.rand(1, 2, 3, 3)
>>> equalize(x).shape
torch.Size([1, 2, 3, 3])
kornia.enhance.equalize_clahe(input, clip_limit=40.0, grid_size=(8, 8), slow_and_differentiable=False)[source]#

Equalize an image tile by tile with a clipped histogram (CLAHE).

out = equalize_clahe(input, clip_limit, grid_size) equalizes each channel of an image in \([0, 1]\) in every tile of a grid_size grid and blends the tiles’ lookup tables where they meet. Clipping the histogram at clip_limit limits the local contrast gain.

_images/equalize_clahe.png
Convention:
  • The tiles are ceil(H / grid_size[0]) by ceil(W / grid_size[1]) pixels, and an axis the grid does not divide is reflect-padded at its end. OpenCV uses the same tiles when the grid divides both axes or neither; when it divides one, OpenCV also pads that axis by one pixel per tile and Kornia does not.

  • Lookup tables are blended with weights anchored at each tile’s centre, (p + 0.5) / tile_size - 0.5 for pixel p, which keeps flip symmetry on unpadded grids. This is half a pixel from the phase of OpenCV and scikit-image, a blending-weight difference of at most 0.5 / tile_size per axis, so the output differs from cv2.createCLAHE even for the same lookup tables. The output is not rounded to 8-bit levels.

  • The default path rounds the lookup tables to nearest like OpenCV; slow_and_differentiable=True does not.

Parameters:
  • input (Tensor) – image with values in \([0, 1]\) and shape (*, C, H, W).

  • clip_limit (float, optional) – caps each bin at max(clip_limit * tile_pixels // 256, 1) counts and spreads the excess over all bins. 0.0 disables clipping. A float; an int raises TypeError. Default: 40.0

  • grid_size (Tuple[int, int], optional) – number of tiles (GH, GW) along H and W, a tuple of int. Each image axis must be larger than its grid size; the ValueError otherwise names the smallest image the grid admits. Default: (8, 8)

  • slow_and_differentiable (bool, optional) – count with a soft histogram, so gradients reach input; the default path has none. It is slower and, without the lookup-table rounding, its values differ from the default path’s. Default: False

Return type:

Tensor

Returns:

Equalized image with the shape of input.

Note

An integer or boolean image raises TypeCheckError (a TypeError), and values whose level before truncation lies outside (-1, 256) raise a RuntimeError naming the range, as for equalize(). That function also describes the 8-bit level the lookup reads and when the range error surfaces on CUDA and MPS.

See also

  • equalize(): equalizes the whole channel with one histogram.

  • RandomClahe: applies it with random parameters per image.

Examples

>>> img = torch.rand(1, 10, 20)
>>> res = equalize_clahe(img)
>>> res.shape
torch.Size([1, 10, 20])
>>> img = torch.rand(2, 3, 10, 20)
>>> res = equalize_clahe(img)
>>> res.shape
torch.Size([2, 3, 10, 20])
kornia.enhance.equalize3d(input)[source]#

Equalize the histogram of each channel of a 3D volume.

out = equalize3d(input) equalizes each channel of each volume in a (*, C, D, H, W) batch in \([0, 1]\) from one histogram over its whole (D, H, W) volume.

Convention:
  • Each voxel is read at its 8-bit level as equalize() reads a pixel.

  • The lookup step and the unchanged-channel rule are those of equalize(), counted over the voxels of a channel volume.

  • Empty leading dimensions are supported; channels and spatial dimensions must be non-empty.

Parameters:

input (Tensor) – volume with shape (*, C, D, H, W).

Return type:

Tensor

Returns:

Equalized volume with the shape of input.

Note

An integer or boolean volume raises TypeCheckError, and a value whose level before truncation lies outside (-1, 256) raises a RuntimeError, as for equalize(), with the same device behavior.

See also

  • equalize(): the image twin, with one histogram per channel plane.

Histograms#

The three histograms are kernel estimates and differ in their input:

  • histogram(): rows of samples (B, D), giving one normalized histogram per row.

  • histogram2d(): two rows of samples, giving their normalized joint histogram.

  • image_histogram2d(): an image, giving one histogram of the pixel values per channel.

kornia.enhance.histogram(x, bins, bandwidth, epsilon=1e-10)[source]#

Estimate the normalized histogram of each sample row with Gaussian kernels.

hist = histogram(x, bins, bandwidth) spreads every value of a row of x over the bin centers bins with a Gaussian kernel and normalizes each row to sum to 1, with epsilon added to the divisor.

Convention:
  • Rows are independent samples, and bins is the one center grid they share.

  • A bandwidth that is not finite and positive raises RuntimeError; on CUDA, and on MPS from torch 2.13, it raises by the next synchronization.

  • For float16 and bfloat16 samples and bins, the kernels are evaluated in float32 and the result is cast back to that dtype.

Parameters:
  • x (Tensor) – samples with shape (B, D).

  • bins (Tensor) – bin centers with shape (N_bins,), in the units of x.

  • bandwidth (Tensor) – 0-d tensor, the standard deviation of the Gaussian kernel in the units of x. A Python float or a (1,) tensor raises.

  • epsilon (float, optional) – value added to the normalization sum. Default: 1e-10

Return type:

Tensor

Returns:

Histogram with shape (B, N_bins). When all kernel weights underflow to zero, it is zero for a positive epsilon and NaN for epsilon=0.

See also

Examples

>>> x = torch.rand(1, 10)
>>> bins = torch.linspace(0, 1, 128)
>>> hist = histogram(x, bins, bandwidth=torch.tensor(0.01))
>>> hist.shape
torch.Size([1, 128])
kornia.enhance.histogram2d(x1, x2, bins, bandwidth, epsilon=1e-10)[source]#

Estimate the normalized joint histogram of two sample rows with Gaussian kernels.

hist = histogram2d(x1, x2, bins, bandwidth) pairs x1[b, n] with x2[b, n]: hist[b, i, j] is the kernel weight of x1 in bin i and x2 in bin j, normalized per batch element to sum to 1.

Convention:
  • x1 and x2 must have the same shape.

  • When x1 and bins promote to float16 or bfloat16, the kernels, joint counts and normalization are evaluated in float32 and the result is cast back to that dtype.

Parameters:
  • x1 (Tensor) – first samples with shape (B, D).

  • x2 (Tensor) – second samples with shape (B, D).

  • bins (Tensor) – bin centers with shape (N_bins,), in the units of the samples.

  • bandwidth (Tensor) – 0-d tensor, the standard deviation of the Gaussian kernel in the units of the samples.

  • epsilon (float, optional) – value added to the normalization sum. Default: 1e-10

Return type:

Tensor

Returns:

Joint histogram with shape (B, N_bins, N_bins); the first bin axis belongs to x1. All-zero kernel weights give zeros for a positive epsilon and NaN for epsilon=0.

See also

Examples

>>> x1 = torch.rand(2, 32)
>>> x2 = torch.rand(2, 32)
>>> bins = torch.linspace(0, 1, 128)
>>> hist = histogram2d(x1, x2, bins, bandwidth=torch.tensor(0.01))
>>> hist.shape
torch.Size([2, 128, 128])
kornia.enhance.image_histogram2d(image, min=0.0, max=255.0, n_bins=256, bandwidth=None, centers=None, return_pdf=False, kernel='triangular', eps=1e-10)[source]#

Estimate the intensity histogram of each channel of an image.

hist, pdf = image_histogram2d(image, min, max, n_bins) weights every pixel over n_bins bin centers with a kernel and returns the summed weights per channel as hist, and their normalization as pdf if return_pdf=True. Despite its name it is a one-dimensional histogram per channel; for the joint histogram of two signals use histogram2d().

Convention:
  • The spatial axes are the final two. An image (H, W), (C, H, W) or (B, C, H, W) gives hist with the same leading axes followed by the bins; another rank raises.

  • A pixel outside the range still adds the kernel’s weight to the centers within reach; it is not clipped into an end bin.

  • Float16 and bfloat16 images are accumulated and returned in float32, or in the dtype of wider explicit centers: ordinary bin counts exceed float16’s range (65504) and bfloat16’s precision.

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

  • min (float, optional) – lower end of the range, a float. It sets the automatic centers and bandwidth. Default: 0.0

  • max (float, optional) – upper end of the range, a float. It sets the automatic centers and bandwidth. The default 255.0 puts a \([0, 1]\) image in two bins; pass 1.0 for that range. It must exceed min while the centers are automatic. Default: 255.0

  • n_bins (int, optional) – number of automatic bins. Default: 256

  • bandwidth (Optional[float], optional) – kernel width, a positive finite float; it does not move the automatic centers. The default, also -1, is (max - min) / n_bins. Default: None

  • centers (Optional[Tensor], optional) – bin centers with shape (K,). The default, also an empty tensor, is the n_bins equal-width bin centers over [min, max], min + (i + 0.5) * (max - min) / n_bins. With explicit centers, also pass bandwidth: the default bandwidth still comes from min, max and n_bins (#6662). Default: None

  • return_pdf (bool, optional) – whether to also return the normalized histogram. Default: False

  • kernel (str, optional) – weight of a pixel at distance d from a center, with u = d / bandwidth: 'triangular' (\(1 - u\) for \(u \leq 1\)), 'gaussian' (\(e^{-u^2 / 2}\)), 'uniform' (1 for \(u \leq 1\)) or 'epanechnikov' (\(1 - u^2\) for \(u \leq 1\)). Gradients reach image except with 'uniform'. Default: "triangular"

  • eps (float, optional) – value added to the normalization sum. Default: 1e-10

Return type:

Tuple[Tensor, Tensor]

Returns:

Tuple (hist, pdf). hist has shape (K,), (C, K) or (B, C, K), where the bin axis K has n_bins entries, or one per center. pdf has the same shape and divides the histogram by its bin sum plus eps. Its sum approaches 1 when the bin sum dominates eps; it is zeros when no pixels contribute or return_pdf is False.

See also

  • histogram(): the Gaussian histogram of sample rows, normalized.

  • histogram2d(): the joint histogram of two sample rows.

Example

>>> image = torch.rand(2, 3, 8, 8)
>>> hist, pdf = image_histogram2d(image, 0.0, 1.0, n_bins=16, return_pdf=True)
>>> hist.shape
torch.Size([2, 3, 16])