Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
vision
Commits
55088157
Commit
55088157
authored
Jul 03, 2019
by
ptrblck
Committed by
Francisco Massa
Jul 03, 2019
Browse files
Add shear parallel to y-axis (#1070)
* initial commit * add more checks, fix lint, fix doc
parent
8350645b
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
48 additions
and
23 deletions
+48
-23
test/test_transforms.py
test/test_transforms.py
+12
-10
torchvision/transforms/functional.py
torchvision/transforms/functional.py
+18
-7
torchvision/transforms/transforms.py
torchvision/transforms/transforms.py
+18
-6
No files found.
test/test_transforms.py
View file @
55088157
...
...
@@ -1087,15 +1087,15 @@ class Tester(unittest.TestCase):
def
_test_transformation
(
a
,
t
,
s
,
sh
):
a_rad
=
math
.
radians
(
a
)
s_rad
=
math
.
radians
(
sh
)
s_rad
=
[
math
.
radians
(
sh
_
)
for
sh_
in
sh
]
# 1) Check transformation matrix:
c_matrix
=
np
.
array
([[
1.0
,
0.0
,
cnt
[
0
]],
[
0.0
,
1.0
,
cnt
[
1
]],
[
0.0
,
0.0
,
1.0
]])
c_inv_matrix
=
np
.
linalg
.
inv
(
c_matrix
)
t_matrix
=
np
.
array
([[
1.0
,
0.0
,
t
[
0
]],
[
0.0
,
1.0
,
t
[
1
]],
[
0.0
,
0.0
,
1.0
]])
r_matrix
=
np
.
array
([[
s
*
math
.
cos
(
a_rad
),
-
s
*
math
.
sin
(
a_rad
+
s_rad
),
0.0
],
[
s
*
math
.
sin
(
a_rad
),
s
*
math
.
cos
(
a_rad
+
s_rad
),
0.0
],
r_matrix
=
np
.
array
([[
s
*
math
.
cos
(
a_rad
+
s_rad
[
1
]
),
-
s
*
math
.
sin
(
a_rad
+
s_rad
[
0
]
),
0.0
],
[
s
*
math
.
sin
(
a_rad
+
s_rad
[
1
]
),
s
*
math
.
cos
(
a_rad
+
s_rad
[
0
]
),
0.0
],
[
0.0
,
0.0
,
1.0
]])
true_matrix
=
np
.
dot
(
t_matrix
,
np
.
dot
(
c_matrix
,
np
.
dot
(
r_matrix
,
c_inv_matrix
)))
result_matrix
=
_to_3x3_inv
(
F
.
_get_inverse_affine_matrix
(
center
=
cnt
,
angle
=
a
,
...
...
@@ -1124,18 +1124,18 @@ class Tester(unittest.TestCase):
# Test rotation
a
=
45
_test_transformation
(
a
=
a
,
t
=
(
0
,
0
),
s
=
1.0
,
sh
=
0.0
)
_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
)
_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
)
_test_transformation
(
a
=
0.0
,
t
=
(
0.0
,
0.0
),
s
=
s
,
sh
=
(
0.0
,
0.0
)
)
# Test shear
sh
=
45.0
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
...
...
@@ -1143,7 +1143,7 @@ class Tester(unittest.TestCase):
for
t1
in
range
(
-
10
,
10
,
5
):
for
s
in
[
0.75
,
0.98
,
1.0
,
1.1
,
1.2
]:
for
sh
in
range
(
-
15
,
15
,
5
):
_test_transformation
(
a
=
a
,
t
=
(
t1
,
t1
),
s
=
s
,
sh
=
sh
)
_test_transformation
(
a
=
a
,
t
=
(
t1
,
t1
),
s
=
s
,
sh
=
(
sh
,
sh
)
)
def
test_random_rotation
(
self
):
...
...
@@ -1182,11 +1182,12 @@ class Tester(unittest.TestCase):
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
])
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
])
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
)
...
...
@@ -1196,7 +1197,8 @@ class Tester(unittest.TestCase):
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
<
10
assert
-
10
<
shear
[
0
]
<
10
assert
-
20
<
shear
[
1
]
<
40
# Checking if RandomAffine can be printed as string
t
.
__repr__
()
...
...
torchvision/transforms/functional.py
View file @
55088157
...
...
@@ -722,20 +722,29 @@ def _get_inverse_affine_matrix(center, angle, translate, scale, shear):
# where T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1]
# C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1]
# RSS is rotation with scale and shear matrix
# RSS(a, scale, shear) = [ cos(a)*scale -sin(a + shear)*scale 0]
# [ sin(a)*scale cos(a + shear)*scale 0]
# RSS(a, scale, shear) = [ cos(a
+ shear_y
)*scale -sin(a + shear
_x
)*scale 0]
# [ sin(a
+ shear_y
)*scale cos(a + shear
_x
)*scale 0]
# [ 0 0 1]
# Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1
angle
=
math
.
radians
(
angle
)
if
isinstance
(
shear
,
(
tuple
,
list
))
and
len
(
shear
)
==
2
:
shear
=
[
math
.
radians
(
s
)
for
s
in
shear
]
elif
isinstance
(
shear
,
numbers
.
Number
):
shear
=
math
.
radians
(
shear
)
shear
=
[
shear
,
0
]
else
:
raise
ValueError
(
"Shear should be a single value or a tuple/list containing "
+
"two values. Got {}"
.
format
(
shear
))
scale
=
1.0
/
scale
# Inverted rotation matrix with scale and shear
d
=
math
.
cos
(
angle
+
shear
)
*
math
.
cos
(
angle
)
+
math
.
sin
(
angle
+
shear
)
*
math
.
sin
(
angle
)
d
=
math
.
cos
(
angle
+
shear
[
0
])
*
math
.
cos
(
angle
+
shear
[
1
])
+
\
math
.
sin
(
angle
+
shear
[
0
])
*
math
.
sin
(
angle
+
shear
[
1
])
matrix
=
[
math
.
cos
(
angle
+
shear
),
math
.
sin
(
angle
+
shear
),
0
,
-
math
.
sin
(
angle
),
math
.
cos
(
angle
),
0
math
.
cos
(
angle
+
shear
[
0
]
),
math
.
sin
(
angle
+
shear
[
0
]
),
0
,
-
math
.
sin
(
angle
+
shear
[
1
]
),
math
.
cos
(
angle
+
shear
[
1
]
),
0
]
matrix
=
[
scale
/
d
*
m
for
m
in
matrix
]
...
...
@@ -757,7 +766,9 @@ def affine(img, angle, translate, scale, shear, resample=0, fillcolor=None):
angle (float or int): rotation angle in degrees between -180 and 180, clockwise direction.
translate (list or tuple of integers): horizontal and vertical translations (post-rotation translation)
scale (float): overall scale
shear (float): shear angle value in degrees between -180 to 180, clockwise direction.
shear (float or tuple or list): shear angle value in degrees between -180 to 180, clockwise direction.
If a tuple of list is specified, the first value corresponds to a shear parallel to the x axis, while
the second value corresponds to a shear parallel to the y axis.
resample (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
An optional resampling filter.
See `filters`_ for more information.
...
...
torchvision/transforms/transforms.py
View file @
55088157
...
...
@@ -1013,8 +1013,11 @@ class RandomAffine(object):
scale (tuple, optional): scaling factor interval, e.g (a, b), then scale is
randomly sampled from the range a <= scale <= b. Will keep original scale by default.
shear (sequence or float or int, optional): Range of degrees to select from.
If degrees is a number instead of sequence like (min, max), the range of degrees
will be (-degrees, +degrees). Will not apply shear by default
If shear is a number, a shear parallel to the x axis in the range (-shear, +shear)
will be apllied. Else if shear is a tuple or list of 2 values a shear parallel to the x axis in the
range (shear[0], shear[1]) will be applied. Else if shear is a tuple or list of 4 values,
a x-axis shear in (shear[0], shear[1]) and y-axis shear in (shear[2], shear[3]) will be applied.
Will not apply shear by default
resample ({PIL.Image.NEAREST, PIL.Image.BILINEAR, PIL.Image.BICUBIC}, optional):
An optional resampling filter. See `filters`_ for more information.
If omitted, or if the image has mode "1" or "P", it is set to PIL.Image.NEAREST.
...
...
@@ -1057,9 +1060,14 @@ class RandomAffine(object):
raise
ValueError
(
"If shear is a single number, it must be positive."
)
self
.
shear
=
(
-
shear
,
shear
)
else
:
assert
isinstance
(
shear
,
(
tuple
,
list
))
and
len
(
shear
)
==
2
,
\
"shear should be a list or tuple and it must be of length 2."
self
.
shear
=
shear
assert
isinstance
(
shear
,
(
tuple
,
list
))
and
\
(
len
(
shear
)
==
2
or
len
(
shear
)
==
4
),
\
"shear should be a list or tuple and it must be of length 2 or 4."
# X-Axis shear with [min, max]
if
len
(
shear
)
==
2
:
self
.
shear
=
[
shear
[
0
],
shear
[
1
],
0.
,
0.
]
elif
len
(
shear
)
==
4
:
self
.
shear
=
[
s
for
s
in
shear
]
else
:
self
.
shear
=
shear
...
...
@@ -1088,7 +1096,11 @@ class RandomAffine(object):
scale
=
1.0
if
shears
is
not
None
:
shear
=
random
.
uniform
(
shears
[
0
],
shears
[
1
])
if
len
(
shears
)
==
2
:
shear
=
[
random
.
uniform
(
shears
[
0
],
shears
[
1
]),
0.
]
elif
len
(
shears
)
==
4
:
shear
=
[
random
.
uniform
(
shears
[
0
],
shears
[
1
]),
random
.
uniform
(
shears
[
2
],
shears
[
3
])]
else
:
shear
=
0.0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment