View on GitHub ↗2026-04-29
Summary
Patch release: maintainability refactor of very large modules (no intended public API moves) plus targeted OpenCV-backed hot paths in functional helpers. No new transforms.
- Module split (facades unchanged): oversized
functional.py/ transform modules were split into smaller internal files; public import paths likealbumentations.augmentations.pixel.functionalandalbumentations.augmentations.geometric.functionalremain re-export facades (#247). - UnsharpMask: final thresholded blend uses
cv2.compare+cv2.copyTofor common channel layouts (C ∈ {1, 3, 4}); other layouts keep the priornp.wherepath (#248). - PixelDropout:
uint8/float32image paths usecv2.copyTowith explicitly contiguous mask/value buffers where applicable (#248). - ToGray (
method="desaturation",uint8): per-pixel min/max usescv2.split/cv2.max/cv2.minon single(H, W, C)images; batch/volumeuint8inputs keep the prior axis-reduction implementation so semantics stay correct (#248). - Elastic-style displacement noise:
generate_displacement_fieldsGaussian normalization usescv2.norm(..., NORM_INF)instead of allocating a fullabsbuffer (#248). - CopyAndPaste visibility helper:
compute_instance_visibilityprecomputes binary instance masks once and usescv2.countNonZero/cv2.bitwise_and(#248). - CropNonEmptyMaskIfExists: random non-empty pixel sampling uses
cv2.findNonZeroinstead ofnp.argwhereon the aggregated mask (#248). - HEStain / stain math:
rgb_to_optical_densityuses in-placecv2.multiply/cv2.max/cv2.logon a contiguous(N*H*W, 3)float32buffer (avoids silentcv2dst layout failures on non-contiguous reshapes) (#248).j
Breaking changes
None.
New features
None.
Bug fixes
- ToGray (
method="desaturation",uint8): OpenCVcv2.splitdoes not split the channel axis forndim > 3inputs;to_gray_desaturationnow avoids that path for batch/volume tensors and matches the oldnp.max/np.minreduction. Regression coverage added intests/functional/test_functional.py(#248).
Performance
Microbenchmarks during development (local timeit, contiguous outputs where relevant) showed meaningful wins on common shapes for:
- UnsharpMask threshold blend (OpenCV path): ~1.3–1.6× on
256–1024RGBuint8/float32forC ∈ {1, 3}(no intended speedup forC ∉ {1, 3, 4}where NumPy fallback remains). - ToGray desaturation (
uint8,(H, W, C)): large speedups vsnp.max/np.minreductions on RGB/RGBA-ish stacks (exact ratio depends onH×W×C); multispectralC=5/C=9still benefits on the 3D path, with no OpenCV split path forndim > 3. - PixelDropout: ~2–20× on
512–1024RGBuint8vsnp.wherein microbench (mask density dependent). generate_displacement_fieldsmax-abs normalization: ~2.7–5.7× vsnp.abs(...).max()on256–1024planes.compute_instance_visibility: ~2.2–2.8× vs boolean reductions for8–64instance masks at512×512.to_distance_maps(internal helper): OpenCV subtract/magnitude loop wins for modest keypoint counts; falls back to vectorized NumPy for largeNto avoid Python-loop overhead at high keypoint counts (#248).
Misc
- Packaging:
pyproject.toml/uv.lockversion →**2.2.4**. - Pre-commit:
cv2.resize/cv2.remapremain forbidden in-repo; geometric resize/remap continues to go through albucore where required by hooks.
Commits
| Commit | PR | Description |
|---|---|---|
9b65e9e | #247 | Split oversized transform/functional modules into smaller files; keep public facades + export surfaces |
b2e287c | #248 | OpenCV hot paths: UnsharpMask, PixelDropout, ToGray desaturation, HEStain optical density, mixing visibility, crops mask sampling, displacement norm, keypoint distance maps |
159c456 | — | chore: set release version to 2.2.4 (pyproject.toml, uv.lock) |