Skip to content

feat(augment): support ShiftScaleRotate on the Kornia backend - #1370

Open
adhavan18 wants to merge 2 commits into
roboflow:developfrom
adhavan18:feat/kornia-shift-scale-rotate
Open

feat(augment): support ShiftScaleRotate on the Kornia backend#1370
adhavan18 wants to merge 2 commits into
roboflow:developfrom
adhavan18:feat/kornia-shift-scale-rotate

Conversation

@adhavan18

Copy link
Copy Markdown
Contributor

What

Adds ShiftScaleRotate to the Kornia GPU augmentation backend. Follow-up to #1330 and #1277, same issue (#1252).

Why

ShiftScaleRotate is one of the names aug_configs documents and transforms.py accepts on the CPU path, while the Kornia registry rejects it. Backend selection is automatic, so the same aug_config trains on a CPU box and raises on a GPU box:

kornia ShiftScaleRotate -> ValueError: Unknown augmentation key 'ShiftScaleRotate' for Kornia GPU backend
albu   ShiftScaleRotate -> OK

It is safe to add for the same reason Perspective was: it preserves the output resolution. The GPU path rebuilds each batch as NestedTensor(img_aug, samples.mask), reusing the pre-augmentation padding mask, so a transform that resizes leaves the mask describing a different shape. That is why the crops stay unsupported, and it does not apply here.

I should correct something I wrote in #1330 while I am here. The doc block excluded this name along with the crops, on the grounds that the group "move boxes and masks". That reason does not hold for this one: Affine moves boxes too and has always been supported. Resolution change is the real constraint, and ShiftScaleRotate does not change resolution.

The part that is not a pass-through

Albumentations calls this "a special case of Affine transform", so an alias to _make_affine looks obvious. It would be wrong, because the limits are deltas rather than absolute ranges.

scale_limit is biased by 1 — from the Albumentations docstring:

Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).

So the documented default (-0.1, 0.1) means a scale between 0.9 and 1.1. Kornia's scale is the absolute multiplier, so forwarding the raw value would ask it to scale the image to between a tenth of its size and nothing at all. Confirmed against the installed Albumentations rather than trusting the docstring:

>>> A.ShiftScaleRotate(scale_limit=0.1).scale_limit
(0.9, 1.1)

The pivot is applied here, the same way _make_sharpen already pivots alpha at 1.0.

Two smaller differences in the same vein:

param Albumentations mapped as
scale_limit delta biased by 1 scale=(1 + low, 1 + high)
shift_limit signed fraction range translate, Kornia's non-negative per-axis maximum, as _make_affine already does for translate_percent
shift_limit_x / shift_limit_y per-axis override Kornia's translate=(tx, ty) expresses this directly
rotate_limit degrees degrees

All three limits also read a scalar v as the symmetric (-v, v), not the degenerate (v, v) that _as_range gives, so they go through a small _as_symmetric_range helper instead.

interpolation, border_mode, mask_interpolation, fill, fill_mask and rotate_method have no RandomAffine equivalent and are ignored with a warning, matching how ToGray and Sharpen already report their dropped parameters.

One thing worth your call

Albumentations deprecates this name. Constructing it emits:

UserWarning: ShiftScaleRotate is a special case of Affine transform. Please use Affine transform instead.

I mapped it anyway because the CPU path in this repo still accepts it, so the parity gap is real for any config already using it, and albumentations<3.0.0 is pinned. But if you would rather close this name out of aug_configs and point users at Affine — which this backend already supports — that is a reasonable call and I am happy to send that PR instead. The docs note added here says new configs should prefer Affine.

Tests

14 cases in TestShiftScaleRotateFactory, covering the delta semantics (scalar and asymmetric pair), symmetric scalar expansion, the Albumentations defaults, per-axis shifts, each ignored option warning, no spurious warning on an ordinary config, resolution preservation, and boxes moving with the image.

Reading the resolved ranges needs RandomAffine._param_generatorflags carries only the resampling options — so there is a helper that fails with an actionable message if a future Kornia release changes that shape, mirroring the existing _sharpness_sampler_range.

121 passed

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86%. Comparing base (6674d85) to head (5dd38d2).

Additional details and impacted files
@@           Coverage Diff           @@
##           develop   #1370   +/-   ##
=======================================
  Coverage       86%     86%           
=======================================
  Files          114     114           
  Lines        14880   14896   +16     
=======================================
+ Hits         12835   12851   +16     
  Misses        2045    2045           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Closes another name from roboflow#1252. It maps onto the same K.RandomAffine that
Affine already uses, which is what makes it safe: the output resolution is
preserved, so the padding-mask constraint that keeps the crops unsupported
does not apply here.

The limits are not pass-through, which is why this needs its own builder
rather than an alias. Albumentations biases scale_limit by 1, sampling from
(1 + low, 1 + high), so the documented default (-0.1, 0.1) means a scale
between 0.9 and 1.1. Forwarding it unchanged would ask Kornia to scale the
image to between a tenth of its size and nothing at all. All three limits
also read a scalar v as the symmetric (-v, v) rather than the degenerate
(v, v) _as_range gives.

shift_limit_x and shift_limit_y map onto Kornia's per-axis translate. The
six options with no equivalent warn instead of vanishing.

Albumentations deprecates this name in favour of Affine; noted in the docs,
mapped anyway because the CPU path still accepts it and the same config
otherwise trains on a CPU box and raises on a GPU box.
@adhavan18
adhavan18 force-pushed the feat/kornia-shift-scale-rotate branch from abea476 to 670f5ae Compare August 26, 2026 10:21
@adhavan18

Copy link
Copy Markdown
Contributor Author

Bumping this since it's been quiet for a couple weeks — just rebased onto current develop, CI green across the full matrix, no conflicts. Happy to address anything if it needs another look.

@Borda

Borda commented Aug 31, 2026

Copy link
Copy Markdown
Member

Hi, apologies for delay, I was off last week but I have on my radar for later this week...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants