Visitar URL original
Use rot90 for right-angle tensor rotations in transforms.functional.rotate by davrens101 · Pull Request #9749 · pytorch/vision · GitHub
Skip to content

Use rot90 for right-angle tensor rotations in transforms.functional.rotate - #9749

Open
davrens101 wants to merge 1 commit into
pytorch:mainfrom
davrens101:vis-rotate-right-angle-r5
Open

davrens101 wants to merge 1 commit into
pytorch:mainfrom
davrens101:vis-rotate-right-angle-r5

Conversation

@davrens101

Copy link
Copy Markdown

torchvision.transforms.functional.rotate on a tensor with expand=True returns an output that is one pixel too large in each dimension for exact right-angle rotations when height and width differ by an odd number. PIL and the v2 rotate return the exact transposed size. This also affects transforms.RandomRotation((90, 90), expand=True) on tensors.

Seen on current main (4260301):

x = torch.zeros(3, 33, 64, dtype=torch.uint8)
v1 tensor  torch.Size([3, 65, 34])
PIL        (64, 33)
v2 tensor  torch.Size([3, 64, 33])
v1 RandomRotation((90,90), expand=True)  torch.Size([3, 65, 34])

The cause is that the v1 tensor path always goes through _compute_affine_output_size, which shifts the rotated corners by the unrotated half-extents. For a 90 degree turn of a 64x33 image the corners land on half-pixel positions, so ceil(max) - floor(min) gives 65x34. PIL special-cases right angles with a transpose, and the v2 rotate_image already has the same fast path (#8295), so v1 was the odd one out.

This change adds the same fast path to the tensor branch of transforms.functional.rotate: for center is None, angles of 0 and 180 degrees, and 90/270 degrees when expand=True or the image is square, it uses torch.rot90 (or clone() for 0). Other cases still go through the affine grid. torch.rot90 is scriptable and the scripted function is covered in the new test.

Tests: I added test_rotate_right_angles to test/test_functional_tensor.py. It compares eager and scripted rotate against PIL for angles 0, 90, 180, 270, -90, 450 with expand on and off, on 33x64, 26x26 and 16x20 images. Shape is always compared; pixel equality is checked when expand=True, when the image is square, or when the angle is a multiple of 180 (non-square expand=False quarter turns can't be exact against PIL, which rotates and crops, and that path is unchanged).

  • New test on upstream source: 8 failed, 64 passed, 72 skipped (the skips are the CUDA parametrizations).
  • New test with the fix: 72 passed, 72 skipped.
  • test/test_functional_tensor.py in full with the fix: 11307 passed, 11305 skipped.
  • test/test_transforms.py -k rotat: 6 passed.
  • flake8 (max line length 120) and black (line length 120) are clean on the two touched files.

Limits: CPU only, CUDA tests were skipped. I ran against a CPU wheel (0.29.2) with the Python sources from main copied over it, since I can't build the C++ extension on this machine; ufmt itself was not run, flake8 and black were. Behaviour change to note: right-angle tensor rotations are now exact transposes instead of nearest-neighbour grid samples, matching what v2 and PIL already do.

I used an AI coding assistant to write and test this change, and the numbers above come from its runs on my machine.

@pytorch-bot

pytorch-bot Bot commented Nov 10, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/vision/9749

Note: Links to docs will display an error until the docs builds have been completed.

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the cla signed label Nov 10, 2026

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant