Skip to content

CLAHE clip_limit: Kornia backend pins a scalar where Albumentations samples a range #1349

Description

@adhavan18

Search before asking

  • I have searched the RF-DETR issues and found no similar bug report.

Bug

On the Kornia GPU backend, CLAHE applies a fixed clip_limit where the CPU (Albumentations) backend samples one from a range. This affects the default configuration, so it is not limited to unusual configs.

Albumentations expands a scalar clip_limit to (1, v):

>>> import albumentations as A
>>> A.CLAHE(clip_limit=4.0).clip_limit
(1.0, 4.0)

_make_clahe in src/rfdetr/datasets/kornia_transforms.py routes it through _as_range, which expands a scalar to (v, v):

return random_clahe(
    clip_limit=_as_range(params.get("clip_limit", 4.0)),
    ...
)

4.0 is the default on both sides, so with no user configuration at all the two backends disagree:

aug_config Kornia clip_limit Albumentations clip_limit
{} (default) (4.0, 4.0) (1.0, 4.0)
{"clip_limit": 4.0} (4.0, 4.0) (1.0, 4.0)
{"clip_limit": 2.0} (2.0, 2.0) (1.0, 2.0)
{"clip_limit": (1.0, 4.0)} (1.0, 4.0) (1.0, 4.0)

Only the explicit-pair form agrees. Every scalar form, including the default, pins the GPU path to maximum contrast enhancement on every sample while the CPU path varies it, and nothing is logged.

The docstring states the opposite:

Both parameters map directly: Albumentations' clip_limit (a scalar or a pair) becomes Kornia's clip_limit range

_as_range's own docstring is accurate about what it does — it expands a scalar to the degenerate (v, v) — so this is a wrong choice of helper at the call site rather than a bug inside the helper.

Expected behavior

A scalar clip_limit should reach Kornia as (1, v), matching how Albumentations reads the same config, so the two backends draw from the same distribution.

Minimal Reproducible Example

import albumentations as A
from rfdetr.datasets.kornia_transforms import _make_clahe

print(_make_clahe({}).clip_limit)              # (4.0, 4.0)
print(A.CLAHE().clip_limit)                    # (1.0, 4.0)

Environment

  • RF-DETR: develop at b18c9ca
  • kornia 0.8.3, albumentations 2.0.8, torch 2.6.0
  • CPU only; no GPU or model weights needed to reproduce

Additional

Introduced in #1277, which added the CLAHE mapping — mine, so I would like to fix it.

I checked the other factories that call _as_range for the same class of mismatch:

  • Sharpen (alpha) and GaussNoise (std_range) — Albumentations rejects a scalar for both with ValueError, so there is no scalar form to disagree about.
  • GaussianBlur (sigma) — Albumentations expands a scalar sigma_limit to (0, v) rather than (v, v), so the same gap exists, but only when a user passes a scalar; the configured default is already a pair. Happy to include it here or leave it separate, whichever you prefer.

Are you willing to submit a PR?

  • Yes, I'd like to help by submitting a PR!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions