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
mmdetection3d
Commits
d75836ea
Commit
d75836ea
authored
Dec 22, 2021
by
Tai-Wang
Browse files
Merge branch 'master' into v1.0.0.dev0
parents
022ee2fb
13f002d7
Changes
105
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
82 additions
and
23 deletions
+82
-23
docs/zh_cn/switch_language.md
docs/zh_cn/switch_language.md
+0
-0
docs/zh_cn/tutorials/config.md
docs/zh_cn/tutorials/config.md
+6
-6
docs/zh_cn/tutorials/coord_sys_tutorial.md
docs/zh_cn/tutorials/coord_sys_tutorial.md
+0
-0
docs/zh_cn/tutorials/customize_dataset.md
docs/zh_cn/tutorials/customize_dataset.md
+0
-0
docs/zh_cn/tutorials/customize_models.md
docs/zh_cn/tutorials/customize_models.md
+0
-0
docs/zh_cn/tutorials/customize_runtime.md
docs/zh_cn/tutorials/customize_runtime.md
+0
-0
docs/zh_cn/tutorials/data_pipeline.md
docs/zh_cn/tutorials/data_pipeline.md
+0
-0
docs/zh_cn/tutorials/index.rst
docs/zh_cn/tutorials/index.rst
+0
-0
docs/zh_cn/useful_tools.md
docs/zh_cn/useful_tools.md
+0
-0
mmdet3d/__init__.py
mmdet3d/__init__.py
+1
-1
mmdet3d/apis/inference.py
mmdet3d/apis/inference.py
+1
-0
mmdet3d/apis/test.py
mmdet3d/apis/test.py
+6
-1
mmdet3d/core/visualizer/show_result.py
mmdet3d/core/visualizer/show_result.py
+22
-4
mmdet3d/models/detectors/base.py
mmdet3d/models/detectors/base.py
+20
-2
mmdet3d/models/detectors/single_stage_mono3d.py
mmdet3d/models/detectors/single_stage_mono3d.py
+9
-2
mmdet3d/models/segmentors/base.py
mmdet3d/models/segmentors/base.py
+11
-2
mmdet3d/version.py
mmdet3d/version.py
+1
-1
requirements/docs.txt
requirements/docs.txt
+2
-3
requirements/mminstall.txt
requirements/mminstall.txt
+1
-1
requirements/readthedocs.txt
requirements/readthedocs.txt
+2
-0
No files found.
docs
_
zh
-CN
/switch_language.md
→
docs
/
zh
_cn
/switch_language.md
View file @
d75836ea
File moved
docs
_
zh
-CN
/tutorials/config.md
→
docs
/
zh
_cn
/tutorials/config.md
View file @
d75836ea
...
@@ -215,13 +215,13 @@ train_pipeline = [ # 训练流水线,更多细节请参考 mmdet3d.datasets.p
...
@@ -215,13 +215,13 @@ train_pipeline = [ # 训练流水线,更多细节请参考 mmdet3d.datasets.p
rot_range
=
[
-
0.027777777777777776
,
0.027777777777777776
],
# 旋转角范围
rot_range
=
[
-
0.027777777777777776
,
0.027777777777777776
],
# 旋转角范围
scale_range
=
None
),
# 缩放尺寸范围
scale_range
=
None
),
# 缩放尺寸范围
dict
(
dict
(
type
=
'DefaultFormatBundle3D'
,
# 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formating
type
=
'DefaultFormatBundle3D'
,
# 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.format
t
ing
class_names
=
(
'cabinet'
,
'bed'
,
'chair'
,
'sofa'
,
'table'
,
'door'
,
class_names
=
(
'cabinet'
,
'bed'
,
'chair'
,
'sofa'
,
'table'
,
'door'
,
'window'
,
'bookshelf'
,
'picture'
,
'counter'
,
'desk'
,
'window'
,
'bookshelf'
,
'picture'
,
'counter'
,
'desk'
,
'curtain'
,
'refrigerator'
,
'showercurtrain'
,
'toilet'
,
'curtain'
,
'refrigerator'
,
'showercurtrain'
,
'toilet'
,
'sink'
,
'bathtub'
,
'garbagebin'
)),
'sink'
,
'bathtub'
,
'garbagebin'
)),
dict
(
dict
(
type
=
'Collect3D'
,
# 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.formating
type
=
'Collect3D'
,
# 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.format
t
ing
keys
=
[
keys
=
[
'points'
,
'gt_bboxes_3d'
,
'gt_labels_3d'
,
'pts_semantic_mask'
,
'points'
,
'gt_bboxes_3d'
,
'gt_labels_3d'
,
'pts_semantic_mask'
,
'pts_instance_mask'
'pts_instance_mask'
...
@@ -236,12 +236,12 @@ test_pipeline = [ # 测试流水线,更多细节请参考 mmdet3d.datasets.pi
...
@@ -236,12 +236,12 @@ test_pipeline = [ # 测试流水线,更多细节请参考 mmdet3d.datasets.pi
dict
(
type
=
'PointSample'
,
# 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
dict
(
type
=
'PointSample'
,
# 室内点采样,更多细节请参考 mmdet3d.datasets.pipelines.indoor_sample
num_points
=
40000
),
# 采样的点的数量
num_points
=
40000
),
# 采样的点的数量
dict
(
dict
(
type
=
'DefaultFormatBundle3D'
,
# 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formating
type
=
'DefaultFormatBundle3D'
,
# 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.format
t
ing
class_names
=
(
'cabinet'
,
'bed'
,
'chair'
,
'sofa'
,
'table'
,
'door'
,
class_names
=
(
'cabinet'
,
'bed'
,
'chair'
,
'sofa'
,
'table'
,
'door'
,
'window'
,
'bookshelf'
,
'picture'
,
'counter'
,
'desk'
,
'window'
,
'bookshelf'
,
'picture'
,
'counter'
,
'desk'
,
'curtain'
,
'refrigerator'
,
'showercurtrain'
,
'toilet'
,
'curtain'
,
'refrigerator'
,
'showercurtrain'
,
'toilet'
,
'sink'
,
'bathtub'
,
'garbagebin'
)),
'sink'
,
'bathtub'
,
'garbagebin'
)),
dict
(
type
=
'Collect3D'
,
# 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.formating
dict
(
type
=
'Collect3D'
,
# 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.format
t
ing
keys
=
[
'points'
])
keys
=
[
'points'
])
]
]
eval_pipeline
=
[
# 模型验证或可视化所使用的流水线,更多细节请参考 mmdet3d.datasets.pipelines
eval_pipeline
=
[
# 模型验证或可视化所使用的流水线,更多细节请参考 mmdet3d.datasets.pipelines
...
@@ -251,13 +251,13 @@ eval_pipeline = [ # 模型验证或可视化所使用的流水线,更多细
...
@@ -251,13 +251,13 @@ eval_pipeline = [ # 模型验证或可视化所使用的流水线,更多细
load_dim
=
6
,
# 读取的点的维度
load_dim
=
6
,
# 读取的点的维度
use_dim
=
[
0
,
1
,
2
]),
# 使用所读取点的哪些维度
use_dim
=
[
0
,
1
,
2
]),
# 使用所读取点的哪些维度
dict
(
dict
(
type
=
'DefaultFormatBundle3D'
,
# 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.formating
type
=
'DefaultFormatBundle3D'
,
# 默认格式打包以收集读取的所有数据,更多细节请参考 mmdet3d.datasets.pipelines.format
t
ing
class_names
=
(
'cabinet'
,
'bed'
,
'chair'
,
'sofa'
,
'table'
,
'door'
,
class_names
=
(
'cabinet'
,
'bed'
,
'chair'
,
'sofa'
,
'table'
,
'door'
,
'window'
,
'bookshelf'
,
'picture'
,
'counter'
,
'desk'
,
'window'
,
'bookshelf'
,
'picture'
,
'counter'
,
'desk'
,
'curtain'
,
'refrigerator'
,
'showercurtrain'
,
'toilet'
,
'curtain'
,
'refrigerator'
,
'showercurtrain'
,
'toilet'
,
'sink'
,
'bathtub'
,
'garbagebin'
)),
'sink'
,
'bathtub'
,
'garbagebin'
)),
with_label
=
False
),
with_label
=
False
),
dict
(
type
=
'Collect3D'
,
# 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.formating
dict
(
type
=
'Collect3D'
,
# 最后一个流程,决定哪些键值对应的数据会被输入给检测器,更多细节请参考 mmdet3d.datasets.pipelines.format
t
ing
keys
=
[
'points'
])
keys
=
[
'points'
])
]
]
data
=
dict
(
data
=
dict
(
...
...
docs
_
zh
-CN
/tutorials/coord_sys_tutorial.md
→
docs
/
zh
_cn
/tutorials/coord_sys_tutorial.md
View file @
d75836ea
File moved
docs
_
zh
-CN
/tutorials/customize_dataset.md
→
docs
/
zh
_cn
/tutorials/customize_dataset.md
View file @
d75836ea
File moved
docs
_
zh
-CN
/tutorials/customize_models.md
→
docs
/
zh
_cn
/tutorials/customize_models.md
View file @
d75836ea
File moved
docs
_
zh
-CN
/tutorials/customize_runtime.md
→
docs
/
zh
_cn
/tutorials/customize_runtime.md
View file @
d75836ea
File moved
docs
_
zh
-CN
/tutorials/data_pipeline.md
→
docs
/
zh
_cn
/tutorials/data_pipeline.md
View file @
d75836ea
File moved
docs
_
zh
-CN
/tutorials/index.rst
→
docs
/
zh
_cn
/tutorials/index.rst
View file @
d75836ea
File moved
docs
_
zh
-CN
/useful_tools.md
→
docs
/
zh
_cn
/useful_tools.md
View file @
d75836ea
File moved
mmdet3d/__init__.py
View file @
d75836ea
...
@@ -19,7 +19,7 @@ def digit_version(version_str):
...
@@ -19,7 +19,7 @@ def digit_version(version_str):
mmcv_minimum_version
=
'1.3.8'
mmcv_minimum_version
=
'1.3.8'
mmcv_maximum_version
=
'1.
4
.0'
mmcv_maximum_version
=
'1.
5
.0'
mmcv_version
=
digit_version
(
mmcv
.
__version__
)
mmcv_version
=
digit_version
(
mmcv
.
__version__
)
...
...
mmdet3d/apis/inference.py
View file @
d75836ea
...
@@ -65,6 +65,7 @@ def init_model(config, checkpoint=None, device='cuda:0'):
...
@@ -65,6 +65,7 @@ def init_model(config, checkpoint=None, device='cuda:0'):
if
'PALETTE'
in
checkpoint
[
'meta'
]:
# 3D Segmentor
if
'PALETTE'
in
checkpoint
[
'meta'
]:
# 3D Segmentor
model
.
PALETTE
=
checkpoint
[
'meta'
][
'PALETTE'
]
model
.
PALETTE
=
checkpoint
[
'meta'
][
'PALETTE'
]
model
.
cfg
=
config
# save the config in the model for convenience
model
.
cfg
=
config
# save the config in the model for convenience
torch
.
cuda
.
set_device
(
device
)
model
.
to
(
device
)
model
.
to
(
device
)
model
.
eval
()
model
.
eval
()
return
model
return
model
...
...
mmdet3d/apis/test.py
View file @
d75836ea
...
@@ -44,7 +44,12 @@ def single_gpu_test(model,
...
@@ -44,7 +44,12 @@ def single_gpu_test(model,
models_3d
=
(
Base3DDetector
,
Base3DSegmentor
,
models_3d
=
(
Base3DDetector
,
Base3DSegmentor
,
SingleStageMono3DDetector
)
SingleStageMono3DDetector
)
if
isinstance
(
model
.
module
,
models_3d
):
if
isinstance
(
model
.
module
,
models_3d
):
model
.
module
.
show_results
(
data
,
result
,
out_dir
=
out_dir
)
model
.
module
.
show_results
(
data
,
result
,
out_dir
=
out_dir
,
show
=
show
,
score_thr
=
show_score_thr
)
# Visualize the results of MMDetection model
# Visualize the results of MMDetection model
# 'show_result' is MMdetection visualization API
# 'show_result' is MMdetection visualization API
else
:
else
:
...
...
mmdet3d/core/visualizer/show_result.py
View file @
d75836ea
...
@@ -78,7 +78,8 @@ def show_result(points,
...
@@ -78,7 +78,8 @@ def show_result(points,
out_dir
,
out_dir
,
filename
,
filename
,
show
=
False
,
show
=
False
,
snapshot
=
False
):
snapshot
=
False
,
pred_labels
=
None
):
"""Convert results into format that is directly readable for meshlab.
"""Convert results into format that is directly readable for meshlab.
Args:
Args:
...
@@ -87,10 +88,11 @@ def show_result(points,
...
@@ -87,10 +88,11 @@ def show_result(points,
pred_bboxes (np.ndarray): Predicted boxes.
pred_bboxes (np.ndarray): Predicted boxes.
out_dir (str): Path of output directory
out_dir (str): Path of output directory
filename (str): Filename of the current frame.
filename (str): Filename of the current frame.
show (bool, optional): Visualize the results online.
show (bool, optional): Visualize the results online. Defaults to False.
Defaults to False.
snapshot (bool, optional): Whether to save the online results.
snapshot (bool, optional): Whether to save the online results.
Defaults to False.
Defaults to False.
pred_labels (np.ndarray, optional): Predicted labels of boxes.
Defaults to None.
"""
"""
result_path
=
osp
.
join
(
out_dir
,
filename
)
result_path
=
osp
.
join
(
out_dir
,
filename
)
mmcv
.
mkdir_or_exist
(
result_path
)
mmcv
.
mkdir_or_exist
(
result_path
)
...
@@ -100,7 +102,23 @@ def show_result(points,
...
@@ -100,7 +102,23 @@ def show_result(points,
vis
=
Visualizer
(
points
)
vis
=
Visualizer
(
points
)
if
pred_bboxes
is
not
None
:
if
pred_bboxes
is
not
None
:
vis
.
add_bboxes
(
bbox3d
=
pred_bboxes
)
if
pred_labels
is
None
:
vis
.
add_bboxes
(
bbox3d
=
pred_bboxes
)
else
:
palette
=
np
.
random
.
randint
(
0
,
255
,
size
=
(
pred_labels
.
max
()
+
1
,
3
))
/
256
labelDict
=
{}
for
j
in
range
(
len
(
pred_labels
)):
i
=
int
(
pred_labels
[
j
].
numpy
())
if
labelDict
.
get
(
i
)
is
None
:
labelDict
[
i
]
=
[]
labelDict
[
i
].
append
(
pred_bboxes
[
j
])
for
i
in
labelDict
:
vis
.
add_bboxes
(
bbox3d
=
np
.
array
(
labelDict
[
i
]),
bbox_color
=
palette
[
i
],
points_in_box_color
=
palette
[
i
])
if
gt_bboxes
is
not
None
:
if
gt_bboxes
is
not
None
:
vis
.
add_bboxes
(
bbox3d
=
gt_bboxes
,
bbox_color
=
(
0
,
0
,
1
))
vis
.
add_bboxes
(
bbox3d
=
gt_bboxes
,
bbox_color
=
(
0
,
0
,
1
))
show_path
=
osp
.
join
(
result_path
,
show_path
=
osp
.
join
(
result_path
,
...
...
mmdet3d/models/detectors/base.py
View file @
d75836ea
...
@@ -60,13 +60,18 @@ class Base3DDetector(BaseDetector):
...
@@ -60,13 +60,18 @@ class Base3DDetector(BaseDetector):
else
:
else
:
return
self
.
forward_test
(
**
kwargs
)
return
self
.
forward_test
(
**
kwargs
)
def
show_results
(
self
,
data
,
result
,
out_dir
):
def
show_results
(
self
,
data
,
result
,
out_dir
,
show
=
False
,
score_thr
=
None
):
"""Results visualization.
"""Results visualization.
Args:
Args:
data (list[dict]): Input points and the information of the sample.
data (list[dict]): Input points and the information of the sample.
result (list[dict]): Prediction results.
result (list[dict]): Prediction results.
out_dir (str): Output directory of visualization result.
out_dir (str): Output directory of visualization result.
show (bool, optional): Determines whether you are
going to show result by open3d.
Defaults to False.
score_thr (float, optional): Score threshold of bounding boxes.
Default to None.
"""
"""
for
batch_id
in
range
(
len
(
result
)):
for
batch_id
in
range
(
len
(
result
)):
if
isinstance
(
data
[
'points'
][
0
],
DC
):
if
isinstance
(
data
[
'points'
][
0
],
DC
):
...
@@ -93,6 +98,12 @@ class Base3DDetector(BaseDetector):
...
@@ -93,6 +98,12 @@ class Base3DDetector(BaseDetector):
assert
out_dir
is
not
None
,
'Expect out_dir, got none.'
assert
out_dir
is
not
None
,
'Expect out_dir, got none.'
pred_bboxes
=
result
[
batch_id
][
'boxes_3d'
]
pred_bboxes
=
result
[
batch_id
][
'boxes_3d'
]
pred_labels
=
result
[
batch_id
][
'labels_3d'
]
if
score_thr
is
not
None
:
mask
=
result
[
batch_id
][
'scores_3d'
]
>
score_thr
pred_bboxes
=
pred_bboxes
[
mask
]
pred_labels
=
pred_labels
[
mask
]
# for now we convert points and bbox into depth mode
# for now we convert points and bbox into depth mode
if
(
box_mode_3d
==
Box3DMode
.
CAM
)
or
(
box_mode_3d
if
(
box_mode_3d
==
Box3DMode
.
CAM
)
or
(
box_mode_3d
...
@@ -105,4 +116,11 @@ class Base3DDetector(BaseDetector):
...
@@ -105,4 +116,11 @@ class Base3DDetector(BaseDetector):
ValueError
(
ValueError
(
f
'Unsupported box_mode_3d
{
box_mode_3d
}
for conversion!'
)
f
'Unsupported box_mode_3d
{
box_mode_3d
}
for conversion!'
)
pred_bboxes
=
pred_bboxes
.
tensor
.
cpu
().
numpy
()
pred_bboxes
=
pred_bboxes
.
tensor
.
cpu
().
numpy
()
show_result
(
points
,
None
,
pred_bboxes
,
out_dir
,
file_name
)
show_result
(
points
,
None
,
pred_bboxes
,
out_dir
,
file_name
,
show
=
show
,
pred_labels
=
pred_labels
)
mmdet3d/models/detectors/single_stage_mono3d.py
View file @
d75836ea
...
@@ -179,13 +179,20 @@ class SingleStageMono3DDetector(SingleStageDetector):
...
@@ -179,13 +179,20 @@ class SingleStageMono3DDetector(SingleStageDetector):
return
[
bbox_list
]
return
[
bbox_list
]
def
show_results
(
self
,
data
,
result
,
out_dir
):
def
show_results
(
self
,
data
,
result
,
out_dir
,
show
=
False
,
score_thr
=
None
):
"""Results visualization.
"""Results visualization.
Args:
Args:
data (list[dict]): Input images and the information of the sample.
data (list[dict]): Input images and the information of the sample.
result (list[dict]): Prediction results.
result (list[dict]): Prediction results.
out_dir (str): Output directory of visualization result.
out_dir (str): Output directory of visualization result.
show (bool, optional): Determines whether you are
going to show result by open3d.
Defaults to False.
TODO: implement score_thr of single_stage_mono3d.
score_thr (float, optional): Score threshold of bounding boxes.
Default to None.
Not implemented yet, but it is here for unification.
"""
"""
for
batch_id
in
range
(
len
(
result
)):
for
batch_id
in
range
(
len
(
result
)):
if
isinstance
(
data
[
'img_metas'
][
0
],
DC
):
if
isinstance
(
data
[
'img_metas'
][
0
],
DC
):
...
@@ -216,4 +223,4 @@ class SingleStageMono3DDetector(SingleStageDetector):
...
@@ -216,4 +223,4 @@ class SingleStageMono3DDetector(SingleStageDetector):
out_dir
,
out_dir
,
file_name
,
file_name
,
'camera'
,
'camera'
,
show
=
True
)
show
=
show
)
mmdet3d/models/segmentors/base.py
View file @
d75836ea
...
@@ -72,7 +72,9 @@ class Base3DSegmentor(BaseSegmentor):
...
@@ -72,7 +72,9 @@ class Base3DSegmentor(BaseSegmentor):
result
,
result
,
palette
=
None
,
palette
=
None
,
out_dir
=
None
,
out_dir
=
None
,
ignore_index
=
None
):
ignore_index
=
None
,
show
=
False
,
score_thr
=
None
):
"""Results visualization.
"""Results visualization.
Args:
Args:
...
@@ -85,6 +87,13 @@ class Base3DSegmentor(BaseSegmentor):
...
@@ -85,6 +87,13 @@ class Base3DSegmentor(BaseSegmentor):
ignore_index (int, optional): The label index to be ignored, e.g.
ignore_index (int, optional): The label index to be ignored, e.g.
unannotated points. If None is given, set to len(self.CLASSES).
unannotated points. If None is given, set to len(self.CLASSES).
Defaults to None.
Defaults to None.
show (bool, optional): Determines whether you are
going to show result by open3d.
Defaults to False.
TODO: implement score_thr of Base3DSegmentor.
score_thr (float, optional): Score threshold of bounding boxes.
Default to None.
Not implemented yet, but it is here for unification.
"""
"""
assert
out_dir
is
not
None
,
'Expect out_dir, got none.'
assert
out_dir
is
not
None
,
'Expect out_dir, got none.'
if
palette
is
None
:
if
palette
is
None
:
...
@@ -123,4 +132,4 @@ class Base3DSegmentor(BaseSegmentor):
...
@@ -123,4 +132,4 @@ class Base3DSegmentor(BaseSegmentor):
file_name
,
file_name
,
palette
,
palette
,
ignore_index
,
ignore_index
,
show
=
True
)
show
=
show
)
mmdet3d/version.py
View file @
d75836ea
# Copyright (c) Open-MMLab. All rights reserved.
# Copyright (c) Open-MMLab. All rights reserved.
__version__
=
'0.17.
2
'
__version__
=
'0.17.
3
'
short_version
=
__version__
short_version
=
__version__
...
...
requirements/docs.txt
View file @
d75836ea
docutils==0.16.0
docutils==0.16.0
m2r
m2r
myst-parser
mistune==0.8.4
opencv-python
-e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme
-e git+https://github.com/open-mmlab/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme
recommonmark
sphinx==4.0.2
sphinx==4.0.2
sphinx-copybutton
sphinx-copybutton
sphinx_markdown_tables
sphinx_markdown_tables
torch
requirements/mminstall.txt
View file @
d75836ea
mmcv-full>=1.3.8,<=1.
4
.0
mmcv-full>=1.3.8,<=1.
5
.0
mmdet>=2.14.0,<=3.0.0
mmdet>=2.14.0,<=3.0.0
mmsegmentation>=0.14.1,<=1.0.0
mmsegmentation>=0.14.1,<=1.0.0
requirements/readthedocs.txt
View file @
d75836ea
mmcv
mmcv
mmdet
mmsegmentation>=0.14.1
torch
torch
torchvision
torchvision
Prev
1
2
3
4
5
6
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