Contrast-Limited Adaptive Histogram Equalization. Boosts local contrast by equalising the histogram of small overlapping tiles across the image instead of the frame as a whole, with a clip limit that prevents noise amplification in flat areas. In astrophotography: pulls out texture in dust lanes, loosens nebula filaments from their background, and reveals subtle brightness variation in galaxy arms without the blown highlights that straight histogram equalization would produce. SASpro's take wraps OpenCV's CLAHE implementation with a pixel-based tile-size slider (more intuitive than tile count), a global blend slider for strength control, document-mask integration, and live preview.
The active image window is the target — CLAHE runs on its luminance (L* channel in Lab for colour, intensity directly for mono) and the result replaces the source on Apply. The dialog opens with a live preview showing the effect at the default settings (clip = 2.0, tile = 128 px, blend = 100%). It's non-modal and stays open after Apply so you can run it on another document with the same (or edited) settings.
Slider, 0.1–4.0, default 2.0. The maximum height any single histogram bin can reach within a tile before being clipped and its excess mass redistributed uniformly. This is what distinguishes CLAHE from plain adaptive histogram equalisation.
Slider, 8–512 px, step 8, default 128. The size of each equalisation tile in image pixels, not tile count — which is how OpenCV itself takes the parameter. SASpro converts automatically (§ 6.2) so the slider behaves the same way regardless of image resolution: a 128 px tile on a 24 MP frame means the same thing as a 128 px tile on a 6 MP crop.
Slider, 0–100%, default 100%. Final linear mix between the original image and the CLAHE-processed result:
This is cleaner than lowering clip limit to achieve the same visual strength, because clip limit also changes which features are enhanced; blend only changes how much.
Every slider change triggers a 250 ms-debounced re-render of the preview so you can judge the result interactively without each tiny slider nudge triggering a full computation. The preview shows the final blended, mask-gated result — exactly what Apply will commit.
Zoom In, Zoom Out, and Fit to Preview buttons sit above the Apply row. The preview view also supports cursor-anchored mouse-wheel and trackpad zoom with the standard SASpro behaviour. The preview auto-fits to the viewport on first open.
If the active document carries a mask, CLAHE runs on the whole image and then blends through the mask so only masked regions receive the effect, with original pixels preserved everywhere else. The global Blend Amount is applied first, then the mask gating on top; both compose cleanly (§ 6.5).
Apply runs the final computation on the full-resolution original and commits the result to the document as an undoable edit named “CLAHE”. The three slider values are recorded in the preset so Replay Last Action and the preset grip reproduce the exact run. The dialog stays open so you can switch to another open image and apply with the same settings.
Reset returns the three sliders to their defaults (clip 2.0, tile 128 px, blend 100%) and shows the untouched original in the preview. Cancel discards everything.
The handle at the bottom-left of the dialog carries the current three slider values and can be dragged to the canvas to make a shortcut tile, or dropped directly on an image to apply those settings headlessly. Double-clicking the tile reopens this dialog pre-seeded with those values.
All processing on float32 [0, 1]. Colour inputs converted to Lab for L*-only processing; mono processed directly.
SASpro calls cv2.createCLAHE(clipLimit, tileGridSize)
with the slider values. Per tile, OpenCV builds the local 256-bin
histogram h(v), clips bins above the limit:
and redistributes the clipped excess uniformly across all bins so the histogram still integrates to the tile's pixel count:
The redistributed histogram's CDF, scaled to [0, 255], becomes the per-tile transform:
and the output for a given pixel is the bilinear interpolation of the four nearest tile transforms, which keeps tile boundaries from showing as visible seams:
For colour inputs, SASpro converts the image to CIE Lab via
OpenCV's COLOR_RGB2LAB, applies CLAHE only to the L*
channel, and converts back — so chroma (a*, b*) is preserved
and only luminance structure changes.
OpenCV's native parameter is tileGridSize = (n, n)
— the number of tiles across each axis. That means a given
n covers a totally different physical area on a
24 MP frame vs a 6 MP crop, which is a confusing user
experience: “tile count 8” doesn't tell you how large
each tile actually is. SASpro's slider instead takes the tile size
in image pixels and derives the count from the image
dimensions:
so a 128 px slider setting always produces roughly 128 px tiles regardless of image resolution. The clamp to [2, 128] handles edge cases (tile sizes larger than the image, or small enough to blow past OpenCV's internal limits).
Older SASpro presets stored tile count directly as
"tile": N. The current decoder accepts both the
new "tile_px": px and the legacy
"tile": N keys, preferring the former when both
are present. Older shortcut tiles continue to work with their
tile-count interpretation; new ones get pixel-based sizing. The
written preset metadata always uses the new key, so Replay Last
Action after a seed-from-old-preset promotes the entry forward.
Global blend is a straight linear mix between the untouched input and the CLAHE output:
applied before any mask gating. Because it rides on top of the same CLAHE result, the character of the effect (which features get enhanced, which are suppressed) stays locked across blend values — only the amplitude changes. This is a strictly better user experience than lowering the clip limit to weaken the effect, which would also redistribute mass differently and change which features win the equalisation.
If the active document carries a mask m ∈ [0, 1],
it's resampled to the processed image's dimensions and used as a
second blend:
which composes with the global blend from § 10.4 so the
final per-pixel effect strength is the product of β
(global strength) and m (local shape). Set a 50% blend
plus a half-transparent mask and the effect lands at 25% in the
masked region, 0% outside. The order matters: global blend first,
then mask gating, so the mask gates the already-blended
result rather than a full-strength CLAHE output.
cv2.createCLAHE with its bilinear tile
interpolation. SASpro's additions are the pixel-based tile-size
slider with its automatic conversion to OpenCV's tile-count
parameter, the global Blend Amount slider, the live-preview
pipeline with its 250 ms debouncing, document-mask integration
with the described global-then-mask compositing order, and the
backward-compatible preset decoder.