Skip to main content

ahe

PyPI uv

A minimalist Python library for (Contrast Limited) (Adaptive) Histogram Equalization, combining the expressiveness of a user-friendly Python interface with the raw power of a low-level implementation.

Development status

ahe is currently in alpha. Fundational features are available, and expected to be stable, but the software as a whole may not be feature complete yet.

Installation

$ python -m pip install ahe

Usage

Simple Histogram Equalization

We'll start by defining an image composed of noise.

import ahe
import numpy as np

image_shape = (128, 128)
prng = np.random.default_rng(0)
image = np.clip(
    prng.normal(
        loc=0.5,
        scale=0.25,
        size=np.prod(image_shape),
    ).reshape(image_shape),
    a_min=0.0,
    a_max=1.0,
)

Non-adaptive histogram equalization is performed as follow

image_eq = ahe.equalize_histogram(image)

This method is least expensive in terms of strain put on hardware resources. However, as a single histogram is computed and adjusted over the entire image, this technique is known to amplify noise in near-uniform regions.

Adaptive Histogram equalization (AHE)

Adaptive Histogram Equalization (AHE) was designed to overcome this limitation by instead computing more localized (and numerous) histograms, improving the local contrast in all regions, at the cost of a reduced efficiency. As illustrated in the following, there are two main variants of AHE, sliding-tile and tile-interpolation. As the names suggest, both methods rely on the use of of tiles, also known as contextual regions, that define sub-domains in which different histograms are computed and applied.

Prioritizing accuracy: sliding-tile

True AHE is intrinsically an expensive operation to perform, as it requires computing a different histogram per pixel. One efficient (although still costly) way to accomplish this, originally proposed by Pizer et al. (1987), reduces the redundancy in intermediate computations and is known as the sliding-tile variant of AHE. Here's how to use it in ahe

image_eq = ahe.equalize_histogram(
    image,
    adaptive_strategy={
        'kind': 'sliding-tile',
        'tile-size': 15,
    },
)

[!NOTE] This strategy requires odd-sized tile shapes, but supports image shapes with any parity.

While an exact implementation of AHE, this option remains resource-demanding and is not recommended for production.

Prioritizing performance: tile-interpolation

Alternatively, very similar results can be obtained at a fraction of the cost using an approximative method known as the tile-interpolation variant of AHE, also introduced by Pizer et al. (1987). In this method, an image is split into equal-sized sub domains (tiles), which may be specified either from a tile size

image_eq = ahe.equalize_histogram(
    image,
    adaptive_strategy={
        'kind': 'tile-interpolation',
        'tile-size': 16,
    },
)

or as a number of tiles to split the domain into (in each direction)

image_eq = equalize_histogram(
    image,
    adaptive_strategy={
        'kind': 'tile-interpolation',
        'tile-into': 8,
    },
)

[!NOTE] This strategy requires even-sized tile and image shapes.

General rules for tiling schemes

In all AHE strategies, all tiles created will be the exact same size, regardless of the pixel's relative position in the image. The whole domain is generally padded internally in order to respect this rule. The exact method used for padding is controlled by the boundaries keyword argument.

Both 'tile-size' and 'tile-into' will accept either a shape as a pair of integers (n, m), or a single integer n, which is a shorthand for (n, n), as illustrated above.

Migrating from scikit-image

TL;DR

Put simply, if all your project needs from scikit-image is skimage.exposure.equalize_(adapt)hist, ahe provides a faster, more lightweight and portable replacement. The following contains a much more detailed explanation of the differences. You can also jump to the final Migration Guide.

Disclaimer: missing features

The following features from skimage.exposure.equalize_(adapt)hist are currently missing from ahe:

  • masking
  • multi-channel images objects (from PIL) or arrays. The expectation is that this should be easy to re-implement on the user side.
  • higher dimensionality: ahe currently only supports 2D input and outputs. The algorithms can be generalized to work in any dimensionality. Please open an issue

Some, though not all of the above are already planned. Don't hesitate to request the others (or anything else that could be in scope) by opening an issue.

Dependency Minimalism

ahe has no runtime dependencies beyond numpy. Additionally, its binaries are orders of magnitude lighter than scikit-image's, as well as future-compatible with yet-unreleased versions of Python.

Shows a bar chart comparing wheel sizes

(*: numpy itself, as the common dependency to ahe and scikit-image, is excluded from this graph)

Better performance

Shows a bar chart comparing wheel sizes

Interface Consistency

scikit-image's implementation of histogram equalization methods are exposed as two different functions: skimage.exposure.equalize_hist and skimage.exposure.equalize_adapthist. Only the former supports masking, and only the latter supports clipping. ahe.equalize_histogram provides a consistent feature set, independent of the adaptive strategy (or lack thereof) selected.

