Skip to content

Commit 7757a8a

Browse files
author
chengduo
authored
Merge pull request #8265 from chengduoZH/feature/add_prior_box_py
Add Python interface of prior_boxes
2 parents 8a0dd24 + dff1bf3 commit 7757a8a

File tree

6 files changed

+302
-27
lines changed

6 files changed

+302
-27
lines changed

paddle/fluid/operators/prior_box_op.cc

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,24 @@ class PriorBoxOp : public framework::OperatorWithKernel {
3838
PADDLE_ENFORCE_LT(input_dims[3], image_dims[3],
3939
"The width of input must smaller than image.");
4040

41-
auto min_sizes = ctx->Attrs().Get<std::vector<int>>("min_sizes");
42-
auto max_sizes = ctx->Attrs().Get<std::vector<int>>("max_sizes");
41+
auto min_sizes = ctx->Attrs().Get<std::vector<float>>("min_sizes");
42+
auto max_sizes = ctx->Attrs().Get<std::vector<float>>("max_sizes");
4343
auto variances = ctx->Attrs().Get<std::vector<float>>("variances");
4444
auto aspect_ratios = ctx->Attrs().Get<std::vector<float>>("aspect_ratios");
4545
bool flip = ctx->Attrs().Get<bool>("flip");
4646

4747
std::vector<float> aspect_ratios_vec;
4848
ExpandAspectRatios(aspect_ratios, flip, aspect_ratios_vec);
4949

50-
int num_priors = aspect_ratios_vec.size() * min_sizes.size();
50+
size_t num_priors = aspect_ratios_vec.size() * min_sizes.size();
5151
if (max_sizes.size() > 0) {
5252
PADDLE_ENFORCE_EQ(max_sizes.size(), min_sizes.size(),
5353
"The number of min_size and max_size must be equal.");
54-
for (size_t i = 0; i < min_sizes.size(); ++i) {
54+
num_priors += max_sizes.size();
55+
for (size_t i = 0; i < max_sizes.size(); ++i) {
5556
PADDLE_ENFORCE_GT(max_sizes[i], min_sizes[i],
5657
"max_size[%d] must be greater than min_size[%d].", i,
5758
i);
58-
num_priors += 1;
5959
}
6060
}
6161

@@ -90,20 +90,20 @@ class PriorBoxOpMaker : public framework::OpProtoAndCheckerMaker {
9090
"H is the height of input, W is the width of input, num_priors "
9191
"is the box count of each position.");
9292

93-
AddAttr<std::vector<int>>("min_sizes",
94-
"(vector<int>) List of min sizes "
95-
"of generated prior boxes.")
96-
.AddCustomChecker([](const std::vector<int>& min_sizes) {
93+
AddAttr<std::vector<float>>("min_sizes",
94+
"(vector<float>) List of min sizes "
95+
"of generated prior boxes.")
96+
.AddCustomChecker([](const std::vector<float>& min_sizes) {
9797
PADDLE_ENFORCE_GT(min_sizes.size(), 0,
9898
"Size of min_sizes must be at least 1.");
9999
for (size_t i = 0; i < min_sizes.size(); ++i) {
100-
PADDLE_ENFORCE_GT(min_sizes[i], 0,
100+
PADDLE_ENFORCE_GT(min_sizes[i], 0.0,
101101
"min_sizes[%d] must be positive.", i);
102102
}
103103
});
104-
AddAttr<std::vector<int>>(
104+
AddAttr<std::vector<float>>(
105105
"max_sizes",
106-
"(vector<int>) List of max sizes of generated prior boxes.");
106+
"(vector<float>) List of max sizes of generated prior boxes.");
107107
AddAttr<std::vector<float>>(
108108
"aspect_ratios",
109109
"(vector<float>) List of aspect ratios of generated prior boxes.");
@@ -125,16 +125,16 @@ class PriorBoxOpMaker : public framework::OpProtoAndCheckerMaker {
125125
.SetDefault(true);
126126

127127
AddAttr<float>("step_w",
128-
"Prior boxes step across width, 0 for auto calculation.")
128+
"Prior boxes step across width, 0.0 for auto calculation.")
129129
.SetDefault(0.0)
130130
.AddCustomChecker([](const float& step_w) {
131-
PADDLE_ENFORCE_GT(step_w, 0.0, "step_w should be larger than 0.");
131+
PADDLE_ENFORCE_GE(step_w, 0.0, "step_w should be larger than 0.");
132132
});
133133
AddAttr<float>("step_h",
134-
"Prior boxes step across height, 0 for auto calculation.")
134+
"Prior boxes step across height, 0.0 for auto calculation.")
135135
.SetDefault(0.0)
136136
.AddCustomChecker([](const float& step_h) {
137-
PADDLE_ENFORCE_GT(step_h, 0.0, "step_h should be larger than 0.");
137+
PADDLE_ENFORCE_GE(step_h, 0.0, "step_h should be larger than 0.");
138138
});
139139

140140
AddAttr<float>("offset",

paddle/fluid/operators/prior_box_op.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ class PriorBoxOpKernel : public framework::OpKernel<T> {
6060
auto* boxes = ctx.Output<paddle::framework::Tensor>("Boxes");
6161
auto* vars = ctx.Output<paddle::framework::Tensor>("Variances");
6262

63-
auto min_sizes = ctx.Attr<std::vector<int>>("min_sizes");
64-
auto max_sizes = ctx.Attr<std::vector<int>>("max_sizes");
63+
auto min_sizes = ctx.Attr<std::vector<float>>("min_sizes");
64+
auto max_sizes = ctx.Attr<std::vector<float>>("max_sizes");
6565
auto input_aspect_ratio = ctx.Attr<std::vector<float>>("aspect_ratios");
6666
auto variances = ctx.Attr<std::vector<float>>("variances");
6767
auto flip = ctx.Attr<bool>("flip");
@@ -108,7 +108,7 @@ class PriorBoxOpKernel : public framework::OpKernel<T> {
108108
T box_width, box_height;
109109
int idx = 0;
110110
for (size_t s = 0; s < min_sizes.size(); ++s) {
111-
int min_size = min_sizes[s];
111+
auto min_size = min_sizes[s];
112112
// first prior: aspect_ratio = 1, size = min_size
113113
box_width = box_height = min_size;
114114
// xmin
@@ -124,7 +124,7 @@ class PriorBoxOpKernel : public framework::OpKernel<T> {
124124

125125
idx++;
126126
if (max_sizes.size() > 0) {
127-
int max_size = max_sizes[s];
127+
auto max_size = max_sizes[s];
128128
// second prior: aspect_ratio = 1,
129129
// size = sqrt(min_size * max_size)
130130
box_width = box_height = sqrt(min_size * max_size);

python/paddle/v2/fluid/layers/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,16 @@
2828
from device import *
2929
import math_op_patch
3030
from math_op_patch import *
31+
import detection
32+
from detection import *
3133

3234
__all__ = []
35+
__all__ += math_op_patch.__all__
3336
__all__ += detection.__all__
3437
__all__ += nn.__all__
3538
__all__ += io.__all__
3639
__all__ += tensor.__all__
3740
__all__ += control_flow.__all__
3841
__all__ += ops.__all__
3942
__all__ += device.__all__
40-
__all__ += math_op_patch.__all__
43+
__all__ += detection.__all__

python/paddle/v2/fluid/layers/detection.py

Lines changed: 214 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve.
1+
# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -16,8 +16,15 @@
1616
"""
1717

1818
from ..layer_helper import LayerHelper
19+
from ..framework import Variable
20+
from tensor import concat
21+
from ops import reshape
22+
import math
1923

20-
__all__ = ['detection_output', ]
24+
__all__ = [
25+
'detection_output',
26+
'prior_box',
27+
]
2128

2229

2330
def detection_output(scores,
@@ -114,3 +121,208 @@ class number, M is number of bounding boxes. For each category
114121
'nms_eta': 1.0
115122
})
116123
return nmsed_outs
124+
125+
126+
def prior_box(inputs,
127+
image,
128+
min_ratio,
129+
max_ratio,
130+
aspect_ratios,
131+
base_size,
132+
steps=None,
133+
step_w=None,
134+
step_h=None,
135+
offset=0.5,
136+
variance=[0.1, 0.1, 0.1, 0.1],
137+
flip=False,
138+
clip=False,
139+
min_sizes=None,
140+
max_sizes=None,
141+
name=None):
142+
"""
143+
**Prior_boxes**
144+
145+
Generate prior boxes for SSD(Single Shot MultiBox Detector)
146+
algorithm. The details of this algorithm, please refer the
147+
section 2.2 of SSD paper (SSD: Single Shot MultiBox Detector)
148+
<https://arxiv.org/abs/1512.02325>`_ .
149+
150+
Args:
151+
inputs(list): The list of input Variables, the format
152+
of all Variables is NCHW.
153+
image(Variable): The input image data of PriorBoxOp,
154+
the layout is NCHW.
155+
min_ratio(int): the min ratio of generated prior boxes.
156+
max_ratio(int): the max ratio of generated prior boxes.
157+
aspect_ratios(list): the aspect ratios of generated prior
158+
boxes. The length of input and aspect_ratios must be equal.
159+
base_size(int): the base_size is used to get min_size
160+
and max_size according to min_ratio and max_ratio.
161+
step_w(list, optional, default=None): Prior boxes step
162+
across width. If step_w[i] == 0.0, the prior boxes step
163+
across width of the inputs[i] will be automatically calculated.
164+
step_h(list, optional, default=None): Prior boxes step
165+
across height, If step_h[i] == 0.0, the prior boxes
166+
step across height of the inputs[i] will be automatically calculated.
167+
offset(float, optional, default=0.5): Prior boxes center offset.
168+
variance(list, optional, default=[0.1, 0.1, 0.1, 0.1]): the variances
169+
to be encoded in prior boxes.
170+
flip(bool, optional, default=False): Whether to flip
171+
aspect ratios.
172+
clip(bool, optional, default=False): Whether to clip
173+
out-of-boundary boxes.
174+
min_sizes(list, optional, default=None): If `len(inputs) <=2`,
175+
min_sizes must be set up, and the length of min_sizes
176+
should equal to the length of inputs.
177+
max_sizes(list, optional, default=None): If `len(inputs) <=2`,
178+
max_sizes must be set up, and the length of min_sizes
179+
should equal to the length of inputs.
180+
name(str, optional, None): Name of the prior box layer.
181+
182+
Returns:
183+
boxes(Variable): the output prior boxes of PriorBoxOp.
184+
The layout is [num_priors, 4]. num_priors is the total
185+
box count of each position of inputs.
186+
Variances(Variable): the expanded variances of PriorBoxOp.
187+
The layout is [num_priors, 4]. num_priors is the total
188+
box count of each position of inputs
189+
190+
Examples:
191+
.. code-block:: python
192+
193+
prior_box(
194+
inputs = [conv1, conv2, conv3, conv4, conv5, conv6],
195+
image = data,
196+
min_ratio = 20, # 0.20
197+
max_ratio = 90, # 0.90
198+
offset = 0.5,
199+
base_size = 300,
200+
variance = [0.1,0.1,0.1,0.1],
201+
aspect_ratios = [[2.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]],
202+
flip=True,
203+
clip=True)
204+
"""
205+
206+
def _prior_box_(input,
207+
image,
208+
min_sizes,
209+
max_sizes,
210+
aspect_ratios,
211+
variance,
212+
flip=False,
213+
clip=False,
214+
step_w=0.0,
215+
step_h=0.0,
216+
offset=0.5,
217+
name=None):
218+
helper = LayerHelper("prior_box", **locals())
219+
dtype = helper.input_dtype()
220+
221+
box = helper.create_tmp_variable(dtype)
222+
var = helper.create_tmp_variable(dtype)
223+
helper.append_op(
224+
type="prior_box",
225+
inputs={"Input": input,
226+
"Image": image},
227+
outputs={"Boxes": box,
228+
"Variances": var},
229+
attrs={
230+
'min_sizes': min_sizes,
231+
'max_sizes': max_sizes,
232+
'aspect_ratios': aspect_ratios,
233+
'variances': variance,
234+
'flip': flip,
235+
'clip': clip,
236+
'step_w': step_w,
237+
'step_h': step_h,
238+
'offset': offset
239+
})
240+
return box, var
241+
242+
def _reshape_with_axis_(input, axis=1):
243+
if not (axis > 0 and axis < len(input.shape)):
244+
raise ValueError("The axis should be smaller than "
245+
"the arity of input and bigger than 0.")
246+
new_shape = [
247+
-1, reduce(lambda x, y: x * y, input.shape[axis:len(input.shape)])
248+
]
249+
out = reshape(x=input, shape=new_shape)
250+
return out
251+
252+
assert isinstance(inputs, list), 'inputs should be a list.'
253+
num_layer = len(inputs)
254+
255+
if num_layer <= 2:
256+
assert min_sizes is not None and max_sizes is not None
257+
assert len(min_sizes) == num_layer and len(max_sizes) == num_layer
258+
else:
259+
min_sizes = []
260+
max_sizes = []
261+
step = int(math.floor(((max_ratio - min_ratio)) / (num_layer - 2)))
262+
for ratio in xrange(min_ratio, max_ratio + 1, step):
263+
min_sizes.append(base_size * ratio / 100.)
264+
max_sizes.append(base_size * (ratio + step) / 100.)
265+
min_sizes = [base_size * .10] + min_sizes
266+
max_sizes = [base_size * .20] + max_sizes
267+
268+
if aspect_ratios:
269+
if not (isinstance(aspect_ratios, list) and
270+
len(aspect_ratios) == num_layer):
271+
raise ValueError(
272+
'aspect_ratios should be list and the length of inputs '
273+
'and aspect_ratios should be the same.')
274+
if step_h:
275+
if not (isinstance(step_h, list) and len(step_h) == num_layer):
276+
raise ValueError(
277+
'step_h should be list and the length of inputs and '
278+
'step_h should be the same.')
279+
if step_w:
280+
if not (isinstance(step_w, list) and len(step_w) == num_layer):
281+
raise ValueError(
282+
'step_w should be list and the length of inputs and '
283+
'step_w should be the same.')
284+
if steps:
285+
if not (isinstance(steps, list) and len(steps) == num_layer):
286+
raise ValueError(
287+
'steps should be list and the length of inputs and '
288+
'step_w should be the same.')
289+
step_w = steps
290+
step_h = steps
291+
292+
box_results = []
293+
var_results = []
294+
for i, input in enumerate(inputs):
295+
min_size = min_sizes[i]
296+
max_size = max_sizes[i]
297+
aspect_ratio = []
298+
if not isinstance(min_size, list):
299+
min_size = [min_size]
300+
if not isinstance(max_size, list):
301+
max_size = [max_size]
302+
if aspect_ratios:
303+
aspect_ratio = aspect_ratios[i]
304+
if not isinstance(aspect_ratio, list):
305+
aspect_ratio = [aspect_ratio]
306+
307+
box, var = _prior_box_(input, image, min_size, max_size, aspect_ratio,
308+
variance, flip, clip, step_w[i]
309+
if step_w else 0.0, step_h[i]
310+
if step_w else 0.0, offset)
311+
312+
box_results.append(box)
313+
var_results.append(var)
314+
315+
if len(box_results) == 1:
316+
box = box_results[0]
317+
var = var_results[0]
318+
else:
319+
reshaped_boxes = []
320+
reshaped_vars = []
321+
for i in range(len(box_results)):
322+
reshaped_boxes.append(_reshape_with_axis_(box_results[i], axis=3))
323+
reshaped_vars.append(_reshape_with_axis_(var_results[i], axis=3))
324+
325+
box = concat(reshaped_boxes)
326+
var = concat(reshaped_vars)
327+
328+
return box, var

0 commit comments

Comments
 (0)