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
SOLOv2-pytorch
Commits
7eb02d29
Commit
7eb02d29
authored
Oct 08, 2018
by
Kai Chen
Browse files
Merge branch 'dev' into single-stage
parents
20e75c22
01a03aab
Changes
44
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
49 additions
and
643 deletions
+49
-643
mmdet/core/mask/mask_target.py
mmdet/core/mask/mask_target.py
+0
-0
mmdet/core/mask/utils.py
mmdet/core/mask/utils.py
+0
-0
mmdet/core/mask_ops/__init__.py
mmdet/core/mask_ops/__init__.py
+0
-11
mmdet/core/mask_ops/segms.py
mmdet/core/mask_ops/segms.py
+0
-272
mmdet/core/parallel/__init__.py
mmdet/core/parallel/__init__.py
+0
-7
mmdet/core/parallel/_functions.py
mmdet/core/parallel/_functions.py
+0
-74
mmdet/core/parallel/data_parallel.py
mmdet/core/parallel/data_parallel.py
+0
-9
mmdet/core/parallel/distributed.py
mmdet/core/parallel/distributed.py
+0
-46
mmdet/core/parallel/scatter_gather.py
mmdet/core/parallel/scatter_gather.py
+0
-54
mmdet/core/post_processing/merge_augs.py
mmdet/core/post_processing/merge_augs.py
+1
-1
mmdet/core/utils/__init__.py
mmdet/core/utils/__init__.py
+3
-4
mmdet/core/utils/dist_utils.py
mmdet/core/utils/dist_utils.py
+32
-39
mmdet/datasets/__init__.py
mmdet/datasets/__init__.py
+4
-6
mmdet/datasets/coco.py
mmdet/datasets/coco.py
+1
-1
mmdet/datasets/loader/__init__.py
mmdet/datasets/loader/__init__.py
+1
-2
mmdet/datasets/loader/build_loader.py
mmdet/datasets/loader/build_loader.py
+6
-1
mmdet/datasets/loader/collate.py
mmdet/datasets/loader/collate.py
+0
-70
mmdet/datasets/transforms.py
mmdet/datasets/transforms.py
+1
-25
mmdet/datasets/utils.py
mmdet/datasets/utils.py
+0
-17
mmdet/datasets/utils/__init__.py
mmdet/datasets/utils/__init__.py
+0
-4
No files found.
mmdet/core/mask
_ops
/mask_target.py
→
mmdet/core/mask/mask_target.py
View file @
7eb02d29
File moved
mmdet/core/mask
_ops
/utils.py
→
mmdet/core/mask/utils.py
View file @
7eb02d29
File moved
mmdet/core/mask_ops/__init__.py
deleted
100644 → 0
View file @
20e75c22
from
.segms
import
(
flip_segms
,
polys_to_mask
,
mask_to_bbox
,
polys_to_mask_wrt_box
,
polys_to_boxes
,
rle_mask_voting
,
rle_mask_nms
,
rle_masks_to_boxes
)
from
.utils
import
split_combined_polys
from
.mask_target
import
mask_target
__all__
=
[
'flip_segms'
,
'polys_to_mask'
,
'mask_to_bbox'
,
'polys_to_mask_wrt_box'
,
'polys_to_boxes'
,
'rle_mask_voting'
,
'rle_mask_nms'
,
'rle_masks_to_boxes'
,
'split_combined_polys'
,
'mask_target'
]
mmdet/core/mask_ops/segms.py
deleted
100644 → 0
View file @
20e75c22
# flake8: noqa
# This file is copied from Detectron.
# Copyright (c) 2017-present, Facebook, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##############################################################################
"""Functions for interacting with segmentation masks in the COCO format.
The following terms are used in this module
mask: a binary mask encoded as a 2D numpy array
segm: a segmentation mask in one of the two COCO formats (polygon or RLE)
polygon: COCO's polygon format
RLE: COCO's run length encoding format
"""
from
__future__
import
absolute_import
from
__future__
import
division
from
__future__
import
print_function
from
__future__
import
unicode_literals
import
numpy
as
np
import
pycocotools.mask
as
mask_util
def
flip_segms
(
segms
,
height
,
width
):
"""Left/right flip each mask in a list of masks."""
def
_flip_poly
(
poly
,
width
):
flipped_poly
=
np
.
array
(
poly
)
flipped_poly
[
0
::
2
]
=
width
-
np
.
array
(
poly
[
0
::
2
])
-
1
return
flipped_poly
.
tolist
()
def
_flip_rle
(
rle
,
height
,
width
):
if
'counts'
in
rle
and
type
(
rle
[
'counts'
])
==
list
:
# Magic RLE format handling painfully discovered by looking at the
# COCO API showAnns function.
rle
=
mask_util
.
frPyObjects
([
rle
],
height
,
width
)
mask
=
mask_util
.
decode
(
rle
)
mask
=
mask
[:,
::
-
1
,
:]
rle
=
mask_util
.
encode
(
np
.
array
(
mask
,
order
=
'F'
,
dtype
=
np
.
uint8
))
return
rle
flipped_segms
=
[]
for
segm
in
segms
:
if
type
(
segm
)
==
list
:
# Polygon format
flipped_segms
.
append
([
_flip_poly
(
poly
,
width
)
for
poly
in
segm
])
else
:
# RLE format
assert
type
(
segm
)
==
dict
flipped_segms
.
append
(
_flip_rle
(
segm
,
height
,
width
))
return
flipped_segms
def
polys_to_mask
(
polygons
,
height
,
width
):
"""Convert from the COCO polygon segmentation format to a binary mask
encoded as a 2D array of data type numpy.float32. The polygon segmentation
is understood to be enclosed inside a height x width image. The resulting
mask is therefore of shape (height, width).
"""
rle
=
mask_util
.
frPyObjects
(
polygons
,
height
,
width
)
mask
=
np
.
array
(
mask_util
.
decode
(
rle
),
dtype
=
np
.
float32
)
# Flatten in case polygons was a list
mask
=
np
.
sum
(
mask
,
axis
=
2
)
mask
=
np
.
array
(
mask
>
0
,
dtype
=
np
.
float32
)
return
mask
def
mask_to_bbox
(
mask
):
"""Compute the tight bounding box of a binary mask."""
xs
=
np
.
where
(
np
.
sum
(
mask
,
axis
=
0
)
>
0
)[
0
]
ys
=
np
.
where
(
np
.
sum
(
mask
,
axis
=
1
)
>
0
)[
0
]
if
len
(
xs
)
==
0
or
len
(
ys
)
==
0
:
return
None
x0
=
xs
[
0
]
x1
=
xs
[
-
1
]
y0
=
ys
[
0
]
y1
=
ys
[
-
1
]
return
np
.
array
((
x0
,
y0
,
x1
,
y1
),
dtype
=
np
.
float32
)
def
polys_to_mask_wrt_box
(
polygons
,
box
,
M
):
"""Convert from the COCO polygon segmentation format to a binary mask
encoded as a 2D array of data type numpy.float32. The polygon segmentation
is understood to be enclosed in the given box and rasterized to an M x M
mask. The resulting mask is therefore of shape (M, M).
"""
w
=
box
[
2
]
-
box
[
0
]
h
=
box
[
3
]
-
box
[
1
]
w
=
np
.
maximum
(
w
,
1
)
h
=
np
.
maximum
(
h
,
1
)
polygons_norm
=
[]
for
poly
in
polygons
:
p
=
np
.
array
(
poly
,
dtype
=
np
.
float32
)
p
[
0
::
2
]
=
(
p
[
0
::
2
]
-
box
[
0
])
*
M
/
w
p
[
1
::
2
]
=
(
p
[
1
::
2
]
-
box
[
1
])
*
M
/
h
polygons_norm
.
append
(
p
)
rle
=
mask_util
.
frPyObjects
(
polygons_norm
,
M
,
M
)
mask
=
np
.
array
(
mask_util
.
decode
(
rle
),
dtype
=
np
.
float32
)
# Flatten in case polygons was a list
mask
=
np
.
sum
(
mask
,
axis
=
2
)
mask
=
np
.
array
(
mask
>
0
,
dtype
=
np
.
float32
)
return
mask
def
polys_to_boxes
(
polys
):
"""Convert a list of polygons into an array of tight bounding boxes."""
boxes_from_polys
=
np
.
zeros
((
len
(
polys
),
4
),
dtype
=
np
.
float32
)
for
i
in
range
(
len
(
polys
)):
poly
=
polys
[
i
]
x0
=
min
(
min
(
p
[::
2
])
for
p
in
poly
)
x1
=
max
(
max
(
p
[::
2
])
for
p
in
poly
)
y0
=
min
(
min
(
p
[
1
::
2
])
for
p
in
poly
)
y1
=
max
(
max
(
p
[
1
::
2
])
for
p
in
poly
)
boxes_from_polys
[
i
,
:]
=
[
x0
,
y0
,
x1
,
y1
]
return
boxes_from_polys
def
rle_mask_voting
(
top_masks
,
all_masks
,
all_dets
,
iou_thresh
,
binarize_thresh
,
method
=
'AVG'
):
"""Returns new masks (in correspondence with `top_masks`) by combining
multiple overlapping masks coming from the pool of `all_masks`. Two methods
for combining masks are supported: 'AVG' uses a weighted average of
overlapping mask pixels; 'UNION' takes the union of all mask pixels.
"""
if
len
(
top_masks
)
==
0
:
return
all_not_crowd
=
[
False
]
*
len
(
all_masks
)
top_to_all_overlaps
=
mask_util
.
iou
(
top_masks
,
all_masks
,
all_not_crowd
)
decoded_all_masks
=
[
np
.
array
(
mask_util
.
decode
(
rle
),
dtype
=
np
.
float32
)
for
rle
in
all_masks
]
decoded_top_masks
=
[
np
.
array
(
mask_util
.
decode
(
rle
),
dtype
=
np
.
float32
)
for
rle
in
top_masks
]
all_boxes
=
all_dets
[:,
:
4
].
astype
(
np
.
int32
)
all_scores
=
all_dets
[:,
4
]
# Fill box support with weights
mask_shape
=
decoded_all_masks
[
0
].
shape
mask_weights
=
np
.
zeros
((
len
(
all_masks
),
mask_shape
[
0
],
mask_shape
[
1
]))
for
k
in
range
(
len
(
all_masks
)):
ref_box
=
all_boxes
[
k
]
x_0
=
max
(
ref_box
[
0
],
0
)
x_1
=
min
(
ref_box
[
2
]
+
1
,
mask_shape
[
1
])
y_0
=
max
(
ref_box
[
1
],
0
)
y_1
=
min
(
ref_box
[
3
]
+
1
,
mask_shape
[
0
])
mask_weights
[
k
,
y_0
:
y_1
,
x_0
:
x_1
]
=
all_scores
[
k
]
mask_weights
=
np
.
maximum
(
mask_weights
,
1e-5
)
top_segms_out
=
[]
for
k
in
range
(
len
(
top_masks
)):
# Corner case of empty mask
if
decoded_top_masks
[
k
].
sum
()
==
0
:
top_segms_out
.
append
(
top_masks
[
k
])
continue
inds_to_vote
=
np
.
where
(
top_to_all_overlaps
[
k
]
>=
iou_thresh
)[
0
]
# Only matches itself
if
len
(
inds_to_vote
)
==
1
:
top_segms_out
.
append
(
top_masks
[
k
])
continue
masks_to_vote
=
[
decoded_all_masks
[
i
]
for
i
in
inds_to_vote
]
if
method
==
'AVG'
:
ws
=
mask_weights
[
inds_to_vote
]
soft_mask
=
np
.
average
(
masks_to_vote
,
axis
=
0
,
weights
=
ws
)
mask
=
np
.
array
(
soft_mask
>
binarize_thresh
,
dtype
=
np
.
uint8
)
elif
method
==
'UNION'
:
# Any pixel that's on joins the mask
soft_mask
=
np
.
sum
(
masks_to_vote
,
axis
=
0
)
mask
=
np
.
array
(
soft_mask
>
1e-5
,
dtype
=
np
.
uint8
)
else
:
raise
NotImplementedError
(
'Method {} is unknown'
.
format
(
method
))
rle
=
mask_util
.
encode
(
np
.
array
(
mask
[:,
:,
np
.
newaxis
],
order
=
'F'
))[
0
]
top_segms_out
.
append
(
rle
)
return
top_segms_out
def
rle_mask_nms
(
masks
,
dets
,
thresh
,
mode
=
'IOU'
):
"""Performs greedy non-maximum suppression based on an overlap measurement
between masks. The type of measurement is determined by `mode` and can be
either 'IOU' (standard intersection over union) or 'IOMA' (intersection over
mininum area).
"""
if
len
(
masks
)
==
0
:
return
[]
if
len
(
masks
)
==
1
:
return
[
0
]
if
mode
==
'IOU'
:
# Computes ious[m1, m2] = area(intersect(m1, m2)) / area(union(m1, m2))
all_not_crowds
=
[
False
]
*
len
(
masks
)
ious
=
mask_util
.
iou
(
masks
,
masks
,
all_not_crowds
)
elif
mode
==
'IOMA'
:
# Computes ious[m1, m2] = area(intersect(m1, m2)) / min(area(m1), area(m2))
all_crowds
=
[
True
]
*
len
(
masks
)
# ious[m1, m2] = area(intersect(m1, m2)) / area(m2)
ious
=
mask_util
.
iou
(
masks
,
masks
,
all_crowds
)
# ... = max(area(intersect(m1, m2)) / area(m2),
# area(intersect(m2, m1)) / area(m1))
ious
=
np
.
maximum
(
ious
,
ious
.
transpose
())
elif
mode
==
'CONTAINMENT'
:
# Computes ious[m1, m2] = area(intersect(m1, m2)) / area(m2)
# Which measures how much m2 is contained inside m1
all_crowds
=
[
True
]
*
len
(
masks
)
ious
=
mask_util
.
iou
(
masks
,
masks
,
all_crowds
)
else
:
raise
NotImplementedError
(
'Mode {} is unknown'
.
format
(
mode
))
scores
=
dets
[:,
4
]
order
=
np
.
argsort
(
-
scores
)
keep
=
[]
while
order
.
size
>
0
:
i
=
order
[
0
]
keep
.
append
(
i
)
ovr
=
ious
[
i
,
order
[
1
:]]
inds_to_keep
=
np
.
where
(
ovr
<=
thresh
)[
0
]
order
=
order
[
inds_to_keep
+
1
]
return
keep
def
rle_masks_to_boxes
(
masks
):
"""Computes the bounding box of each mask in a list of RLE encoded masks."""
if
len
(
masks
)
==
0
:
return
[]
decoded_masks
=
[
np
.
array
(
mask_util
.
decode
(
rle
),
dtype
=
np
.
float32
)
for
rle
in
masks
]
def
get_bounds
(
flat_mask
):
inds
=
np
.
where
(
flat_mask
>
0
)[
0
]
return
inds
.
min
(),
inds
.
max
()
boxes
=
np
.
zeros
((
len
(
decoded_masks
),
4
))
keep
=
[
True
]
*
len
(
decoded_masks
)
for
i
,
mask
in
enumerate
(
decoded_masks
):
if
mask
.
sum
()
==
0
:
keep
[
i
]
=
False
continue
flat_mask
=
mask
.
sum
(
axis
=
0
)
x0
,
x1
=
get_bounds
(
flat_mask
)
flat_mask
=
mask
.
sum
(
axis
=
1
)
y0
,
y1
=
get_bounds
(
flat_mask
)
boxes
[
i
,
:]
=
(
x0
,
y0
,
x1
,
y1
)
return
boxes
,
np
.
where
(
keep
)[
0
]
mmdet/core/parallel/__init__.py
deleted
100644 → 0
View file @
20e75c22
from
.data_parallel
import
MMDataParallel
from
.distributed
import
MMDistributedDataParallel
from
.scatter_gather
import
scatter
,
scatter_kwargs
__all__
=
[
'MMDataParallel'
,
'MMDistributedDataParallel'
,
'scatter'
,
'scatter_kwargs'
]
mmdet/core/parallel/_functions.py
deleted
100644 → 0
View file @
20e75c22
import
torch
from
torch.nn.parallel._functions
import
_get_stream
def
scatter
(
input
,
devices
,
streams
=
None
):
"""Scatters tensor across multiple GPUs.
"""
if
streams
is
None
:
streams
=
[
None
]
*
len
(
devices
)
if
isinstance
(
input
,
list
):
chunk_size
=
(
len
(
input
)
-
1
)
//
len
(
devices
)
+
1
outputs
=
[
scatter
(
input
[
i
],
[
devices
[
i
//
chunk_size
]],
[
streams
[
i
//
chunk_size
]])
for
i
in
range
(
len
(
input
))
]
return
outputs
elif
isinstance
(
input
,
torch
.
Tensor
):
output
=
input
.
contiguous
()
# TODO: copy to a pinned buffer first (if copying from CPU)
stream
=
streams
[
0
]
if
output
.
numel
()
>
0
else
None
with
torch
.
cuda
.
device
(
devices
[
0
]),
torch
.
cuda
.
stream
(
stream
):
output
=
output
.
cuda
(
devices
[
0
],
non_blocking
=
True
)
return
output
else
:
raise
Exception
(
'Unknown type {}.'
.
format
(
type
(
input
)))
def
synchronize_stream
(
output
,
devices
,
streams
):
if
isinstance
(
output
,
list
):
chunk_size
=
len
(
output
)
//
len
(
devices
)
for
i
in
range
(
len
(
devices
)):
for
j
in
range
(
chunk_size
):
synchronize_stream
(
output
[
i
*
chunk_size
+
j
],
[
devices
[
i
]],
[
streams
[
i
]])
elif
isinstance
(
output
,
torch
.
Tensor
):
if
output
.
numel
()
!=
0
:
with
torch
.
cuda
.
device
(
devices
[
0
]):
main_stream
=
torch
.
cuda
.
current_stream
()
main_stream
.
wait_stream
(
streams
[
0
])
output
.
record_stream
(
main_stream
)
else
:
raise
Exception
(
'Unknown type {}.'
.
format
(
type
(
output
)))
def
get_input_device
(
input
):
if
isinstance
(
input
,
list
):
for
item
in
input
:
input_device
=
get_input_device
(
item
)
if
input_device
!=
-
1
:
return
input_device
return
-
1
elif
isinstance
(
input
,
torch
.
Tensor
):
return
input
.
get_device
()
if
input
.
is_cuda
else
-
1
else
:
raise
Exception
(
'Unknown type {}.'
.
format
(
type
(
input
)))
class
Scatter
(
object
):
@
staticmethod
def
forward
(
target_gpus
,
input
):
input_device
=
get_input_device
(
input
)
streams
=
None
if
input_device
==
-
1
:
# Perform CPU to GPU copies in a background stream
streams
=
[
_get_stream
(
device
)
for
device
in
target_gpus
]
outputs
=
scatter
(
input
,
target_gpus
,
streams
)
# Synchronize with the copy stream
if
streams
is
not
None
:
synchronize_stream
(
outputs
,
target_gpus
,
streams
)
return
tuple
(
outputs
)
mmdet/core/parallel/data_parallel.py
deleted
100644 → 0
View file @
20e75c22
from
torch.nn.parallel
import
DataParallel
from
.scatter_gather
import
scatter_kwargs
class
MMDataParallel
(
DataParallel
):
def
scatter
(
self
,
inputs
,
kwargs
,
device_ids
):
return
scatter_kwargs
(
inputs
,
kwargs
,
device_ids
,
dim
=
self
.
dim
)
mmdet/core/parallel/distributed.py
deleted
100644 → 0
View file @
20e75c22
import
torch
import
torch.distributed
as
dist
import
torch.nn
as
nn
from
torch._utils
import
(
_flatten_dense_tensors
,
_unflatten_dense_tensors
,
_take_tensors
)
from
.scatter_gather
import
scatter_kwargs
class
MMDistributedDataParallel
(
nn
.
Module
):
def
__init__
(
self
,
module
,
dim
=
0
,
broadcast_buffers
=
True
):
super
(
MMDistributedDataParallel
,
self
).
__init__
()
self
.
module
=
module
self
.
dim
=
dim
self
.
broadcast_buffers
=
broadcast_buffers
self
.
broadcast_bucket_size
=
32
*
1024
*
1024
self
.
_sync_params
()
def
_dist_broadcast_coalesced
(
self
,
tensors
,
buffer_size
):
for
tensors
in
_take_tensors
(
tensors
,
buffer_size
):
flat_tensors
=
_flatten_dense_tensors
(
tensors
)
dist
.
broadcast
(
flat_tensors
,
0
)
for
tensor
,
synced
in
zip
(
tensors
,
_unflatten_dense_tensors
(
flat_tensors
,
tensors
)):
tensor
.
copy_
(
synced
)
def
_sync_params
(
self
):
module_states
=
list
(
self
.
module
.
state_dict
().
values
())
if
len
(
module_states
)
>
0
:
self
.
_dist_broadcast_coalesced
(
module_states
,
self
.
broadcast_bucket_size
)
if
self
.
broadcast_buffers
:
buffers
=
[
b
.
data
for
b
in
self
.
module
.
_all_buffers
()]
if
len
(
buffers
)
>
0
:
self
.
_dist_broadcast_coalesced
(
buffers
,
self
.
broadcast_bucket_size
)
def
scatter
(
self
,
inputs
,
kwargs
,
device_ids
):
return
scatter_kwargs
(
inputs
,
kwargs
,
device_ids
,
dim
=
self
.
dim
)
def
forward
(
self
,
*
inputs
,
**
kwargs
):
inputs
,
kwargs
=
self
.
scatter
(
inputs
,
kwargs
,
[
torch
.
cuda
.
current_device
()])
return
self
.
module
(
*
inputs
[
0
],
**
kwargs
[
0
])
mmdet/core/parallel/scatter_gather.py
deleted
100644 → 0
View file @
20e75c22
import
torch
from
torch.nn.parallel._functions
import
Scatter
as
OrigScatter
from
._functions
import
Scatter
from
mmdet.datasets.utils
import
DataContainer
def
scatter
(
inputs
,
target_gpus
,
dim
=
0
):
"""Scatter inputs to target gpus.
The only difference from original :func:`scatter` is to add support for
:type:`~mmdet.DataContainer`.
"""
def
scatter_map
(
obj
):
if
isinstance
(
obj
,
torch
.
Tensor
):
return
OrigScatter
.
apply
(
target_gpus
,
None
,
dim
,
obj
)
if
isinstance
(
obj
,
DataContainer
):
if
obj
.
cpu_only
:
return
obj
.
data
else
:
return
Scatter
.
forward
(
target_gpus
,
obj
.
data
)
if
isinstance
(
obj
,
tuple
)
and
len
(
obj
)
>
0
:
return
list
(
zip
(
*
map
(
scatter_map
,
obj
)))
if
isinstance
(
obj
,
list
)
and
len
(
obj
)
>
0
:
out
=
list
(
map
(
list
,
zip
(
*
map
(
scatter_map
,
obj
))))
return
out
if
isinstance
(
obj
,
dict
)
and
len
(
obj
)
>
0
:
out
=
list
(
map
(
type
(
obj
),
zip
(
*
map
(
scatter_map
,
obj
.
items
()))))
return
out
return
[
obj
for
targets
in
target_gpus
]
# After scatter_map is called, a scatter_map cell will exist. This cell
# has a reference to the actual function scatter_map, which has references
# to a closure that has a reference to the scatter_map cell (because the
# fn is recursive). To avoid this reference cycle, we set the function to
# None, clearing the cell
try
:
return
scatter_map
(
inputs
)
finally
:
scatter_map
=
None
def
scatter_kwargs
(
inputs
,
kwargs
,
target_gpus
,
dim
=
0
):
"""Scatter with support for kwargs dictionary"""
inputs
=
scatter
(
inputs
,
target_gpus
,
dim
)
if
inputs
else
[]
kwargs
=
scatter
(
kwargs
,
target_gpus
,
dim
)
if
kwargs
else
[]
if
len
(
inputs
)
<
len
(
kwargs
):
inputs
.
extend
([()
for
_
in
range
(
len
(
kwargs
)
-
len
(
inputs
))])
elif
len
(
kwargs
)
<
len
(
inputs
):
kwargs
.
extend
([{}
for
_
in
range
(
len
(
inputs
)
-
len
(
kwargs
))])
inputs
=
tuple
(
inputs
)
kwargs
=
tuple
(
kwargs
)
return
inputs
,
kwargs
mmdet/core/post_processing/merge_augs.py
View file @
7eb02d29
...
...
@@ -3,7 +3,7 @@ import torch
import
numpy
as
np
from
mmdet.ops
import
nms
from
..bbox
_ops
import
bbox_mapping_back
from
..bbox
import
bbox_mapping_back
def
merge_aug_proposals
(
aug_proposals
,
img_metas
,
rpn_test_cfg
):
...
...
mmdet/core/utils/__init__.py
View file @
7eb02d29
from
.dist_utils
import
(
init_dist
,
reduce_grads
,
DistOptimizerHook
,
DistSamplerSeedHook
)
from
.dist_utils
import
init_dist
,
allreduce_grads
,
DistOptimizerHook
from
.misc
import
tensor2imgs
,
unmap
,
multi_apply
__all__
=
[
'init_dist'
,
'reduce_grads'
,
'DistOptimizerHook'
,
'
DistSamplerSeedHook
'
,
'tensor2imgs'
,
'unmap'
,
'multi_apply'
'init_dist'
,
'
all
reduce_grads'
,
'DistOptimizerHook'
,
'
tensor2imgs
'
,
'unmap'
,
'multi_apply'
]
mmdet/core/utils/dist_utils.py
View file @
7eb02d29
...
...
@@ -4,9 +4,9 @@ from collections import OrderedDict
import
torch
import
torch.multiprocessing
as
mp
import
torch.distributed
as
dist
from
torch._utils
import
_flatten_dense_tensors
,
_unflatten_dense_tensors
from
torch.nn.utils
import
clip_grad
from
mmcv.runner
import
Hook
,
OptimizerHook
from
torch._utils
import
(
_flatten_dense_tensors
,
_unflatten_dense_tensors
,
_take_tensors
)
from
mmcv.runner
import
OptimizerHook
def
init_dist
(
launcher
,
backend
=
'nccl'
,
**
kwargs
):
...
...
@@ -38,59 +38,52 @@ def _init_dist_slurm(backend, **kwargs):
raise
NotImplementedError
# modified from
# https://github.com/NVIDIA/apex/blob/master/apex/parallel/distributed.py#L9
def
all_reduce_coalesced
(
tensors
):
buckets
=
OrderedDict
(
)
for
tensor
in
tensors
:
tp
=
tensor
.
type
()
if
tp
not
in
bucket
s
:
buckets
[
tp
]
=
[]
buckets
[
tp
].
append
(
tensor
)
world_size
=
dist
.
get_world_size
(
)
for
tp
in
buckets
:
bucket
=
buckets
[
tp
]
coalesced
=
_flatten_dense_tensors
(
bucket
)
dist
.
all_reduce
(
coalesced
)
coalesced
.
div_
(
world_size
)
for
buf
,
synced
in
zip
(
bucket
,
_unflatten_dense_tensors
(
coalesced
,
bucket
)):
buf
.
copy_
(
synced
)
def
reduce_grads
(
model
,
coalesce
=
True
):
def
_allreduce_coalesced
(
tensors
,
world_size
,
bucket_size_mb
=-
1
):
if
bucket_size_mb
>
0
:
bucket_size_bytes
=
bucket_size_mb
*
1024
*
1024
buckets
=
_take_tensors
(
tensors
,
bucket_size_bytes
)
else
:
buckets
=
OrderedDict
()
for
tensor
in
tensor
s
:
tp
=
tensor
.
type
()
if
tp
not
in
buckets
:
buckets
[
tp
]
=
[]
buckets
[
tp
].
append
(
tensor
)
buckets
=
buckets
.
values
()
for
bucket
in
bucket
s
:
flat_tensors
=
_flatten_dense_tensors
(
bucket
)
dist
.
all_reduce
(
flat_tensors
)
flat_tensors
.
div_
(
world_size
)
for
tensor
,
synced
in
zip
(
bucket
,
_unflatten_dense_tensors
(
flat_tensors
,
bucket
)):
tensor
.
copy_
(
synced
)
def
all
reduce_grads
(
model
,
coalesce
=
True
,
bucket_size_mb
=-
1
):
grads
=
[
param
.
grad
.
data
for
param
in
model
.
parameters
()
if
param
.
requires_grad
and
param
.
grad
is
not
None
]
world_size
=
dist
.
get_world_size
()
if
coalesce
:
all
_
reduce_coalesced
(
grads
)
_
allreduce_coalesced
(
grads
,
world_size
,
bucket_size_mb
)
else
:
world_size
=
dist
.
get_world_size
()
for
tensor
in
grads
:
dist
.
all_reduce
(
tensor
.
div_
(
world_size
))
class
DistOptimizerHook
(
OptimizerHook
):
def
__init__
(
self
,
grad_clip
=
None
,
coalesce
=
True
):
def
__init__
(
self
,
grad_clip
=
None
,
coalesce
=
True
,
bucket_size_mb
=-
1
):
self
.
grad_clip
=
grad_clip
self
.
coalesce
=
coalesce
self
.
bucket_size_mb
=
bucket_size_mb
def
after_train_iter
(
self
,
runner
):
runner
.
optimizer
.
zero_grad
()
runner
.
outputs
[
'loss'
].
backward
()
reduce_grads
(
runner
.
model
,
self
.
coalesce
)
all
reduce_grads
(
runner
.
model
,
self
.
coalesce
,
self
.
bucket_size_mb
)
if
self
.
grad_clip
is
not
None
:
clip_grad
.
clip_grad_norm_
(
filter
(
lambda
p
:
p
.
requires_grad
,
runner
.
model
.
parameters
()),
**
self
.
grad_clip
)
self
.
clip_grads
(
runner
.
model
.
parameters
())
runner
.
optimizer
.
step
()
class
DistSamplerSeedHook
(
Hook
):
def
before_epoch
(
self
,
runner
):
runner
.
data_loader
.
sampler
.
set_epoch
(
runner
.
epoch
)
mmdet/datasets/__init__.py
View file @
7eb02d29
from
.coco
import
CocoDataset
from
.loader
import
(
collate
,
GroupSampler
,
DistributedGroupSampler
,
build_dataloader
)
from
.utils
import
DataContainer
,
to_tensor
,
random_scale
,
show_ann
from
.loader
import
GroupSampler
,
DistributedGroupSampler
,
build_dataloader
from
.utils
import
to_tensor
,
random_scale
,
show_ann
__all__
=
[
'CocoDataset'
,
'collate'
,
'GroupSampler'
,
'DistributedGroupSampler'
,
'build_dataloader'
,
'DataContainer'
,
'to_tensor'
,
'random_scale'
,
'show_ann'
'CocoDataset'
,
'GroupSampler'
,
'DistributedGroupSampler'
,
'build_dataloader'
,
'to_tensor'
,
'random_scale'
,
'show_ann'
]
mmdet/datasets/coco.py
View file @
7eb02d29
...
...
@@ -2,13 +2,13 @@ import os.path as osp
import
mmcv
import
numpy
as
np
from
mmcv.parallel
import
DataContainer
as
DC
from
pycocotools.coco
import
COCO
from
torch.utils.data
import
Dataset
from
.transforms
import
(
ImageTransform
,
BboxTransform
,
MaskTransform
,
Numpy2Tensor
)
from
.utils
import
to_tensor
,
show_ann
,
random_scale
from
.utils
import
DataContainer
as
DC
class
CocoDataset
(
Dataset
):
...
...
mmdet/datasets/loader/__init__.py
View file @
7eb02d29
from
.build_loader
import
build_dataloader
from
.collate
import
collate
from
.sampler
import
GroupSampler
,
DistributedGroupSampler
__all__
=
[
'collate'
,
'GroupSampler'
,
'DistributedGroupSampler'
,
'build_dataloader'
'GroupSampler'
,
'DistributedGroupSampler'
,
'build_dataloader'
]
mmdet/datasets/loader/build_loader.py
View file @
7eb02d29
from
functools
import
partial
from
mmcv.runner
import
get_dist_info
from
mmcv.parallel
import
collate
from
torch.utils.data
import
DataLoader
from
.collate
import
collate
from
.sampler
import
GroupSampler
,
DistributedGroupSampler
# https://github.com/pytorch/pytorch/issues/973
import
resource
rlimit
=
resource
.
getrlimit
(
resource
.
RLIMIT_NOFILE
)
resource
.
setrlimit
(
resource
.
RLIMIT_NOFILE
,
(
4096
,
rlimit
[
1
]))
def
build_dataloader
(
dataset
,
imgs_per_gpu
,
...
...
mmdet/datasets/loader/collate.py
deleted
100644 → 0
View file @
20e75c22
import
collections
import
torch
import
torch.nn.functional
as
F
from
torch.utils.data.dataloader
import
default_collate
from
..utils
import
DataContainer
# https://github.com/pytorch/pytorch/issues/973
import
resource
rlimit
=
resource
.
getrlimit
(
resource
.
RLIMIT_NOFILE
)
resource
.
setrlimit
(
resource
.
RLIMIT_NOFILE
,
(
4096
,
rlimit
[
1
]))
def
collate
(
batch
,
samples_per_gpu
=
1
):
"""Puts each data field into a tensor/DataContainer with outer dimension
batch size.
Extend default_collate to add support for :type:`~mmdet.DataContainer`.
There are 3 cases for data containers.
1. cpu_only = True, e.g., meta data
2. cpu_only = False, stack = True, e.g., images tensors
3. cpu_only = False, stack = False, e.g., gt bboxes
"""
if
not
isinstance
(
batch
,
collections
.
Sequence
):
raise
TypeError
(
"{} is not supported."
.
format
(
batch
.
dtype
))
if
isinstance
(
batch
[
0
],
DataContainer
):
assert
len
(
batch
)
%
samples_per_gpu
==
0
stacked
=
[]
if
batch
[
0
].
cpu_only
:
for
i
in
range
(
0
,
len
(
batch
),
samples_per_gpu
):
stacked
.
append
(
[
sample
.
data
for
sample
in
batch
[
i
:
i
+
samples_per_gpu
]])
return
DataContainer
(
stacked
,
batch
[
0
].
stack
,
batch
[
0
].
padding_value
,
cpu_only
=
True
)
elif
batch
[
0
].
stack
:
for
i
in
range
(
0
,
len
(
batch
),
samples_per_gpu
):
assert
isinstance
(
batch
[
i
].
data
,
torch
.
Tensor
)
# TODO: handle tensors other than 3d
assert
batch
[
i
].
dim
()
==
3
c
,
h
,
w
=
batch
[
0
].
size
()
for
sample
in
batch
[
i
:
i
+
samples_per_gpu
]:
assert
c
==
sample
.
size
(
0
)
h
=
max
(
h
,
sample
.
size
(
1
))
w
=
max
(
w
,
sample
.
size
(
2
))
padded_samples
=
[
F
.
pad
(
sample
.
data
,
(
0
,
w
-
sample
.
size
(
2
),
0
,
h
-
sample
.
size
(
1
)),
value
=
sample
.
padding_value
)
for
sample
in
batch
[
i
:
i
+
samples_per_gpu
]
]
stacked
.
append
(
default_collate
(
padded_samples
))
else
:
for
i
in
range
(
0
,
len
(
batch
),
samples_per_gpu
):
stacked
.
append
(
[
sample
.
data
for
sample
in
batch
[
i
:
i
+
samples_per_gpu
]])
return
DataContainer
(
stacked
,
batch
[
0
].
stack
,
batch
[
0
].
padding_value
)
elif
isinstance
(
batch
[
0
],
collections
.
Sequence
):
transposed
=
zip
(
*
batch
)
return
[
collate
(
samples
,
samples_per_gpu
)
for
samples
in
transposed
]
elif
isinstance
(
batch
[
0
],
collections
.
Mapping
):
return
{
key
:
collate
([
d
[
key
]
for
d
in
batch
],
samples_per_gpu
)
for
key
in
batch
[
0
]
}
else
:
return
default_collate
(
batch
)
mmdet/datasets/transforms.py
View file @
7eb02d29
...
...
@@ -2,11 +2,7 @@ import mmcv
import
numpy
as
np
import
torch
from
mmdet.core.mask_ops
import
segms
__all__
=
[
'ImageTransform'
,
'BboxTransform'
,
'PolyMaskTransform'
,
'Numpy2Tensor'
]
__all__
=
[
'ImageTransform'
,
'BboxTransform'
,
'MaskTransform'
,
'Numpy2Tensor'
]
class
ImageTransform
(
object
):
...
...
@@ -85,26 +81,6 @@ class BboxTransform(object):
return
padded_bboxes
class
PolyMaskTransform
(
object
):
"""Preprocess polygons."""
def
__init__
(
self
):
pass
def
__call__
(
self
,
gt_mask_polys
,
gt_poly_lens
,
img_h
,
img_w
,
flip
=
False
):
if
flip
:
gt_mask_polys
=
segms
.
flip_segms
(
gt_mask_polys
,
img_h
,
img_w
)
num_polys_per_mask
=
np
.
array
(
[
len
(
mask_polys
)
for
mask_polys
in
gt_mask_polys
],
dtype
=
np
.
int64
)
gt_poly_lens
=
np
.
array
(
gt_poly_lens
,
dtype
=
np
.
int64
)
gt_mask_polys
=
[
np
.
concatenate
(
mask_polys
).
astype
(
np
.
float32
)
for
mask_polys
in
gt_mask_polys
]
gt_mask_polys
=
np
.
concatenate
(
gt_mask_polys
)
return
gt_mask_polys
,
gt_poly_lens
,
num_polys_per_mask
class
MaskTransform
(
object
):
"""Preprocess masks.
...
...
mmdet/datasets/utils
/misc
.py
→
mmdet/datasets/utils.py
View file @
7eb02d29
...
...
@@ -5,7 +5,6 @@ import torch
import
matplotlib.pyplot
as
plt
import
numpy
as
np
import
pycocotools.mask
as
maskUtils
def
to_tensor
(
data
):
...
...
@@ -68,19 +67,3 @@ def show_ann(coco, img, ann_info):
plt
.
axis
(
'off'
)
coco
.
showAnns
(
ann_info
)
plt
.
show
()
def
draw_bbox_and_segm
(
img
,
results
,
dataset
,
score_thr
=
0.5
):
bbox_results
,
segm_results
=
results
hi_bboxes
=
[]
for
cls_bboxes
,
cls_segms
in
zip
(
bbox_results
,
segm_results
):
if
len
(
cls_bboxes
)
==
0
:
hi_bboxes
.
append
(
cls_bboxes
)
continue
inds
=
np
.
where
(
cls_bboxes
[:,
-
1
]
>
score_thr
)[
0
]
hi_bboxes
.
append
(
cls_bboxes
[
inds
,
:])
color_mask
=
np
.
random
.
random
((
1
,
3
))
for
i
in
inds
:
mask
=
maskUtils
.
decode
(
cls_segms
[
i
]).
astype
(
np
.
bool
)
img
[
mask
]
=
img
[
mask
]
*
0.5
+
color_mask
*
0.5
mmcv
.
draw_bboxes_with_label
(
np
.
ascontiguousarray
(
img
),
hi_bboxes
,
dataset
)
mmdet/datasets/utils/__init__.py
deleted
100644 → 0
View file @
20e75c22
from
.data_container
import
DataContainer
from
.misc
import
to_tensor
,
random_scale
,
show_ann
__all__
=
[
'DataContainer'
,
'to_tensor'
,
'random_scale'
,
'show_ann'
]
Prev
1
2
3
Next
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