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
ModelZoo
ResNet50_tensorflow
Commits
abd09bdb
Commit
abd09bdb
authored
Jul 17, 2020
by
Vighnesh Birodkar
Committed by
TF Object Detection Team
Jul 17, 2020
Browse files
Internal change
PiperOrigin-RevId: 321865650
parent
756e4741
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
306 additions
and
7 deletions
+306
-7
research/object_detection/core/target_assigner.py
research/object_detection/core/target_assigner.py
+131
-7
research/object_detection/core/target_assigner_test.py
research/object_detection/core/target_assigner_test.py
+175
-0
No files found.
research/object_detection/core/target_assigner.py
View file @
abd09bdb
...
...
@@ -1600,6 +1600,17 @@ class CenterNetKeypointTargetAssigner(object):
return
(
batch_indices
,
batch_offsets
,
batch_weights
)
def
_resize_masks
(
masks
,
height
,
width
,
method
):
# Resize segmentation masks to conform to output dimensions. Use TF2
# image resize because TF1's version is buggy:
# https://yaqs.corp.google.com/eng/q/4970450458378240
masks
=
tf2
.
image
.
resize
(
masks
[:,
:,
:,
tf
.
newaxis
],
size
=
(
height
,
width
),
method
=
method
)
return
masks
[:,
:,
:,
0
]
class
CenterNetMaskTargetAssigner
(
object
):
"""Wrapper to compute targets for segmentation masks."""
...
...
@@ -1641,13 +1652,9 @@ class CenterNetMaskTargetAssigner(object):
segmentation_targets_list
=
[]
for
gt_masks
,
gt_classes
in
zip
(
gt_masks_list
,
gt_classes_list
):
# Resize segmentation masks to conform to output dimensions. Use TF2
# image resize because TF1's version is buggy:
# https://yaqs.corp.google.com/eng/q/4970450458378240
gt_masks
=
tf2
.
image
.
resize
(
gt_masks
[:,
:,
:,
tf
.
newaxis
],
size
=
(
output_height
,
output_width
),
method
=
mask_resize_method
)
gt_masks
=
_resize_masks
(
gt_masks
,
output_height
,
output_width
,
mask_resize_method
)
gt_masks
=
gt_masks
[:,
:,
:,
tf
.
newaxis
]
gt_classes_reshaped
=
tf
.
reshape
(
gt_classes
,
[
-
1
,
1
,
1
,
num_classes
])
# Shape: [h, w, num_classes].
segmentations_for_image
=
tf
.
reduce_max
(
...
...
@@ -1771,3 +1778,120 @@ class CenterNetDensePoseTargetAssigner(object):
batch_surface_coords
=
tf
.
concat
(
batch_surface_coords
,
axis
=
0
)
batch_weights
=
tf
.
concat
(
batch_weights
,
axis
=
0
)
return
batch_indices
,
batch_part_ids
,
batch_surface_coords
,
batch_weights
def
filter_mask_overlap_min_area
(
masks
):
"""If a pixel belongs to 2 instances, remove it from the larger instance."""
num_instances
=
tf
.
shape
(
masks
)[
0
]
def
_filter_min_area
():
"""Helper function to filter non empty masks."""
areas
=
tf
.
reduce_sum
(
masks
,
axis
=
[
1
,
2
],
keepdims
=
True
)
per_pixel_area
=
masks
*
areas
# Make sure background is ignored in argmin.
per_pixel_area
=
(
masks
*
per_pixel_area
+
(
1
-
masks
)
*
per_pixel_area
.
dtype
.
max
)
min_index
=
tf
.
cast
(
tf
.
argmin
(
per_pixel_area
,
axis
=
0
),
tf
.
int32
)
filtered_masks
=
(
tf
.
range
(
num_instances
)[:,
tf
.
newaxis
,
tf
.
newaxis
]
==
min_index
[
tf
.
newaxis
,
:,
:]
)
return
tf
.
cast
(
filtered_masks
,
tf
.
float32
)
*
masks
return
tf
.
cond
(
num_instances
>
0
,
_filter_min_area
,
lambda
:
masks
)
def
filter_mask_overlap
(
masks
,
method
=
'min_area'
):
if
method
==
'min_area'
:
return
filter_mask_overlap_min_area
(
masks
)
else
:
raise
ValueError
(
'Unknown mask overlap filter type - {}'
.
format
(
method
))
class
CenterNetCornerOffsetTargetAssigner
(
object
):
"""Wrapper to compute corner offsets for boxes using masks."""
def
__init__
(
self
,
stride
,
overlap_resolution
=
'min_area'
):
"""Initializes the corner offset target assigner.
Args:
stride: int, the stride of the network in output pixels.
overlap_resolution: string, specifies how we handle overlapping
instance masks. Currently only 'min_area' is supported which assigns
overlapping pixels to the instance with the minimum area.
"""
self
.
_stride
=
stride
self
.
_overlap_resolution
=
overlap_resolution
def
assign_corner_offset_targets
(
self
,
gt_boxes_list
,
gt_masks_list
):
"""Computes the corner offset targets and foreground map.
For each pixel that is part of any object's foreground, this function
computes the relative offsets to the top-left and bottom-right corners of
that instance's bounding box. It also returns a foreground map to indicate
which pixels contain valid corner offsets.
Args:
gt_boxes_list: A list of float tensors with shape [num_boxes, 4]
representing the groundtruth detection bounding boxes for each sample in
the batch. The coordinates are expected in normalized coordinates.
gt_masks_list: A list of float tensors with shape [num_boxes,
input_height, input_width] with values in {0, 1} representing instance
masks for each object.
Returns:
corner_offsets: A float tensor of shape [batch_size, height, width, 4]
containing, in order, the (y, x) offsets to the top left corner and
the (y, x) offsets to the bottom right corner for each foregroung pixel
foreground: A float tensor of shape [batch_size, height, width] in which
each pixel is set to 1 if it is a part of any instance's foreground
(and thus contains valid corner offsets) and 0 otherwise.
"""
_
,
input_height
,
input_width
=
(
shape_utils
.
combined_static_and_dynamic_shape
(
gt_masks_list
[
0
]))
output_height
=
input_height
//
self
.
_stride
output_width
=
input_width
//
self
.
_stride
y_grid
,
x_grid
=
tf
.
meshgrid
(
tf
.
range
(
output_height
),
tf
.
range
(
output_width
),
indexing
=
'ij'
)
y_grid
,
x_grid
=
tf
.
cast
(
y_grid
,
tf
.
float32
),
tf
.
cast
(
x_grid
,
tf
.
float32
)
corner_targets
=
[]
foreground_targets
=
[]
for
gt_masks
,
gt_boxes
in
zip
(
gt_masks_list
,
gt_boxes_list
):
gt_masks
=
_resize_masks
(
gt_masks
,
output_height
,
output_width
,
method
=
ResizeMethod
.
NEAREST_NEIGHBOR
)
gt_masks
=
filter_mask_overlap
(
gt_masks
,
self
.
_overlap_resolution
)
ymin
,
xmin
,
ymax
,
xmax
=
tf
.
unstack
(
gt_boxes
,
axis
=
1
)
ymin
,
ymax
=
ymin
*
output_height
,
ymax
*
output_height
xmin
,
xmax
=
xmin
*
output_width
,
xmax
*
output_width
top_y
=
ymin
[:,
tf
.
newaxis
,
tf
.
newaxis
]
-
y_grid
[
tf
.
newaxis
]
left_x
=
xmin
[:,
tf
.
newaxis
,
tf
.
newaxis
]
-
x_grid
[
tf
.
newaxis
]
bottom_y
=
ymax
[:,
tf
.
newaxis
,
tf
.
newaxis
]
-
y_grid
[
tf
.
newaxis
]
right_x
=
xmax
[:,
tf
.
newaxis
,
tf
.
newaxis
]
-
x_grid
[
tf
.
newaxis
]
foreground_target
=
tf
.
cast
(
tf
.
reduce_sum
(
gt_masks
,
axis
=
0
)
>
0.5
,
tf
.
float32
)
foreground_targets
.
append
(
foreground_target
)
corner_target
=
tf
.
stack
([
tf
.
reduce_sum
(
top_y
*
gt_masks
,
axis
=
0
),
tf
.
reduce_sum
(
left_x
*
gt_masks
,
axis
=
0
),
tf
.
reduce_sum
(
bottom_y
*
gt_masks
,
axis
=
0
),
tf
.
reduce_sum
(
right_x
*
gt_masks
,
axis
=
0
),
],
axis
=
2
)
corner_targets
.
append
(
corner_target
)
return
(
tf
.
stack
(
corner_targets
,
axis
=
0
),
tf
.
stack
(
foreground_targets
,
axis
=
0
))
research/object_detection/core/target_assigner_test.py
View file @
abd09bdb
...
...
@@ -1999,6 +1999,181 @@ class CenterNetDensePoseTargetAssignerTest(test_case.TestCase):
self
.
assertAllClose
(
expected_batch_weights
,
batch_weights
)
class
CornerOffsetTargetAssignerTest
(
test_case
.
TestCase
):
def
test_filter_overlap_min_area_empty
(
self
):
"""Test that empty masks work on CPU."""
def
graph_fn
(
masks
):
return
targetassigner
.
filter_mask_overlap_min_area
(
masks
)
masks
=
self
.
execute_cpu
(
graph_fn
,
[
np
.
zeros
((
0
,
5
,
5
),
dtype
=
np
.
float32
)])
self
.
assertEqual
(
masks
.
shape
,
(
0
,
5
,
5
))
def
test_filter_overlap_min_area
(
self
):
"""Test the object with min. area is selected instead of overlap."""
def
graph_fn
(
masks
):
return
targetassigner
.
filter_mask_overlap_min_area
(
masks
)
masks
=
np
.
zeros
((
3
,
4
,
4
),
dtype
=
np
.
float32
)
masks
[
0
,
:
2
,
:
2
]
=
1.0
masks
[
1
,
:
3
,
:
3
]
=
1.0
masks
[
2
,
3
,
3
]
=
1.0
masks
=
self
.
execute
(
graph_fn
,
[
masks
])
self
.
assertAllClose
(
masks
[
0
],
[[
1
,
1
,
0
,
0
],
[
1
,
1
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
]])
self
.
assertAllClose
(
masks
[
1
],
[[
0
,
0
,
1
,
0
],
[
0
,
0
,
1
,
0
],
[
1
,
1
,
1
,
0
],
[
0
,
0
,
0
,
0
]])
self
.
assertAllClose
(
masks
[
2
],
[[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
1
]])
def
test_assign_corner_offset_single_object
(
self
):
"""Test that corner offsets are correct with a single object."""
assigner
=
targetassigner
.
CenterNetCornerOffsetTargetAssigner
(
stride
=
1
)
def
graph_fn
():
boxes
=
[
tf
.
constant
([[
0.
,
0.
,
1.
,
1.
]])
]
mask
=
np
.
zeros
((
1
,
4
,
4
),
dtype
=
np
.
float32
)
mask
[
0
,
1
:
3
,
1
:
3
]
=
1.0
masks
=
[
tf
.
constant
(
mask
)]
return
assigner
.
assign_corner_offset_targets
(
boxes
,
masks
)
corner_offsets
,
foreground
=
self
.
execute
(
graph_fn
,
[])
self
.
assertAllClose
(
foreground
[
0
],
[[
0
,
0
,
0
,
0
],
[
0
,
1
,
1
,
0
],
[
0
,
1
,
1
,
0
],
[
0
,
0
,
0
,
0
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
0
],
[[
0
,
0
,
0
,
0
],
[
0
,
-
1
,
-
1
,
0
],
[
0
,
-
2
,
-
2
,
0
],
[
0
,
0
,
0
,
0
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
1
],
[[
0
,
0
,
0
,
0
],
[
0
,
-
1
,
-
2
,
0
],
[
0
,
-
1
,
-
2
,
0
],
[
0
,
0
,
0
,
0
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
2
],
[[
0
,
0
,
0
,
0
],
[
0
,
3
,
3
,
0
],
[
0
,
2
,
2
,
0
],
[
0
,
0
,
0
,
0
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
3
],
[[
0
,
0
,
0
,
0
],
[
0
,
3
,
2
,
0
],
[
0
,
3
,
2
,
0
],
[
0
,
0
,
0
,
0
]])
def
test_assign_corner_offset_multiple_objects
(
self
):
"""Test corner offsets are correct with multiple objects."""
assigner
=
targetassigner
.
CenterNetCornerOffsetTargetAssigner
(
stride
=
1
)
def
graph_fn
():
boxes
=
[
tf
.
constant
([[
0.
,
0.
,
1.
,
1.
],
[
0.
,
0.
,
0.
,
0.
]]),
tf
.
constant
([[
0.
,
0.
,
.
25
,
.
25
],
[.
25
,
.
25
,
1.
,
1.
]])
]
mask1
=
np
.
zeros
((
2
,
4
,
4
),
dtype
=
np
.
float32
)
mask1
[
0
,
0
,
0
]
=
1.0
mask1
[
0
,
3
,
3
]
=
1.0
mask2
=
np
.
zeros
((
2
,
4
,
4
),
dtype
=
np
.
float32
)
mask2
[
0
,
:
2
,
:
2
]
=
1.0
mask2
[
1
,
1
:,
1
:]
=
1.0
masks
=
[
tf
.
constant
(
mask1
),
tf
.
constant
(
mask2
)]
return
assigner
.
assign_corner_offset_targets
(
boxes
,
masks
)
corner_offsets
,
foreground
=
self
.
execute
(
graph_fn
,
[])
self
.
assertEqual
(
corner_offsets
.
shape
,
(
2
,
4
,
4
,
4
))
self
.
assertEqual
(
foreground
.
shape
,
(
2
,
4
,
4
))
self
.
assertAllClose
(
foreground
[
0
],
[[
1
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
1
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
0
],
[[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
-
3
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
1
],
[[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
-
3
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
2
],
[[
4
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
1
]])
self
.
assertAllClose
(
corner_offsets
[
0
,
:,
:,
3
],
[[
4
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
0
],
[
0
,
0
,
0
,
1
]])
self
.
assertAllClose
(
foreground
[
1
],
[[
1
,
1
,
0
,
0
],
[
1
,
1
,
1
,
1
],
[
0
,
1
,
1
,
1
],
[
0
,
1
,
1
,
1
]])
self
.
assertAllClose
(
corner_offsets
[
1
,
:,
:,
0
],
[[
0
,
0
,
0
,
0
],
[
-
1
,
-
1
,
0
,
0
],
[
0
,
-
1
,
-
1
,
-
1
],
[
0
,
-
2
,
-
2
,
-
2
]])
self
.
assertAllClose
(
corner_offsets
[
1
,
:,
:,
1
],
[[
0
,
-
1
,
0
,
0
],
[
0
,
-
1
,
-
1
,
-
2
],
[
0
,
0
,
-
1
,
-
2
],
[
0
,
0
,
-
1
,
-
2
]])
self
.
assertAllClose
(
corner_offsets
[
1
,
:,
:,
2
],
[[
1
,
1
,
0
,
0
],
[
0
,
0
,
3
,
3
],
[
0
,
2
,
2
,
2
],
[
0
,
1
,
1
,
1
]])
self
.
assertAllClose
(
corner_offsets
[
1
,
:,
:,
3
],
[[
1
,
0
,
0
,
0
],
[
1
,
0
,
2
,
1
],
[
0
,
3
,
2
,
1
],
[
0
,
3
,
2
,
1
]])
def
test_assign_corner_offsets_no_objects
(
self
):
"""Test assignment works with empty input on cpu."""
assigner
=
targetassigner
.
CenterNetCornerOffsetTargetAssigner
(
stride
=
1
)
def
graph_fn
():
boxes
=
[
tf
.
zeros
((
0
,
4
),
dtype
=
tf
.
float32
)
]
masks
=
[
tf
.
zeros
((
0
,
5
,
5
),
dtype
=
tf
.
float32
)]
return
assigner
.
assign_corner_offset_targets
(
boxes
,
masks
)
corner_offsets
,
foreground
=
self
.
execute_cpu
(
graph_fn
,
[])
self
.
assertAllClose
(
corner_offsets
,
np
.
zeros
((
1
,
5
,
5
,
4
)))
self
.
assertAllClose
(
foreground
,
np
.
zeros
((
1
,
5
,
5
)))
if
__name__
==
'__main__'
:
tf
.
enable_v2_behavior
()
tf
.
test
.
main
()
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