Furthermore, implicit, default behavior can be hard to reproduce explicitly. For instance, equalize_adapthist will, by default, create tiles by dividing the image in 8 along each direction, but only exposes a tile_size argument to override this; as a result, one needs to re-implement division logic if they need something very similar to the default, but with any other value than 8. In stark contrast, ahe.equalize_histogram's adaptive_strategy argument supports all these applications:

  • adaptive_strategy=None corresponds to skimage.exposure.equalize_hist
  • adaptive_strategy={'kind': 'tile-interpolation', 'tile-into': 8} corresponds to skimage.exposure.equalize_adapthist's default, but the exact divisor(s) used can easily be adjusted
  • adaptive_strategy={'kind': 'tile-interpolation', 'tile-size': 64} is akin to using skimage.exposure.equalize_adapthist's tile_size argument.

Last but not least, equalize_hist does not support contrast limitation, while equalize_adapthist enables it by default (clip_limit defaults to 0.01), and things get messy when you actually want to disable it:

  • clip_limit's name is not descriptive and only makes sense if you already know what it does under the hood. ahe's equivalent parameter is named max_normalized_bincount, which is more verbose, but also more explicit about what the number represents.

  • clip_limit (a.k.a max_normalized_bincount) is effectively a fraction; only values within the open interval ]0.0, 1.0] are meaningful, but clip_limit=0.0 is allowed, and effectivaly disable all clipping, which means it's equivalent to 1.0, further mystifying the underlying behavior and meaning of the parameter. Another way to phrase this is that the results change discontinuously at 0.0, which is very close to the default value and should be easy to reason about.

  • results for clip_limit=1.0 (or 0.0, as we just saw), are actually incorrect at a level that is visible to the naked eye (aliasing may be prominent). In comparison, ahe does not enable contrast limitation by default: such flagrant defects would be immediately visible in tests.

Conservation of transformation invariants

ahe.equalize_histogram also provides stricter guarantees regarding the transformation's geometric invariants. Outputs are guaranteed to be invariant (to machine precision) to left/right and top/ bottom symmetries. In contrast, skimage.exposure.equalize_adapthist's outputs are subject to biases on, because it does not enforce symmetry in its internal tiling scheme (as of scikit-image v0.26.0). This improved tiling scheme comes at the cost of stricter requirements in ahe.equalize_histogram: the tile-interpolation strategy only supports tiles and images with even sizes in both directions.

Additional features

ahe.equalize_histogram supports more tiling scheme than skimage.exposure.equalize_hist and skimage.exposure.equalize_adapthist combined, within a consistent interface and a unified feature set. In particular, it offers an exact implementation of Adaptive Histogram Equalization implemented as a sliding-tile, while skimage.exposure.equalize_adapthist only supports tile-interpolation (also available in ahe), which is generally faster, but also a less accurate approximation of a true AHE.

ahe.equalize_histogram also supports periodic boundary conditions, which can be specified as boundaries='periodic'.

Migration Guide

This section provides some practical examples of scikit-image applications and their equivalent in ahe.

Notes

  • in ahe, the default nbins is generally aligned with scikit-image's (256), except for kernels (or tiles) spanning less than 256 pixels. For maximu compatibility, specifying an explicit value is recommended.
  • ahe.equalize_histogram's max_normalized_bincount represents the same parameter as skimage.exposure.equalize_adapthist's clip_limit, but their default values differ: max_normalized_bincount defaults to 1.0 (no contrast limitation), while scikit-image's clip_limit default to 0.01
  • "kernel", "tile" and "contextual region" are different names for the same concept.

HE

from skimage.exposure import equalize_hist

result = equalize_hist(array)

# becomes
import ahe

result = ahe.equalize_histogram(array, nbins=256)

AHE with implicit kernel size

from skimage.exposure import equalize_adapthist

result = equalize_adapthist(array, clip_limit=1.0)

# becomes
import ahe

result = ahe.equalize_histogram(
    array,
    nbins=256,
    adaptive_strategy={
      "kind": "tile-interpolation",
      "tile-into": 8, # or (8, 8)
    },
)

CLAHE with explicit kernel size

from skimage.exposure import equalize_adapthist

result = equalize_adapthist(array, kernel_size=64)

# becomes
import ahe

result = ahe.equalize_histogram(
    array,
    nbins=256,
    adaptive_strategy={
      "kind": "tile-interpolation",
      "tile-size": 64, # or (64, 64)
    },
    max_normalized_bincount=0.01,
)

References

  1. Pizer, Stephen M. et al. (1987). Adaptive Histogram Equalization and Its Variations. Compute Vizion, Graphics, and Image Processing, 39, 355-368

