Skip to content

Commit a568c7f

Browse files
authored
Fixes incoherence in affine transformation when center is defined as half image size + 0.5 (#2468)
Incoherence is when affine transformation is 90 degrees rotation and output contains a zero line
1 parent 0344603 commit a568c7f

File tree

2 files changed

+12
-10
lines changed

2 files changed

+12
-10
lines changed

test/test_transforms.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,14 +1311,11 @@ def test_rotate_fill(self):
13111311

13121312
def test_affine(self):
13131313
input_img = np.zeros((40, 40, 3), dtype=np.uint8)
1314-
pts = []
13151314
cnt = [20, 20]
13161315
for pt in [(16, 16), (20, 16), (20, 20)]:
13171316
for i in range(-5, 5):
13181317
for j in range(-5, 5):
13191318
input_img[pt[0] + i, pt[1] + j, :] = [255, 155, 55]
1320-
pts.append((pt[0] + i, pt[1] + j))
1321-
pts = list(set(pts))
13221319

13231320
with self.assertRaises(TypeError):
13241321
F.affine(input_img, 10)
@@ -1373,9 +1370,12 @@ def _test_transformation(a, t, s, sh):
13731370
inv_true_matrix = np.linalg.inv(true_matrix)
13741371
for y in range(true_result.shape[0]):
13751372
for x in range(true_result.shape[1]):
1376-
res = np.dot(inv_true_matrix, [x, y, 1])
1377-
_x = int(res[0] + 0.5)
1378-
_y = int(res[1] + 0.5)
1373+
# Same as for PIL:
1374+
# https://github.com/python-pillow/Pillow/blob/71f8ec6a0cfc1008076a023c0756542539d057ab/
1375+
# src/libImaging/Geometry.c#L1060
1376+
input_pt = np.array([x + 0.5, y + 0.5, 1.0])
1377+
res = np.floor(np.dot(inv_true_matrix, input_pt)).astype(np.int)
1378+
_x, _y = res[:2]
13791379
if 0 <= _x < input_img.shape[1] and 0 <= _y < input_img.shape[0]:
13801380
true_result[y, x, :] = input_img[_y, _x, :]
13811381

@@ -1408,7 +1408,7 @@ def _test_transformation(a, t, s, sh):
14081408
# Test rotation, scale, translation, shear
14091409
for a in range(-90, 90, 25):
14101410
for t1 in range(-10, 10, 5):
1411-
for s in [0.75, 0.98, 1.0, 1.1, 1.2]:
1411+
for s in [0.75, 0.98, 1.0, 1.2, 1.4]:
14121412
for sh in range(-15, 15, 5):
14131413
_test_transformation(a=a, t=(t1, t1), s=s, sh=(sh, sh))
14141414

torchvision/transforms/functional.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import math
22
import numbers
33
import warnings
4-
from collections.abc import Iterable
54
from typing import Any
65

76
import numpy as np
87
from numpy import sin, cos, tan
9-
from PIL import Image, ImageOps, ImageEnhance, __version__ as PILLOW_VERSION
8+
from PIL import Image, __version__ as PILLOW_VERSION
109

1110
import torch
1211
from torch import Tensor
@@ -910,7 +909,10 @@ def affine(img, angle, translate, scale, shear, resample=0, fillcolor=None):
910909
assert scale > 0.0, "Argument scale should be positive"
911910

912911
output_size = img.size
913-
center = (img.size[0] * 0.5 + 0.5, img.size[1] * 0.5 + 0.5)
912+
# center = (img.size[0] * 0.5 + 0.5, img.size[1] * 0.5 + 0.5)
913+
# it is visually better to estimate the center without 0.5 offset
914+
# otherwise image rotated by 90 degrees is shifted 1 pixel
915+
center = (img.size[0] * 0.5, img.size[1] * 0.5)
914916
matrix = _get_inverse_affine_matrix(center, angle, translate, scale, shear)
915917
kwargs = {"fillcolor": fillcolor} if int(PILLOW_VERSION.split('.')[0]) >= 5 else {}
916918
return img.transform(output_size, Image.AFFINE, matrix, resample, **kwargs)

0 commit comments

Comments
 (0)