Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
PaddleDetection
提交
cc534530
P
PaddleDetection
项目概览
PaddlePaddle
/
PaddleDetection
大约 1 年 前同步成功
通知
695
Star
11112
Fork
2696
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
184
列表
看板
标记
里程碑
合并请求
40
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleDetection
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
184
Issue
184
列表
看板
标记
里程碑
合并请求
40
合并请求
40
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
cc534530
编写于
1月 23, 2019
作者:
J
jerrywgz
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add comment and refine code, test=develop
上级
7d0c5faf
变更
4
显示空白变更内容
内联
并排
Showing
4 changed file
with
170 addition
and
121 deletion
+170
-121
paddle/fluid/API.spec
paddle/fluid/API.spec
+1
-1
paddle/fluid/operators/detection/bbox_util.h
paddle/fluid/operators/detection/bbox_util.h
+0
-20
paddle/fluid/operators/detection/multiclass_nms_op.cc
paddle/fluid/operators/detection/multiclass_nms_op.cc
+88
-99
python/paddle/fluid/layers/detection.py
python/paddle/fluid/layers/detection.py
+81
-1
未找到文件。
paddle/fluid/API.spec
浏览文件 @
cc534530
...
...
@@ -318,7 +318,7 @@ paddle.fluid.layers.iou_similarity ArgSpec(args=['x', 'y', 'name'], varargs=None
paddle.fluid.layers.box_coder ArgSpec(args=['prior_box', 'prior_box_var', 'target_box', 'code_type', 'box_normalized', 'name'], varargs=None, keywords=None, defaults=('encode_center_size', True, None))
paddle.fluid.layers.polygon_box_transform ArgSpec(args=['input', 'name'], varargs=None, keywords=None, defaults=(None,))
paddle.fluid.layers.yolov3_loss ArgSpec(args=['x', 'gtbox', 'gtlabel', 'anchors', 'class_num', 'ignore_thresh', 'loss_weight_xy', 'loss_weight_wh', 'loss_weight_conf_target', 'loss_weight_conf_notarget', 'loss_weight_class', 'name'], varargs=None, keywords=None, defaults=(None, None, None, None, None, None))
paddle.fluid.layers.multiclass_nms ArgSpec(args=['bboxes', 'scores', 'score_threshold', 'nms_top_k', 'nms_threshold', 'keep_top_k', 'normalized', 'nms_eta', 'background_label'
], varargs=None, keywords=None, defaults=(True, 1.0, 0
))
paddle.fluid.layers.multiclass_nms ArgSpec(args=['bboxes', 'scores', 'score_threshold', 'nms_top_k', 'nms_threshold', 'keep_top_k', 'normalized', 'nms_eta', 'background_label'
, 'name'], varargs=None, keywords=None, defaults=(True, 1.0, 0, None
))
paddle.fluid.layers.accuracy ArgSpec(args=['input', 'label', 'k', 'correct', 'total'], varargs=None, keywords=None, defaults=(1, None, None))
paddle.fluid.layers.auc ArgSpec(args=['input', 'label', 'curve', 'num_thresholds', 'topk', 'slide_steps'], varargs=None, keywords=None, defaults=('ROC', 4095, 1, 1))
paddle.fluid.layers.exponential_decay ArgSpec(args=['learning_rate', 'decay_steps', 'decay_rate', 'staircase'], varargs=None, keywords=None, defaults=(False,))
...
...
paddle/fluid/operators/detection/bbox_util.h
浏览文件 @
cc534530
...
...
@@ -93,25 +93,5 @@ void BboxOverlaps(const framework::Tensor& r_boxes,
}
}
template
<
class
T
>
void
SliceOneClass
(
const
platform
::
DeviceContext
&
ctx
,
const
framework
::
Tensor
&
items
,
const
int
class_id
,
framework
::
Tensor
*
one_class_item
)
{
T
*
item_data
=
one_class_item
->
mutable_data
<
T
>
(
ctx
.
GetPlace
());
const
T
*
items_data
=
items
.
data
<
T
>
();
const
int64_t
num_item
=
items
.
dims
()[
0
];
const
int
class_num
=
items
.
dims
()[
1
];
int
item_size
=
1
;
if
(
items
.
dims
().
size
()
==
3
)
{
item_size
=
items
.
dims
()[
2
];
}
for
(
int
i
=
0
;
i
<
num_item
;
++
i
)
{
for
(
int
j
=
0
;
j
<
item_size
;
++
j
)
{
item_data
[
i
*
item_size
+
j
]
=
items_data
[
i
*
class_num
*
item_size
+
class_id
*
item_size
+
j
];
}
}
}
}
// namespace operators
}
// namespace paddle
paddle/fluid/operators/detection/multiclass_nms_op.cc
浏览文件 @
cc534530
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
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.
...
...
@@ -10,7 +13,6 @@ limitations under the License. */
#include <glog/logging.h>
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/operators/detection/bbox_util.h"
#include "paddle/fluid/operators/detection/poly_util.h"
namespace
paddle
{
...
...
@@ -136,12 +138,9 @@ static inline T JaccardOverlap(const T* box1, const T* box2,
const
T
inter_ymin
=
std
::
max
(
box1
[
1
],
box2
[
1
]);
const
T
inter_xmax
=
std
::
min
(
box1
[
2
],
box2
[
2
]);
const
T
inter_ymax
=
std
::
min
(
box1
[
3
],
box2
[
3
]);
T
inter_w
=
inter_xmax
-
inter_xmin
;
T
inter_h
=
inter_ymax
-
inter_ymin
;
if
(
!
normalized
)
{
inter_w
+=
1
;
inter_h
+=
1
;
}
T
norm
=
normalized
?
static_cast
<
T
>
(
0.
)
:
static_cast
<
T
>
(
1.
);
T
inter_w
=
inter_xmax
-
inter_xmin
+
norm
;
T
inter_h
=
inter_ymax
-
inter_ymin
+
norm
;
const
T
inter_area
=
inter_w
*
inter_h
;
const
T
bbox1_area
=
BBoxArea
<
T
>
(
box1
,
normalized
);
const
T
bbox2_area
=
BBoxArea
<
T
>
(
box2
,
normalized
);
...
...
@@ -164,6 +163,25 @@ T PolyIoU(const T* box1, const T* box2, const size_t box_size,
}
}
template
<
class
T
>
void
SliceOneClass
(
const
platform
::
DeviceContext
&
ctx
,
const
framework
::
Tensor
&
items
,
const
int
class_id
,
framework
::
Tensor
*
one_class_item
)
{
T
*
item_data
=
one_class_item
->
mutable_data
<
T
>
(
ctx
.
GetPlace
());
const
T
*
items_data
=
items
.
data
<
T
>
();
const
int64_t
num_item
=
items
.
dims
()[
0
];
const
int
class_num
=
items
.
dims
()[
1
];
int
item_size
=
1
;
if
(
items
.
dims
().
size
()
==
3
)
{
item_size
=
items
.
dims
()[
2
];
}
for
(
int
i
=
0
;
i
<
num_item
;
++
i
)
{
std
::
memcpy
(
item_data
+
i
*
item_size
,
items_data
+
i
*
class_num
*
item_size
+
class_id
*
item_size
,
sizeof
(
T
)
*
item_size
);
}
}
template
<
typename
T
>
class
MultiClassNMSKernel
:
public
framework
::
OpKernel
<
T
>
{
public:
...
...
@@ -237,33 +255,26 @@ class MultiClassNMSKernel : public framework::OpKernel<T> {
auto
&
dev_ctx
=
ctx
.
template
device_context
<
platform
::
CPUDeviceContext
>();
int
num_det
=
0
;
int64_t
box_num
=
0
,
class_num
=
0
,
predict_dim
=
0
;
if
(
scores_size
==
3
)
{
class_num
=
scores
.
dims
()[
0
];
predict_dim
=
scores
.
dims
()[
1
];
int64_t
class_num
=
scores_size
==
3
?
scores
.
dims
()[
0
]
:
scores
.
dims
()[
1
];
Tensor
bbox_slice
,
score_slice
;
for
(
int64_t
c
=
0
;
c
<
class_num
;
++
c
)
{
if
(
c
==
background_label
)
continue
;
Tensor
score
=
scores
.
Slice
(
c
,
c
+
1
);
NMSFast
(
bboxes
,
score
,
score_threshold
,
nms_threshold
,
nms_eta
,
nms_top_k
,
&
((
*
indices
)[
c
]),
normalized
);
num_det
+=
(
*
indices
)[
c
].
size
();
}
if
(
scores_size
==
3
)
{
score_slice
=
scores
.
Slice
(
c
,
c
+
1
);
bbox_slice
=
bboxes
;
}
else
{
box_num
=
scores
.
dims
()[
0
];
class_num
=
scores
.
dims
()[
1
];
Tensor
score
;
score
.
Resize
({
box_num
,
1
});
Tensor
bbox
;
bbox
.
Resize
({
box_num
,
4
});
for
(
int64_t
c
=
0
;
c
<
class_num
;
++
c
)
{
if
(
c
==
background_label
)
continue
;
SliceOneClass
<
T
>
(
dev_ctx
,
scores
,
c
,
&
score
);
SliceOneClass
<
T
>
(
dev_ctx
,
bboxes
,
c
,
&
bbox
);
NMSFast
(
bbox
,
score
,
score_threshold
,
nms_threshold
,
nms_eta
,
nms_top_k
,
&
((
*
indices
)[
c
]),
normalized
);
score_slice
.
Resize
({
scores
.
dims
()[
0
],
1
});
bbox_slice
.
Resize
({
scores
.
dims
()[
0
],
4
});
SliceOneClass
<
T
>
(
dev_ctx
,
scores
,
c
,
&
score_slice
);
SliceOneClass
<
T
>
(
dev_ctx
,
bboxes
,
c
,
&
bbox_slice
);
}
NMSFast
(
bbox_slice
,
score_slice
,
score_threshold
,
nms_threshold
,
nms_eta
,
nms_top_k
,
&
((
*
indices
)[
c
]),
normalized
);
if
(
scores_size
==
2
)
{
std
::
stable_sort
((
*
indices
)[
c
].
begin
(),
(
*
indices
)[
c
].
end
());
num_det
+=
(
*
indices
)[
c
].
size
();
}
num_det
+=
(
*
indices
)[
c
].
size
();
}
*
num_nmsed_out
=
num_det
;
...
...
@@ -274,12 +285,11 @@ class MultiClassNMSKernel : public framework::OpKernel<T> {
for
(
const
auto
&
it
:
*
indices
)
{
int
label
=
it
.
first
;
if
(
scores_size
==
3
)
{
sdata
=
scores_data
+
label
*
predict_dim
;
sdata
=
scores_data
+
label
*
scores
.
dims
()[
1
]
;
}
else
{
Tensor
score
;
score
.
Resize
({
box_num
,
1
});
SliceOneClass
<
T
>
(
dev_ctx
,
scores
,
label
,
&
score
);
sdata
=
score
.
data
<
T
>
();
score_slice
.
Resize
({
scores
.
dims
()[
0
],
1
});
SliceOneClass
<
T
>
(
dev_ctx
,
scores
,
label
,
&
score_slice
);
sdata
=
score_slice
.
data
<
T
>
();
}
const
std
::
vector
<
int
>&
label_indices
=
it
.
second
;
for
(
size_t
j
=
0
;
j
<
label_indices
.
size
();
++
j
)
{
...
...
@@ -362,44 +372,34 @@ class MultiClassNMSKernel : public framework::OpKernel<T> {
auto
*
outs
=
ctx
.
Output
<
LoDTensor
>
(
"Out"
);
auto
score_dims
=
scores
->
dims
();
int64_t
class_num
=
score_dims
[
1
]
;
auto
score_size
=
score_dims
.
size
()
;
auto
&
dev_ctx
=
ctx
.
template
device_context
<
platform
::
CPUDeviceContext
>();
std
::
vector
<
std
::
map
<
int
,
std
::
vector
<
int
>>>
all_indices
;
std
::
vector
<
size_t
>
batch_starts
=
{
0
};
int64_t
batch_size
=
score_dims
[
0
];
int64_t
predict_dim
=
0
;
int64_t
box_dim
=
boxes
->
dims
()[
2
];
int64_t
out_dim
=
box_dim
+
2
;
int
num_nmsed_out
=
0
;
if
(
score_dims
.
size
()
==
3
)
{
predict_dim
=
score_dims
[
2
];
for
(
int64_t
i
=
0
;
i
<
batch_size
;
++
i
)
{
Tensor
ins_score
=
scores
->
Slice
(
i
,
i
+
1
);
ins_score
.
Resize
({
class_num
,
predict_dim
});
Tensor
ins_boxes
=
boxes
->
Slice
(
i
,
i
+
1
);
ins_boxes
.
Resize
({
predict_dim
,
box_dim
});
std
::
map
<
int
,
std
::
vector
<
int
>>
indices
;
MultiClassNMS
(
ctx
,
ins_score
,
ins_boxes
,
score_dims
.
size
(),
&
indices
,
&
num_nmsed_out
);
all_indices
.
push_back
(
indices
);
batch_starts
.
push_back
(
batch_starts
.
back
()
+
num_nmsed_out
);
}
Tensor
boxes_slice
,
scores_slice
;
int
n
=
score_size
==
3
?
batch_size
:
boxes
->
lod
().
back
().
size
()
-
1
;
for
(
int
i
=
0
;
i
<
n
;
++
i
)
{
if
(
score_size
==
3
)
{
scores_slice
=
scores
->
Slice
(
i
,
i
+
1
);
scores_slice
.
Resize
({
score_dims
[
1
],
score_dims
[
2
]});
boxes_slice
=
boxes
->
Slice
(
i
,
i
+
1
);
boxes_slice
.
Resize
({
score_dims
[
2
],
box_dim
});
}
else
{
auto
boxes_lod
=
boxes
->
lod
().
back
();
int64_t
n
=
static_cast
<
int64_t
>
(
boxes_lod
.
size
()
-
1
);
for
(
int
i
=
0
;
i
<
n
;
++
i
)
{
Tensor
boxes_slice
=
boxes
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
Tensor
scores_slice
=
scores
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
scores_slice
=
scores
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
boxes_slice
=
boxes
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
}
std
::
map
<
int
,
std
::
vector
<
int
>>
indices
;
MultiClassNMS
(
ctx
,
scores_slice
,
boxes_slice
,
score_dims
.
size
()
,
&
indices
,
&
num_nmsed_out
);
MultiClassNMS
(
ctx
,
scores_slice
,
boxes_slice
,
score_size
,
&
indices
,
&
num_nmsed_out
);
all_indices
.
push_back
(
indices
);
batch_starts
.
push_back
(
batch_starts
.
back
()
+
num_nmsed_out
);
}
}
int
num_kept
=
batch_starts
.
back
();
if
(
num_kept
==
0
)
{
...
...
@@ -408,28 +408,17 @@ class MultiClassNMSKernel : public framework::OpKernel<T> {
batch_starts
=
{
0
,
1
};
}
else
{
outs
->
mutable_data
<
T
>
({
num_kept
,
out_dim
},
ctx
.
GetPlace
());
if
(
score_dims
.
size
()
==
3
)
{
for
(
int64_t
i
=
0
;
i
<
batch_size
;
++
i
)
{
Tensor
ins_score
=
scores
->
Slice
(
i
,
i
+
1
);
ins_score
.
Resize
({
class_num
,
predict_dim
});
Tensor
ins_boxes
=
boxes
->
Slice
(
i
,
i
+
1
);
ins_boxes
.
Resize
({
predict_dim
,
box_dim
});
int64_t
s
=
batch_starts
[
i
];
int64_t
e
=
batch_starts
[
i
+
1
];
if
(
e
>
s
)
{
Tensor
out
=
outs
->
Slice
(
s
,
e
);
MultiClassOutput
(
dev_ctx
,
ins_score
,
ins_boxes
,
all_indices
[
i
],
score_dims
.
size
(),
&
out
);
}
}
for
(
int
i
=
0
;
i
<
n
;
++
i
)
{
if
(
score_size
==
3
)
{
scores_slice
=
scores
->
Slice
(
i
,
i
+
1
);
boxes_slice
=
boxes
->
Slice
(
i
,
i
+
1
);
scores_slice
.
Resize
({
score_dims
[
1
],
score_dims
[
2
]});
boxes_slice
.
Resize
({
score_dims
[
2
],
box_dim
});
}
else
{
auto
boxes_lod
=
boxes
->
lod
().
back
();
int64_t
n
=
static_cast
<
int64_t
>
(
boxes_lod
.
size
()
-
1
);
for
(
int
i
=
0
;
i
<
n
;
++
i
)
{
Tensor
boxes_slice
=
boxes
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
Tensor
scores_slice
=
scores
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
scores_slice
=
scores
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
boxes_slice
=
boxes
->
Slice
(
boxes_lod
[
i
],
boxes_lod
[
i
+
1
]);
}
int64_t
s
=
batch_starts
[
i
];
int64_t
e
=
batch_starts
[
i
+
1
];
if
(
e
>
s
)
{
...
...
@@ -439,7 +428,6 @@ class MultiClassNMSKernel : public framework::OpKernel<T> {
}
}
}
}
framework
::
LoD
lod
;
lod
.
emplace_back
(
batch_starts
);
...
...
@@ -458,17 +446,18 @@ class MultiClassNMSOpMaker : public framework::OpProtoAndCheckerMaker {
"predicted locations of M bounding bboxes, N is the batch size. "
"Each bounding box has four coordinate values and the layout is "
"[xmin, ymin, xmax, ymax], when box size equals to 4."
"2. (LoDTensor) A 3-D Tensor with shape [
N, M
, 4]"
"
N is the number of boxes, M
is the class number"
);
"2. (LoDTensor) A 3-D Tensor with shape [
M, C
, 4]"
"
M is the number of bounding boxes, C
is the class number"
);
AddInput
(
"Scores"
,
"Two types of scores are supported:"
"1. (Tensor) A 3-D Tensor with shape [N, C, M] represents the "
"predicted confidence predictions. N is the batch size, C is the "
"class number, M is number of bounding boxes. For each category "
"there are total M scores which corresponding M bounding boxes. "
" Please note, M is equal to the 1st dimension of BBoxes. "
"2. (LoDTensor) A 2-D LoDTensor with shape"
"[N, num_class]. N is the number of bbox"
);
" Please note, M is equal to the 2nd dimension of BBoxes. "
"2. (LoDTensor) A 2-D LoDTensor with shape [M, C]. "
"M is the number of bbox, C is the class number. In this case, "
"Input BBoxes should be the second case with shape [M, C, 4]."
);
AddAttr
<
int
>
(
"background_label"
,
"(int, defalut: 0) "
...
...
@@ -528,8 +517,8 @@ independently for each class. The outputs is a 2-D LoDTenosr, for each
image, the offsets in first dimension of LoDTensor are called LoD, the number
of offset is N + 1, where N is the batch size. If LoD[i + 1] - LoD[i] == 0,
means there is no detected bbox for this image. If there is no detected boxes
for all images, all the elements in LoD are
0, and the Out only contains one
value which is -1.
for all images, all the elements in LoD are
set to {0,1}, and the Out only
contains one
value which is -1.
)DOC"
);
}
};
...
...
python/paddle/fluid/layers/detection.py
浏览文件 @
cc534530
...
...
@@ -1821,8 +1821,88 @@ def multiclass_nms(bboxes,
keep_top_k
,
normalized
=
True
,
nms_eta
=
1.
,
background_label
=
0
):
background_label
=
0
,
name
=
None
):
"""
**Multiclass NMS**
This operator is to do multi-class non maximum suppression (NMS) on
boxes and scores.
In the NMS step, this operator greedily selects a subset of detection bounding
boxes that have high scores larger than score_threshold, if providing this
threshold, then selects the largest nms_top_k confidences scores if nms_top_k
is larger than -1. Then this operator pruns away boxes that have high IOU
(intersection over union) overlap with already selected boxes by adaptive
threshold NMS based on parameters of nms_threshold and nms_eta.
Aftern NMS step, at most keep_top_k number of total bboxes are to be kept
per image if keep_top_k is larger than -1.
Args:
bboxes (Variable): Two types of bboxes are supported:
1. (Tensor) A 3-D Tensor with shape
[N, M, 4 or 8 16 24 32] represents the
predicted locations of M bounding bboxes,
N is the batch size. Each bounding box has four
coordinate values and the layout is
[xmin, ymin, xmax, ymax], when box size equals to 4.
2. (LoDTensor) A 3-D Tensor with shape [M, C, 4]
M is the number of bounding boxes, C is the
class number
scores (Variable): Two types of scores are supported:
1. (Tensor) A 3-D Tensor with shape [N, C, M]
represents the predicted confidence predictions.
N is the batch size, C is the class number, M is
number of bounding boxes. For each category there
are total M scores which corresponding M bounding
boxes. Please note, M is equal to the 2nd dimension
of BBoxes.
2. (LoDTensor) A 2-D LoDTensor with shape [M, C].
M is the number of bbox, C is the class number.
In this case, input BBoxes should be the second
case with shape [M, C, 4].
background_label (int): The index of background label, the background
label will be ignored. If set to -1, then all
categories will be considered. Default: 0
score_threshold (float): Threshold to filter out bounding boxes with
low confidence score. If not provided,
consider all boxes.
nms_top_k (int): Maximum number of detections to be kept according to
the confidences aftern the filtering detections based
on score_threshold.
nms_threshold (float): The threshold to be used in NMS. Default: 0.3
nms_eta (float): The threshold to be used in NMS. Default: 1.0
keep_top_k (int): Number of total bboxes to be kept per image after NMS
step. -1 means keeping all bboxes after NMS step.
normalized (bool): Whether detections are normalized. Default: True
name(str): Name of the multiclass nms op. Default: None.
Returns:
Out: A 2-D LoDTensor with shape [No, 6] represents the detections.
Each row has 6 values: [label, confidence, xmin, ymin, xmax, ymax]
or A 2-D LoDTensor with shape [No, 10] represents the detections.
Each row has 10 values:
[label, confidence, x1, y1, x2, y2, x3, y3, x4, y4]. No is the
total number of detections. If there is no detected boxes for all
images, lod will be set to {0, 1} and Out only contains one value
which is -1.
Examples:
.. code-block:: python
boxes = fluid.layers.data(name='bboxes', shape=[81, 4],
dtype='float32', lod_level=1)
scores = fluid.layers.data(name='scores', shape=[81],
dtype='float32', lod_level=1)
out = fluid.layers.multiclass_nms(bboxes=boxes,
scores=scores,
background_label=0,
score_threshold=0.5,
nms_top_k=400,
nms_threshold=0.3,
keep_top_k=200,
normalized=False)
"""
helper
=
LayerHelper
(
'multiclass_nms'
,
**
locals
())
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录