From 85975fd101870a82b3f84fb1f2cfc25c7bc7b0fe Mon Sep 17 00:00:00 2001 From: Ishan Kumar Date: Mon, 7 Jun 2021 13:31:15 +0530 Subject: [PATCH 1/2] Port affine transform tests in test_tranforms to pytest --- test/test_transforms.py | 355 +++++++++++++++++++++------------------- 1 file changed, 188 insertions(+), 167 deletions(-) diff --git a/test/test_transforms.py b/test/test_transforms.py index bf3a030f285..51b208af660 100644 --- a/test/test_transforms.py +++ b/test/test_transforms.py @@ -435,173 +435,6 @@ def test_linear_transformation(self): # Checking if LinearTransformation can be printed as string whitening.__repr__() - def test_affine(self): - input_img = np.zeros((40, 40, 3), dtype=np.uint8) - cnt = [20, 20] - for pt in [(16, 16), (20, 16), (20, 20)]: - for i in range(-5, 5): - for j in range(-5, 5): - input_img[pt[0] + i, pt[1] + j, :] = [255, 155, 55] - - with self.assertRaises(TypeError, msg="Argument translate should be a sequence"): - F.affine(input_img, 10, translate=0, scale=1, shear=1) - - pil_img = F.to_pil_image(input_img) - - def _to_3x3_inv(inv_result_matrix): - result_matrix = np.zeros((3, 3)) - result_matrix[:2, :] = np.array(inv_result_matrix).reshape((2, 3)) - result_matrix[2, 2] = 1 - return np.linalg.inv(result_matrix) - - def _test_transformation(a, t, s, sh): - a_rad = math.radians(a) - s_rad = [math.radians(sh_) for sh_ in sh] - cx, cy = cnt - tx, ty = t - sx, sy = s_rad - rot = a_rad - - # 1) Check transformation matrix: - C = np.array([[1, 0, cx], - [0, 1, cy], - [0, 0, 1]]) - T = np.array([[1, 0, tx], - [0, 1, ty], - [0, 0, 1]]) - Cinv = np.linalg.inv(C) - - RS = np.array( - [[s * math.cos(rot), -s * math.sin(rot), 0], - [s * math.sin(rot), s * math.cos(rot), 0], - [0, 0, 1]]) - - SHx = np.array([[1, -math.tan(sx), 0], - [0, 1, 0], - [0, 0, 1]]) - - SHy = np.array([[1, 0, 0], - [-math.tan(sy), 1, 0], - [0, 0, 1]]) - - RSS = np.matmul(RS, np.matmul(SHy, SHx)) - - true_matrix = np.matmul(T, np.matmul(C, np.matmul(RSS, Cinv))) - - result_matrix = _to_3x3_inv(F._get_inverse_affine_matrix(center=cnt, angle=a, - translate=t, scale=s, shear=sh)) - self.assertLess(np.sum(np.abs(true_matrix - result_matrix)), 1e-10) - # 2) Perform inverse mapping: - true_result = np.zeros((40, 40, 3), dtype=np.uint8) - inv_true_matrix = np.linalg.inv(true_matrix) - for y in range(true_result.shape[0]): - for x in range(true_result.shape[1]): - # Same as for PIL: - # https://github.com/python-pillow/Pillow/blob/71f8ec6a0cfc1008076a023c0756542539d057ab/ - # src/libImaging/Geometry.c#L1060 - input_pt = np.array([x + 0.5, y + 0.5, 1.0]) - res = np.floor(np.dot(inv_true_matrix, input_pt)).astype(np.int) - _x, _y = res[:2] - if 0 <= _x < input_img.shape[1] and 0 <= _y < input_img.shape[0]: - true_result[y, x, :] = input_img[_y, _x, :] - - result = F.affine(pil_img, angle=a, translate=t, scale=s, shear=sh) - self.assertEqual(result.size, pil_img.size) - # Compute number of different pixels: - np_result = np.array(result) - n_diff_pixels = np.sum(np_result != true_result) / 3 - # Accept 3 wrong pixels - self.assertLess(n_diff_pixels, 3, - "a={}, t={}, s={}, sh={}\n".format(a, t, s, sh) + - "n diff pixels={}\n".format(n_diff_pixels)) - - # Test rotation - a = 45 - _test_transformation(a=a, t=(0, 0), s=1.0, sh=(0.0, 0.0)) - - # Test translation - t = [10, 15] - _test_transformation(a=0.0, t=t, s=1.0, sh=(0.0, 0.0)) - - # Test scale - s = 1.2 - _test_transformation(a=0.0, t=(0.0, 0.0), s=s, sh=(0.0, 0.0)) - - # Test shear - sh = [45.0, 25.0] - _test_transformation(a=0.0, t=(0.0, 0.0), s=1.0, sh=sh) - - # Test rotation, scale, translation, shear - for a in range(-90, 90, 36): - for t1 in range(-10, 10, 5): - for s in [0.77, 1.0, 1.27]: - for sh in range(-15, 15, 5): - _test_transformation(a=a, t=(t1, t1), s=s, sh=(sh, sh)) - - def test_random_affine(self): - - with self.assertRaises(ValueError): - transforms.RandomAffine(-0.7) - transforms.RandomAffine([-0.7]) - transforms.RandomAffine([-0.7, 0, 0.7]) - - transforms.RandomAffine([-90, 90], translate=2.0) - transforms.RandomAffine([-90, 90], translate=[-1.0, 1.0]) - transforms.RandomAffine([-90, 90], translate=[-1.0, 0.0, 1.0]) - - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.0]) - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[-1.0, 1.0]) - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, -0.5]) - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 3.0, -0.5]) - - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=-7) - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=[-10]) - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=[-10, 0, 10]) - transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=[-10, 0, 10, 0, 10]) - - # assert fill being either a Sequence or a Number - with self.assertRaises(TypeError): - transforms.RandomAffine(0, fill={}) - - t = transforms.RandomAffine(0, fill=None) - self.assertTrue(t.fill == 0) - - x = np.zeros((100, 100, 3), dtype=np.uint8) - img = F.to_pil_image(x) - - t = transforms.RandomAffine(10, translate=[0.5, 0.3], scale=[0.7, 1.3], shear=[-10, 10, 20, 40]) - for _ in range(100): - angle, translations, scale, shear = t.get_params(t.degrees, t.translate, t.scale, t.shear, - img_size=img.size) - self.assertTrue(-10 < angle < 10) - self.assertTrue(-img.size[0] * 0.5 <= translations[0] <= img.size[0] * 0.5, - "{} vs {}".format(translations[0], img.size[0] * 0.5)) - self.assertTrue(-img.size[1] * 0.5 <= translations[1] <= img.size[1] * 0.5, - "{} vs {}".format(translations[1], img.size[1] * 0.5)) - self.assertTrue(0.7 < scale < 1.3) - self.assertTrue(-10 < shear[0] < 10) - self.assertTrue(-20 < shear[1] < 40) - - # Checking if RandomAffine can be printed as string - t.__repr__() - - t = transforms.RandomAffine(10, interpolation=transforms.InterpolationMode.BILINEAR) - self.assertIn("bilinear", t.__repr__()) - - # assert deprecation warning and non-BC - with self.assertWarnsRegex(UserWarning, r"Argument resample is deprecated and will be removed"): - t = transforms.RandomAffine(10, resample=2) - self.assertEqual(t.interpolation, transforms.InterpolationMode.BILINEAR) - - with self.assertWarnsRegex(UserWarning, r"Argument fillcolor is deprecated and will be removed"): - t = transforms.RandomAffine(10, fillcolor=10) - self.assertEqual(t.fill, 10) - - # assert changed type warning - with self.assertWarnsRegex(UserWarning, r"Argument interpolation should be of type InterpolationMode"): - t = transforms.RandomAffine(10, interpolation=2) - self.assertEqual(t.interpolation, transforms.InterpolationMode.BILINEAR) - def test_autoaugment(self): for policy in transforms.AutoAugmentPolicy: for fill in [None, 85, (128, 128, 128)]: @@ -2090,5 +1923,193 @@ def test_normalize_3d_tensor(): torch.testing.assert_close(target, result2) +class TestAffine: + + input_img = np.zeros((40, 40, 3), dtype=np.uint8) + cnt = [20, 20] + + @pytest.mark.parametrize("pt", [(16, 16), (20, 16), (20, 20)]) + @pytest.mark.parametrize("i", range(-5, 5)) + @pytest.mark.parametrize("j", range(-5, 5)) + def test_make_img_affine(self, pt, i, j): + self.input_img[pt[0] + i, pt[1] + j, :] = [255, 155, 55] + + def test_affine_translate_seq(self): + with pytest.raises(TypeError, match=r"Argument translate should be a sequence"): + F.affine(self.input_img, 10, translate=0, scale=1, shear=1) + + @pytest.fixture + def PIL_Image(self): + return F.to_pil_image(self.input_img) + + def _to_3x3_inv(self, inv_result_matrix): + result_matrix = np.zeros((3, 3)) + result_matrix[:2, :] = np.array(inv_result_matrix).reshape((2, 3)) + result_matrix[2, 2] = 1 + return np.linalg.inv(result_matrix) + + def _test_transformation(self, a, t, s, sh, PIL_Image): + + a_rad = math.radians(a) + s_rad = [math.radians(sh_) for sh_ in sh] + cx, cy = self.cnt + tx, ty = t + sx, sy = s_rad + rot = a_rad + + # 1) Check transformation matrix: + C = np.array([[1, 0, cx], + [0, 1, cy], + [0, 0, 1]]) + T = np.array([[1, 0, tx], + [0, 1, ty], + [0, 0, 1]]) + Cinv = np.linalg.inv(C) + + RS = np.array( + [[s * math.cos(rot), -s * math.sin(rot), 0], + [s * math.sin(rot), s * math.cos(rot), 0], + [0, 0, 1]]) + + SHx = np.array([[1, -math.tan(sx), 0], + [0, 1, 0], + [0, 0, 1]]) + + SHy = np.array([[1, 0, 0], + [-math.tan(sy), 1, 0], + [0, 0, 1]]) + + RSS = np.matmul(RS, np.matmul(SHy, SHx)) + + true_matrix = np.matmul(T, np.matmul(C, np.matmul(RSS, Cinv))) + + result_matrix = self._to_3x3_inv(F._get_inverse_affine_matrix(center=self.cnt, angle=a, + translate=t, scale=s, shear=sh)) + assert np.sum(np.abs(true_matrix - result_matrix)) < 1e-10 + # 2) Perform inverse mapping: + true_result = np.zeros((40, 40, 3), dtype=np.uint8) + inv_true_matrix = np.linalg.inv(true_matrix) + for y in range(true_result.shape[0]): + for x in range(true_result.shape[1]): + # Same as for PIL: + # https://github.com/python-pillow/Pillow/blob/71f8ec6a0cfc1008076a023c0756542539d057ab/ + # src/libImaging/Geometry.c#L1060 + input_pt = np.array([x + 0.5, y + 0.5, 1.0]) + res = np.floor(np.dot(inv_true_matrix, input_pt)).astype(np.int) + _x, _y = res[:2] + if 0 <= _x < self.input_img.shape[1] and 0 <= _y < self.input_img.shape[0]: + true_result[y, x, :] = self.input_img[_y, _x, :] + + result = F.affine(PIL_Image, angle=a, translate=t, scale=s, shear=sh) + assert result.size == PIL_Image.size + # Compute number of different pixels: + np_result = np.array(result) + n_diff_pixels = np.sum(np_result != true_result) / 3 + # Accept 3 wrong pixels + error_msg = 'a={}, t={}, s={}, sh={}\n'.format(a, t, s, sh) + 'n diff pixels={}\n'.format(n_diff_pixels) + assert n_diff_pixels < 3, error_msg + + def test_transformation_discrete(self, PIL_Image): + # Test rotation + a = 45 + self._test_transformation(a=a, t=(0, 0), s=1.0, sh=(0.0, 0.0), PIL_Image=PIL_Image) + + # Test translation + t = [10, 15] + self._test_transformation(a=0.0, t=t, s=1.0, sh=(0.0, 0.0), PIL_Image=PIL_Image) + + # Test scale + s = 1.2 + self._test_transformation(a=0.0, t=(0.0, 0.0), s=s, sh=(0.0, 0.0), PIL_Image=PIL_Image) + + # Test shear + sh = [45.0, 25.0] + self._test_transformation(a=0.0, t=(0.0, 0.0), s=1.0, sh=sh, PIL_Image=PIL_Image) + + @pytest.mark.parametrize("a", range(-90, 90, 36)) + @pytest.mark.parametrize("t1", range(-10, 10, 5)) + @pytest.mark.parametrize("s", [0.77, 1.0, 1.27]) + @pytest.mark.parametrize("sh", range(-15, 15, 5)) + def test_transformation_range(self, a, t1, s, sh, PIL_Image): + self._test_transformation(a=a, t=(t1, t1), s=s, sh=(sh, sh), PIL_Image=PIL_Image) + + +def test_random_affine(): + + with pytest.raises(ValueError): + transforms.RandomAffine(-0.7) + with pytest.raises(ValueError): + transforms.RandomAffine([-0.7]) + with pytest.raises(ValueError): + transforms.RandomAffine([-0.7, 0, 0.7]) + with pytest.raises(TypeError): + transforms.RandomAffine([-90, 90], translate=2.0) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[-1.0, 1.0]) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[-1.0, 0.0, 1.0]) + + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.0]) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[-1.0, 1.0]) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, -0.5]) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 3.0, -0.5]) + + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=-7) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=[-10]) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=[-10, 0, 10]) + with pytest.raises(ValueError): + transforms.RandomAffine([-90, 90], translate=[0.2, 0.2], scale=[0.5, 0.5], shear=[-10, 0, 10, 0, 10]) + + # assert fill being either a Sequence or a Number + with pytest.raises(TypeError): + transforms.RandomAffine(0, fill={}) + + t = transforms.RandomAffine(0, fill=None) + assert t.fill == 0 + + x = np.zeros((100, 100, 3), dtype=np.uint8) + img = F.to_pil_image(x) + + t = transforms.RandomAffine(10, translate=[0.5, 0.3], scale=[0.7, 1.3], shear=[-10, 10, 20, 40]) + for _ in range(100): + angle, translations, scale, shear = t.get_params(t.degrees, t.translate, t.scale, t.shear, + img_size=img.size) + assert -10 < angle < 10 + assert -img.size[0] * 0.5 <= translations[0] <= img.size[0] * 0.5, ("{} vs {}" + .format(translations[0], img.size[0] * 0.5)) + assert -img.size[1] * 0.5 <= translations[1] <= img.size[1] * 0.5, ("{} vs {}" + .format(translations[1], img.size[1] * 0.5)) + assert 0.7 < scale < 1.3 + assert -10 < shear[0] < 10 + assert -20 < shear[1] < 40 + + # Checking if RandomAffine can be printed as string + t.__repr__() + + t = transforms.RandomAffine(10, interpolation=transforms.InterpolationMode.BILINEAR) + assert "bilinear" in t.__repr__() + + # assert deprecation warning and non-BC + with pytest.warns(UserWarning, match=r"Argument resample is deprecated and will be removed"): + t = transforms.RandomAffine(10, resample=2) + assert t.interpolation == transforms.InterpolationMode.BILINEAR + + with pytest.warns(UserWarning, match=r"Argument fillcolor is deprecated and will be removed"): + t = transforms.RandomAffine(10, fillcolor=10) + assert t.fill == 10 + + # assert changed type warning + with pytest.warns(UserWarning, match=r"Argument interpolation should be of type InterpolationMode"): + t = transforms.RandomAffine(10, interpolation=2) + assert t.interpolation == transforms.InterpolationMode.BILINEAR + + if __name__ == '__main__': unittest.main() From 4b057c4f56252bdfb49ceae5eb52529054e36a4c Mon Sep 17 00:00:00 2001 From: Ishan Kumar Date: Mon, 7 Jun 2021 23:59:11 +0530 Subject: [PATCH 2/2] refactor code and update variable names --- test/test_transforms.py | 95 ++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/test/test_transforms.py b/test/test_transforms.py index 51b208af660..61ec1326c43 100644 --- a/test/test_transforms.py +++ b/test/test_transforms.py @@ -1925,22 +1925,22 @@ def test_normalize_3d_tensor(): class TestAffine: - input_img = np.zeros((40, 40, 3), dtype=np.uint8) - cnt = [20, 20] - - @pytest.mark.parametrize("pt", [(16, 16), (20, 16), (20, 20)]) - @pytest.mark.parametrize("i", range(-5, 5)) - @pytest.mark.parametrize("j", range(-5, 5)) - def test_make_img_affine(self, pt, i, j): - self.input_img[pt[0] + i, pt[1] + j, :] = [255, 155, 55] - - def test_affine_translate_seq(self): + @pytest.fixture(scope='class') + def input_img(self): + input_img = np.zeros((40, 40, 3), dtype=np.uint8) + for pt in [(16, 16), (20, 16), (20, 20)]: + for i in range(-5, 5): + for j in range(-5, 5): + input_img[pt[0] + i, pt[1] + j, :] = [255, 155, 55] + return input_img + + def test_affine_translate_seq(self, input_img): with pytest.raises(TypeError, match=r"Argument translate should be a sequence"): - F.affine(self.input_img, 10, translate=0, scale=1, shear=1) + F.affine(input_img, 10, translate=0, scale=1, shear=1) - @pytest.fixture - def PIL_Image(self): - return F.to_pil_image(self.input_img) + @pytest.fixture(scope='class') + def pil_image(self, input_img): + return F.to_pil_image(input_img) def _to_3x3_inv(self, inv_result_matrix): result_matrix = np.zeros((3, 3)) @@ -1948,12 +1948,13 @@ def _to_3x3_inv(self, inv_result_matrix): result_matrix[2, 2] = 1 return np.linalg.inv(result_matrix) - def _test_transformation(self, a, t, s, sh, PIL_Image): + def _test_transformation(self, angle, translate, scale, shear, pil_image, input_img): - a_rad = math.radians(a) - s_rad = [math.radians(sh_) for sh_ in sh] - cx, cy = self.cnt - tx, ty = t + a_rad = math.radians(angle) + s_rad = [math.radians(sh_) for sh_ in shear] + cnt = [20, 20] + cx, cy = cnt + tx, ty = translate sx, sy = s_rad rot = a_rad @@ -1967,8 +1968,8 @@ def _test_transformation(self, a, t, s, sh, PIL_Image): Cinv = np.linalg.inv(C) RS = np.array( - [[s * math.cos(rot), -s * math.sin(rot), 0], - [s * math.sin(rot), s * math.cos(rot), 0], + [[scale * math.cos(rot), -scale * math.sin(rot), 0], + [scale * math.sin(rot), scale * math.cos(rot), 0], [0, 0, 1]]) SHx = np.array([[1, -math.tan(sx), 0], @@ -1983,8 +1984,8 @@ def _test_transformation(self, a, t, s, sh, PIL_Image): true_matrix = np.matmul(T, np.matmul(C, np.matmul(RSS, Cinv))) - result_matrix = self._to_3x3_inv(F._get_inverse_affine_matrix(center=self.cnt, angle=a, - translate=t, scale=s, shear=sh)) + result_matrix = self._to_3x3_inv(F._get_inverse_affine_matrix(center=cnt, angle=angle, + translate=translate, scale=scale, shear=shear)) assert np.sum(np.abs(true_matrix - result_matrix)) < 1e-10 # 2) Perform inverse mapping: true_result = np.zeros((40, 40, 3), dtype=np.uint8) @@ -1997,41 +1998,47 @@ def _test_transformation(self, a, t, s, sh, PIL_Image): input_pt = np.array([x + 0.5, y + 0.5, 1.0]) res = np.floor(np.dot(inv_true_matrix, input_pt)).astype(np.int) _x, _y = res[:2] - if 0 <= _x < self.input_img.shape[1] and 0 <= _y < self.input_img.shape[0]: - true_result[y, x, :] = self.input_img[_y, _x, :] + if 0 <= _x < input_img.shape[1] and 0 <= _y < input_img.shape[0]: + true_result[y, x, :] = input_img[_y, _x, :] - result = F.affine(PIL_Image, angle=a, translate=t, scale=s, shear=sh) - assert result.size == PIL_Image.size + result = F.affine(pil_image, angle=angle, translate=translate, scale=scale, shear=shear) + assert result.size == pil_image.size # Compute number of different pixels: np_result = np.array(result) n_diff_pixels = np.sum(np_result != true_result) / 3 # Accept 3 wrong pixels - error_msg = 'a={}, t={}, s={}, sh={}\n'.format(a, t, s, sh) + 'n diff pixels={}\n'.format(n_diff_pixels) + error_msg = ("angle={}, translate={}, scale={}, shear={}\n".format(angle, translate, scale, shear) + + "n diff pixels={}\n".format(n_diff_pixels)) assert n_diff_pixels < 3, error_msg - def test_transformation_discrete(self, PIL_Image): + def test_transformation_discrete(self, pil_image, input_img): # Test rotation - a = 45 - self._test_transformation(a=a, t=(0, 0), s=1.0, sh=(0.0, 0.0), PIL_Image=PIL_Image) + angle = 45 + self._test_transformation(angle=angle, translate=(0, 0), scale=1.0, + shear=(0.0, 0.0), pil_image=pil_image, input_img=input_img) # Test translation - t = [10, 15] - self._test_transformation(a=0.0, t=t, s=1.0, sh=(0.0, 0.0), PIL_Image=PIL_Image) + translate = [10, 15] + self._test_transformation(angle=0.0, translate=translate, scale=1.0, + shear=(0.0, 0.0), pil_image=pil_image, input_img=input_img) # Test scale - s = 1.2 - self._test_transformation(a=0.0, t=(0.0, 0.0), s=s, sh=(0.0, 0.0), PIL_Image=PIL_Image) + scale = 1.2 + self._test_transformation(angle=0.0, translate=(0.0, 0.0), scale=scale, + shear=(0.0, 0.0), pil_image=pil_image, input_img=input_img) # Test shear - sh = [45.0, 25.0] - self._test_transformation(a=0.0, t=(0.0, 0.0), s=1.0, sh=sh, PIL_Image=PIL_Image) - - @pytest.mark.parametrize("a", range(-90, 90, 36)) - @pytest.mark.parametrize("t1", range(-10, 10, 5)) - @pytest.mark.parametrize("s", [0.77, 1.0, 1.27]) - @pytest.mark.parametrize("sh", range(-15, 15, 5)) - def test_transformation_range(self, a, t1, s, sh, PIL_Image): - self._test_transformation(a=a, t=(t1, t1), s=s, sh=(sh, sh), PIL_Image=PIL_Image) + shear = [45.0, 25.0] + self._test_transformation(angle=0.0, translate=(0.0, 0.0), scale=1.0, + shear=shear, pil_image=pil_image, input_img=input_img) + + @pytest.mark.parametrize("angle", range(-90, 90, 36)) + @pytest.mark.parametrize("translate", range(-10, 10, 5)) + @pytest.mark.parametrize("scale", [0.77, 1.0, 1.27]) + @pytest.mark.parametrize("shear", range(-15, 15, 5)) + def test_transformation_range(self, angle, translate, scale, shear, pil_image, input_img): + self._test_transformation(angle=angle, translate=(translate, translate), scale=scale, + shear=(shear, shear), pil_image=pil_image, input_img=input_img) def test_random_affine():