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
6472a5cb
"csrc/pythonInterface.cpp" did not exist on "8400b58cbbc06e0a434cfa71f76c2efd713473fc"
Unverified
Commit
6472a5cb
authored
Aug 28, 2023
by
Philip Meier
Committed by
GitHub
Aug 28, 2023
Browse files
improve affine bounding box reference helper (#7884)
parent
fae53217
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
104 additions
and
78 deletions
+104
-78
test/test_transforms_v2_refactored.py
test/test_transforms_v2_refactored.py
+102
-76
torchvision/transforms/v2/functional/_geometry.py
torchvision/transforms/v2/functional/_geometry.py
+2
-2
No files found.
test/test_transforms_v2_refactored.py
View file @
6472a5cb
...
@@ -351,45 +351,62 @@ def assert_warns_antialias_default_value():
...
@@ -351,45 +351,62 @@ def assert_warns_antialias_default_value():
yield
yield
def
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
*
,
format
,
canvas_size
,
affine_matrix
):
def
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
*
,
affine_matrix
,
new_canvas_size
=
None
,
clamp
=
True
):
def
transform
(
bbox
):
format
=
bounding_boxes
.
format
canvas_size
=
new_canvas_size
or
bounding_boxes
.
canvas_size
def
affine_bounding_boxes
(
bounding_boxes
):
dtype
=
bounding_boxes
.
dtype
# Go to float before converting to prevent precision loss in case of CXCYWH -> XYXY and W or H is 1
# Go to float before converting to prevent precision loss in case of CXCYWH -> XYXY and W or H is 1
in_dtype
=
bbox
.
dtype
input_xyxy
=
F
.
convert_bounding_box_format
(
if
not
torch
.
is_floating_point
(
bbox
):
bounding_boxes
.
to
(
torch
.
float64
,
copy
=
True
),
bbox
=
bbox
.
float
()
bbox_xyxy
=
F
.
convert_bounding_box_format
(
bbox
.
as_subclass
(
torch
.
Tensor
),
old_format
=
format
,
old_format
=
format
,
new_format
=
datapoints
.
BoundingBoxFormat
.
XYXY
,
new_format
=
datapoints
.
BoundingBoxFormat
.
XYXY
,
inplace
=
True
,
inplace
=
True
,
)
)
x1
,
y1
,
x2
,
y2
=
input_xyxy
.
squeeze
(
0
).
tolist
()
points
=
np
.
array
(
points
=
np
.
array
(
[
[
[
bbox_xyxy
[
0
].
item
(),
bbox_xyxy
[
1
].
item
()
,
1.0
],
[
x1
,
y1
,
1.0
],
[
bbox_xyxy
[
2
].
item
(),
bbox_xyxy
[
1
].
item
()
,
1.0
],
[
x2
,
y1
,
1.0
],
[
bbox_xyxy
[
0
].
item
(),
bbox_xyxy
[
3
].
item
()
,
1.0
],
[
x1
,
y2
,
1.0
],
[
bbox_xyxy
[
2
].
item
(),
bbox_xyxy
[
3
].
item
()
,
1.0
],
[
x2
,
y2
,
1.0
],
]
]
)
)
transformed_points
=
np
.
matmul
(
points
,
affine_matrix
.
T
)
transformed_points
=
np
.
matmul
(
points
,
affine_matrix
.
astype
(
points
.
dtype
).
T
)
out_bbox
=
torch
.
tensor
(
output_xyxy
=
torch
.
Tensor
(
[
[
np
.
min
(
transformed_points
[:,
0
]).
item
(),
float
(
np
.
min
(
transformed_points
[:,
0
])),
np
.
min
(
transformed_points
[:,
1
]).
item
(),
float
(
np
.
min
(
transformed_points
[:,
1
])),
np
.
max
(
transformed_points
[:,
0
]).
item
(),
float
(
np
.
max
(
transformed_points
[:,
0
])),
np
.
max
(
transformed_points
[:,
1
]).
item
(),
float
(
np
.
max
(
transformed_points
[:,
1
])),
],
]
dtype
=
bbox_xyxy
.
dtype
,
)
)
out_bbox
=
F
.
convert_bounding_box_format
(
out_bbox
,
old_format
=
datapoints
.
BoundingBoxFormat
.
XYXY
,
new_format
=
format
,
inplace
=
True
output
=
F
.
convert_bounding_box_format
(
output_xyxy
,
old_format
=
datapoints
.
BoundingBoxFormat
.
XYXY
,
new_format
=
format
)
)
# It is important to clamp before casting, especially for CXCYWH format, dtype=int64
out_bbox
=
F
.
clamp_bounding_boxes
(
out_bbox
,
format
=
format
,
canvas_size
=
canvas_size
)
out_bbox
=
out_bbox
.
to
(
dtype
=
in_dtype
)
return
out_bbox
return
torch
.
stack
([
transform
(
b
)
for
b
in
bounding_boxes
.
reshape
(
-
1
,
4
).
unbind
()]).
reshape
(
bounding_boxes
.
shape
)
if
clamp
:
# It is important to clamp before casting, especially for CXCYWH format, dtype=int64
output
=
F
.
clamp_bounding_boxes
(
output
,
format
=
format
,
canvas_size
=
canvas_size
,
).
to
(
dtype
)
return
output
return
datapoints
.
BoundingBoxes
(
torch
.
cat
([
affine_bounding_boxes
(
b
)
for
b
in
bounding_boxes
.
reshape
(
-
1
,
4
).
unbind
()],
dim
=
0
).
reshape
(
bounding_boxes
.
shape
),
format
=
format
,
canvas_size
=
canvas_size
,
)
class
TestResize
:
class
TestResize
:
...
@@ -580,16 +597,13 @@ class TestResize:
...
@@ -580,16 +597,13 @@ class TestResize:
[
new_width
/
old_width
,
0
,
0
],
[
new_width
/
old_width
,
0
,
0
],
[
0
,
new_height
/
old_height
,
0
],
[
0
,
new_height
/
old_height
,
0
],
],
],
dtype
=
"float64"
if
bounding_boxes
.
dtype
==
torch
.
float64
else
"float32"
,
)
)
expected_bboxes
=
reference_affine_bounding_boxes_helper
(
return
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
bounding_boxes
,
format
=
bounding_boxes
.
format
,
canvas_size
=
(
new_height
,
new_width
),
affine_matrix
=
affine_matrix
,
affine_matrix
=
affine_matrix
,
new_canvas_size
=
(
new_height
,
new_width
),
)
)
return
datapoints
.
wrap
(
expected_bboxes
,
like
=
bounding_boxes
,
canvas_size
=
(
new_height
,
new_width
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"size"
,
OUTPUT_SIZES
)
@
pytest
.
mark
.
parametrize
(
"size"
,
OUTPUT_SIZES
)
...
@@ -884,17 +898,9 @@ class TestHorizontalFlip:
...
@@ -884,17 +898,9 @@ class TestHorizontalFlip:
[
-
1
,
0
,
bounding_boxes
.
canvas_size
[
1
]],
[
-
1
,
0
,
bounding_boxes
.
canvas_size
[
1
]],
[
0
,
1
,
0
],
[
0
,
1
,
0
],
],
],
dtype
=
"float64"
if
bounding_boxes
.
dtype
==
torch
.
float64
else
"float32"
,
)
expected_bboxes
=
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
format
=
bounding_boxes
.
format
,
canvas_size
=
bounding_boxes
.
canvas_size
,
affine_matrix
=
affine_matrix
,
)
)
return
datapoints
.
wrap
(
expected_bboxes
,
like
=
bounding_boxes
)
return
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
affine_matrix
=
affine_matrix
)
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
@
pytest
.
mark
.
parametrize
(
...
@@ -1129,26 +1135,19 @@ class TestAffine:
...
@@ -1129,26 +1135,19 @@ class TestAffine:
shear_y_matrix
=
np
.
array
([[
1
,
0
,
0
],
[
-
math
.
tan
(
sy
),
1
,
0
],
[
0
,
0
,
1
]])
shear_y_matrix
=
np
.
array
([[
1
,
0
,
0
],
[
-
math
.
tan
(
sy
),
1
,
0
],
[
0
,
0
,
1
]])
rss_matrix
=
np
.
matmul
(
rs_matrix
,
np
.
matmul
(
shear_y_matrix
,
shear_x_matrix
))
rss_matrix
=
np
.
matmul
(
rs_matrix
,
np
.
matmul
(
shear_y_matrix
,
shear_x_matrix
))
true_matrix
=
np
.
matmul
(
t_matrix
,
np
.
matmul
(
c_matrix
,
np
.
matmul
(
rss_matrix
,
c_matrix_inv
)))
true_matrix
=
np
.
matmul
(
t_matrix
,
np
.
matmul
(
c_matrix
,
np
.
matmul
(
rss_matrix
,
c_matrix_inv
)))
return
true_matrix
return
true_matrix
[:
2
,
:]
def
_reference_affine_bounding_boxes
(
self
,
bounding_boxes
,
*
,
angle
,
translate
,
scale
,
shear
,
center
):
def
_reference_affine_bounding_boxes
(
self
,
bounding_boxes
,
*
,
angle
,
translate
,
scale
,
shear
,
center
):
if
center
is
None
:
if
center
is
None
:
center
=
[
s
*
0.5
for
s
in
bounding_boxes
.
canvas_size
[::
-
1
]]
center
=
[
s
*
0.5
for
s
in
bounding_boxes
.
canvas_size
[::
-
1
]]
affine_matrix
=
self
.
_compute_affine_matrix
(
return
reference_affine_bounding_boxes_helper
(
angle
=
angle
,
translate
=
translate
,
scale
=
scale
,
shear
=
shear
,
center
=
center
)
affine_matrix
=
affine_matrix
[:
2
,
:]
expected_bboxes
=
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
bounding_boxes
,
format
=
bounding_boxes
.
format
,
affine_matrix
=
self
.
_compute_affine_matrix
(
canvas_size
=
bounding_boxes
.
canvas_size
,
angle
=
angle
,
translate
=
translate
,
scale
=
scale
,
shear
=
shear
,
center
=
center
affine_matrix
=
affine_matrix
,
)
,
)
)
return
expected_bboxes
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"angle"
,
_CORRECTNESS_AFFINE_KWARGS
[
"angle"
])
@
pytest
.
mark
.
parametrize
(
"angle"
,
_CORRECTNESS_AFFINE_KWARGS
[
"angle"
])
@
pytest
.
mark
.
parametrize
(
"translate"
,
_CORRECTNESS_AFFINE_KWARGS
[
"translate"
])
@
pytest
.
mark
.
parametrize
(
"translate"
,
_CORRECTNESS_AFFINE_KWARGS
[
"translate"
])
...
@@ -1347,17 +1346,9 @@ class TestVerticalFlip:
...
@@ -1347,17 +1346,9 @@ class TestVerticalFlip:
[
1
,
0
,
0
],
[
1
,
0
,
0
],
[
0
,
-
1
,
bounding_boxes
.
canvas_size
[
0
]],
[
0
,
-
1
,
bounding_boxes
.
canvas_size
[
0
]],
],
],
dtype
=
"float64"
if
bounding_boxes
.
dtype
==
torch
.
float64
else
"float32"
,
)
expected_bboxes
=
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
format
=
bounding_boxes
.
format
,
canvas_size
=
bounding_boxes
.
canvas_size
,
affine_matrix
=
affine_matrix
,
)
)
return
datapoints
.
wrap
(
expected_bboxes
,
like
=
bounding_boxes
)
return
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
affine_matrix
=
affine_matrix
)
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"fn"
,
[
F
.
vertical_flip
,
transform_cls_to_functional
(
transforms
.
RandomVerticalFlip
,
p
=
1
)])
@
pytest
.
mark
.
parametrize
(
"fn"
,
[
F
.
vertical_flip
,
transform_cls_to_functional
(
transforms
.
RandomVerticalFlip
,
p
=
1
)])
...
@@ -1535,39 +1526,73 @@ class TestRotate:
...
@@ -1535,39 +1526,73 @@ class TestRotate:
mae
=
(
actual
.
float
()
-
expected
.
float
()).
abs
().
mean
()
mae
=
(
actual
.
float
()
-
expected
.
float
()).
abs
().
mean
()
assert
mae
<
1
if
interpolation
is
transforms
.
InterpolationMode
.
NEAREST
else
6
assert
mae
<
1
if
interpolation
is
transforms
.
InterpolationMode
.
NEAREST
else
6
def
_reference_rotate_bounding_boxes
(
self
,
bounding_boxes
,
*
,
angle
,
expand
,
center
):
def
_compute_output_canvas_size
(
self
,
*
,
expand
,
canvas_size
,
affine_matrix
):
# FIXME
if
not
expand
:
if
expand
:
return
canvas_size
,
(
0.0
,
0.0
)
raise
ValueError
(
"This reference currently does not support expand=True"
)
input_height
,
input_width
=
canvas_size
input_image_frame
=
np
.
array
(
[
[
0.0
,
0.0
,
1.0
],
[
0.0
,
input_height
,
1.0
],
[
input_width
,
input_height
,
1.0
],
[
input_width
,
0.0
,
1.0
],
],
dtype
=
np
.
float64
,
)
output_image_frame
=
np
.
matmul
(
input_image_frame
,
affine_matrix
.
astype
(
input_image_frame
.
dtype
).
T
)
recenter_x
=
float
(
np
.
min
(
output_image_frame
[:,
0
]))
recenter_y
=
float
(
np
.
min
(
output_image_frame
[:,
1
]))
output_width
=
int
(
np
.
max
(
output_image_frame
[:,
0
])
-
recenter_x
)
output_height
=
int
(
np
.
max
(
output_image_frame
[:,
1
])
-
recenter_y
)
return
(
output_height
,
output_width
),
(
recenter_x
,
recenter_y
)
def
_recenter_bounding_boxes_after_expand
(
self
,
bounding_boxes
,
*
,
recenter_xy
):
x
,
y
=
recenter_xy
if
bounding_boxes
.
format
is
datapoints
.
BoundingBoxFormat
.
XYXY
:
translate
=
[
x
,
y
,
x
,
y
]
else
:
translate
=
[
x
,
y
,
0.0
,
0.0
]
return
datapoints
.
wrap
(
(
bounding_boxes
.
to
(
torch
.
float64
)
-
torch
.
tensor
(
translate
)).
to
(
bounding_boxes
.
dtype
),
like
=
bounding_boxes
)
def
_reference_rotate_bounding_boxes
(
self
,
bounding_boxes
,
*
,
angle
,
expand
,
center
):
if
center
is
None
:
if
center
is
None
:
center
=
[
s
*
0.5
for
s
in
bounding_boxes
.
canvas_size
[::
-
1
]]
center
=
[
s
*
0.5
for
s
in
bounding_boxes
.
canvas_size
[::
-
1
]]
cx
,
cy
=
center
a
=
np
.
cos
(
angle
*
np
.
pi
/
180.0
)
a
=
np
.
cos
(
angle
*
np
.
pi
/
180.0
)
b
=
np
.
sin
(
angle
*
np
.
pi
/
180.0
)
b
=
np
.
sin
(
angle
*
np
.
pi
/
180.0
)
cx
=
center
[
0
]
cy
=
center
[
1
]
affine_matrix
=
np
.
array
(
affine_matrix
=
np
.
array
(
[
[
[
a
,
b
,
cx
-
cx
*
a
-
b
*
cy
],
[
a
,
b
,
cx
-
cx
*
a
-
b
*
cy
],
[
-
b
,
a
,
cy
+
cx
*
b
-
a
*
cy
],
[
-
b
,
a
,
cy
+
cx
*
b
-
a
*
cy
],
],
],
dtype
=
"float64"
if
bounding_boxes
.
dtype
==
torch
.
float64
else
"float32"
,
)
)
expected_bboxes
=
reference_affine_bounding_boxes_helper
(
new_canvas_size
,
recenter_xy
=
self
.
_compute_output_canvas_size
(
expand
=
expand
,
canvas_size
=
bounding_boxes
.
canvas_size
,
affine_matrix
=
affine_matrix
)
output
=
reference_affine_bounding_boxes_helper
(
bounding_boxes
,
bounding_boxes
,
format
=
bounding_boxes
.
format
,
canvas_size
=
bounding_boxes
.
canvas_size
,
affine_matrix
=
affine_matrix
,
affine_matrix
=
affine_matrix
,
new_canvas_size
=
new_canvas_size
,
clamp
=
False
,
)
)
return
expected_bboxes
return
F
.
clamp_bounding_boxes
(
self
.
_recenter_bounding_boxes_after_expand
(
output
,
recenter_xy
=
recenter_xy
)).
to
(
bounding_boxes
)
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"angle"
,
_CORRECTNESS_AFFINE_KWARGS
[
"angle"
])
@
pytest
.
mark
.
parametrize
(
"angle"
,
_CORRECTNESS_AFFINE_KWARGS
[
"angle"
])
# TODO: add support for expand=True in the reference
@
pytest
.
mark
.
parametrize
(
"expand"
,
[
False
,
True
])
@
pytest
.
mark
.
parametrize
(
"expand"
,
[
False
])
@
pytest
.
mark
.
parametrize
(
"center"
,
_CORRECTNESS_AFFINE_KWARGS
[
"center"
])
@
pytest
.
mark
.
parametrize
(
"center"
,
_CORRECTNESS_AFFINE_KWARGS
[
"center"
])
def
test_functional_bounding_boxes_correctness
(
self
,
format
,
angle
,
expand
,
center
):
def
test_functional_bounding_boxes_correctness
(
self
,
format
,
angle
,
expand
,
center
):
bounding_boxes
=
make_bounding_boxes
(
format
=
format
)
bounding_boxes
=
make_bounding_boxes
(
format
=
format
)
...
@@ -1576,10 +1601,10 @@ class TestRotate:
...
@@ -1576,10 +1601,10 @@ class TestRotate:
expected
=
self
.
_reference_rotate_bounding_boxes
(
bounding_boxes
,
angle
=
angle
,
expand
=
expand
,
center
=
center
)
expected
=
self
.
_reference_rotate_bounding_boxes
(
bounding_boxes
,
angle
=
angle
,
expand
=
expand
,
center
=
center
)
torch
.
testing
.
assert_close
(
actual
,
expected
)
torch
.
testing
.
assert_close
(
actual
,
expected
)
torch
.
testing
.
assert_close
(
F
.
get_size
(
actual
),
F
.
get_size
(
expected
),
atol
=
2
if
expand
else
0
,
rtol
=
0
)
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
@
pytest
.
mark
.
parametrize
(
"format"
,
list
(
datapoints
.
BoundingBoxFormat
))
# TODO: add support for expand=True in the reference
@
pytest
.
mark
.
parametrize
(
"expand"
,
[
False
,
True
])
@
pytest
.
mark
.
parametrize
(
"expand"
,
[
False
])
@
pytest
.
mark
.
parametrize
(
"center"
,
_CORRECTNESS_AFFINE_KWARGS
[
"center"
])
@
pytest
.
mark
.
parametrize
(
"center"
,
_CORRECTNESS_AFFINE_KWARGS
[
"center"
])
@
pytest
.
mark
.
parametrize
(
"seed"
,
list
(
range
(
5
)))
@
pytest
.
mark
.
parametrize
(
"seed"
,
list
(
range
(
5
)))
def
test_transform_bounding_boxes_correctness
(
self
,
format
,
expand
,
center
,
seed
):
def
test_transform_bounding_boxes_correctness
(
self
,
format
,
expand
,
center
,
seed
):
...
@@ -1596,6 +1621,7 @@ class TestRotate:
...
@@ -1596,6 +1621,7 @@ class TestRotate:
expected
=
self
.
_reference_rotate_bounding_boxes
(
bounding_boxes
,
**
params
,
expand
=
expand
,
center
=
center
)
expected
=
self
.
_reference_rotate_bounding_boxes
(
bounding_boxes
,
**
params
,
expand
=
expand
,
center
=
center
)
torch
.
testing
.
assert_close
(
actual
,
expected
)
torch
.
testing
.
assert_close
(
actual
,
expected
)
torch
.
testing
.
assert_close
(
F
.
get_size
(
actual
),
F
.
get_size
(
expected
),
atol
=
2
if
expand
else
0
,
rtol
=
0
)
@
pytest
.
mark
.
parametrize
(
"degrees"
,
_EXHAUSTIVE_TYPE_TRANSFORM_AFFINE_RANGES
[
"degrees"
])
@
pytest
.
mark
.
parametrize
(
"degrees"
,
_EXHAUSTIVE_TYPE_TRANSFORM_AFFINE_RANGES
[
"degrees"
])
@
pytest
.
mark
.
parametrize
(
"seed"
,
list
(
range
(
10
)))
@
pytest
.
mark
.
parametrize
(
"seed"
,
list
(
range
(
10
)))
...
...
torchvision/transforms/v2/functional/_geometry.py
View file @
6472a5cb
...
@@ -803,8 +803,8 @@ def _affine_bounding_boxes_with_expand(
...
@@ -803,8 +803,8 @@ def _affine_bounding_boxes_with_expand(
tr
=
torch
.
amin
(
new_points
,
dim
=
0
,
keepdim
=
True
)
tr
=
torch
.
amin
(
new_points
,
dim
=
0
,
keepdim
=
True
)
# Translate bounding boxes
# Translate bounding boxes
out_bboxes
.
sub_
(
tr
.
repeat
((
1
,
2
)))
out_bboxes
.
sub_
(
tr
.
repeat
((
1
,
2
)))
# Estimate meta-data for image with inverted=True
and with center=[0,0]
# Estimate meta-data for image with inverted=True
affine_vector
=
_get_inverse_affine_matrix
(
[
0.0
,
0.0
]
,
angle
,
translate
,
scale
,
shear
)
affine_vector
=
_get_inverse_affine_matrix
(
center
,
angle
,
translate
,
scale
,
shear
)
new_width
,
new_height
=
_compute_affine_output_size
(
affine_vector
,
width
,
height
)
new_width
,
new_height
=
_compute_affine_output_size
(
affine_vector
,
width
,
height
)
canvas_size
=
(
new_height
,
new_width
)
canvas_size
=
(
new_height
,
new_width
)
...
...
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