Metadata

Release files for ahe 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ahe 0.1.0
File Size Uploaded
ahe-0.1.0.tar.gz 48.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for ahe 0.1.0
File
ahe-0.1.0-cp310-abi3-win_arm64.whl CPython 3.10 abi3 Windows ARM64 Details
ahe-0.1.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
ahe-0.1.0-cp310-abi3-win32.whl CPython 3.10 abi3 Windows x86-32 Details
ahe-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl CPython 3.10 abi3 Linux musl 1.2+ x86-64 Details
ahe-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl CPython 3.10 abi3 Linux musl 1.2+ ARM64 Details
ahe-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64 Details
ahe-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl CPython 3.10 abi3 Linux glibc 2.28+ ARM64 Details
ahe-0.1.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
ahe-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 2.3 MB

Release files / ahe-0.1.0.tar.gz

Download URL ahe-0.1.0.tar.gz
Size 48.3 kB
Tags Source
SHA-256 checksum
How to use checksums
4f0dcb3d7e9a7742ce3b4eb8a2f9ab87d872456edf86b51948c941a4e4e5cbdb
BLAKE2b-256 checksum
How to use checksums
5b25de6c4bc21f4e1df6e695b4af062f060eab787425c1b6411ce30e55400d9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-win_arm64.whl

Download URL ahe-0.1.0-cp310-abi3-win_arm64.whl
Size 143.1 kB
Tags CPython 3.10 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
4556bc5ec75d81ff11c5dd553dd64815f3f728c411fd113713929b6587567544
BLAKE2b-256 checksum
How to use checksums
6e1898793cfe77a014e9ac7d565e085e72f0a92d76c4370e74b675fd8ed34f73
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-win_amd64.whl

Download URL ahe-0.1.0-cp310-abi3-win_amd64.whl
Size 152.2 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
d3edea04d8348db14e7dff505f289ae7a9504fd88291561b0e6923e0d140ac24
BLAKE2b-256 checksum
How to use checksums
ddf519515a1188c7512576abafe3a4b30ba7b7aa67a8f8b2cfb508bf5ff35ec7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-win32.whl

Download URL ahe-0.1.0-cp310-abi3-win32.whl
Size 144.5 kB
Tags CPython 3.10 Windows x86-32 abi3
SHA-256 checksum
How to use checksums
3773381c0b043294df045d16c7ed0c53c57c468d2fd69a43c3991e3ba00a3080
BLAKE2b-256 checksum
How to use checksums
eebde6cad012e5dfa3a283778572ca99c2c1533cd7b1071e2b78e705a076765a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl

Download URL ahe-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl
Size 467.6 kB
Tags CPython 3.10 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
4fab4ea9a195abe9efdf2af8905dca72293d6aecb5b60b6bc73dc5df4f11ca84
BLAKE2b-256 checksum
How to use checksums
3a4092e3e462548deb1e167f6e4f57cfb9d2be9554bb48853a4fef917205d2f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl

Download URL ahe-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl
Size 425.4 kB
Tags CPython 3.10 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
122a4bb222064773ec2feb4ffd886c307d3ea48fc0f7890fed8f93171f4457be
BLAKE2b-256 checksum
How to use checksums
4fdd2c7037cd635149956b8754040bb035efc662454b9b7e0d5c1969bcd58b3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl

Download URL ahe-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl
Size 255.0 kB
Tags CPython 3.10 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
7765e88d686121ac77662974582bde1417934d0c47fc4f8f94e8b9c40302ffc7
BLAKE2b-256 checksum
How to use checksums
eabbf495887b826e98ea78fae8cddb8373862d5240359e645f0330da40b8a2fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl

Download URL ahe-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl
Size 242.3 kB
Tags CPython 3.10 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
f73a6082cdc3ff6d512eef9aa26e0577c9dbb95a4461506bd27d06e66df791b1
BLAKE2b-256 checksum
How to use checksums
6b23c54c7956fc63c83a65a91e3ee89596a8a09e8941007d7094d98a8fcbdb3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL ahe-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
Size 221.8 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6a016593b55fa9da72edccb9ace4ab606c004addd999ee575d35d44dbf875fb0
BLAKE2b-256 checksum
How to use checksums
72977f79539786f571b01f29e080eec71fc9730e8f112deac666b0045880fb51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release files / ahe-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL ahe-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 233.2 kB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
6e2bf7b597d04c540c686f7acdf2bc2645d75ecc95f484f93a5ed75c5d1cd7dc
BLAKE2b-256 checksum
How to use checksums
b68b4907f351ef14acef86dc2a62da70904dc50ffba05469276bf06375dff35c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

10 release files

0.0.3

1 release file

0.0.2

1 release file

0.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page