Skip to content

Commit b53bdcd

Browse files
authored
Merge pull request #867 from Noplz/ssd
priorbox layer for Single Shot Multibox Detection Network
2 parents 28c5010 + 8d24931 commit b53bdcd

File tree

6 files changed

+446
-0
lines changed

6 files changed

+446
-0
lines changed

paddle/gserver/layers/PriorBox.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include "Layer.h"
16+
#include "paddle/math/BaseMatrix.h"
17+
#include "paddle/math/Matrix.h"
18+
19+
namespace paddle {
20+
/**
21+
* @brief A layer for generating priorbox locations and variances.
22+
* - Input: Two and only two input layer are accepted. The input layer must be
23+
* be a data output layer and a convolution output layer.
24+
* - Output: The priorbox locations and variances of the input data.
25+
* Reference:
26+
* Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed,
27+
* Cheng-Yang Fu, Alexander C. Berg. SSD: Single Shot MultiBox Detector
28+
*/
29+
30+
class PriorBoxLayer : public Layer {
31+
public:
32+
explicit PriorBoxLayer(const LayerConfig& config) : Layer(config) {}
33+
bool init(const LayerMap& layerMap, const ParameterMap& parameterMap);
34+
35+
void forward(PassType passType);
36+
void backward(const UpdateCallback& callback) {}
37+
38+
protected:
39+
int numPriors_;
40+
std::vector<int> minSize_;
41+
std::vector<int> maxSize_;
42+
std::vector<real> aspectRatio_;
43+
std::vector<real> variance_;
44+
MatrixPtr buffer_;
45+
};
46+
47+
bool PriorBoxLayer::init(const LayerMap& layerMap,
48+
const ParameterMap& parameterMap) {
49+
Layer::init(layerMap, parameterMap);
50+
auto pbConf = config_.inputs(0).priorbox_conf();
51+
std::copy(pbConf.min_size().begin(),
52+
pbConf.min_size().end(),
53+
std::back_inserter(minSize_));
54+
std::copy(pbConf.max_size().begin(),
55+
pbConf.max_size().end(),
56+
std::back_inserter(maxSize_));
57+
std::copy(pbConf.aspect_ratio().begin(),
58+
pbConf.aspect_ratio().end(),
59+
std::back_inserter(aspectRatio_));
60+
std::copy(pbConf.variance().begin(),
61+
pbConf.variance().end(),
62+
std::back_inserter(variance_));
63+
// flip
64+
int inputRatioLength = aspectRatio_.size();
65+
for (int index = 0; index < inputRatioLength; index++)
66+
aspectRatio_.push_back(1 / aspectRatio_[index]);
67+
aspectRatio_.push_back(1.);
68+
numPriors_ = aspectRatio_.size();
69+
if (maxSize_.size() > 0) numPriors_++;
70+
return true;
71+
}
72+
73+
void PriorBoxLayer::forward(PassType passType) {
74+
Layer::forward(passType);
75+
auto input = getInput(0);
76+
int layerWidth = input.getFrameWidth();
77+
int layerHeight = input.getFrameHeight();
78+
79+
auto image = getInput(1);
80+
int imageWidth = image.getFrameWidth();
81+
int imageHeight = image.getFrameHeight();
82+
83+
real stepW = static_cast<real>(imageWidth) / layerWidth;
84+
real stepH = static_cast<real>(imageHeight) / layerHeight;
85+
int dim = layerHeight * layerWidth * numPriors_ * 4;
86+
reserveOutput(1, dim * 2);
87+
// use a cpu buffer to compute
88+
Matrix::resizeOrCreate(buffer_, 1, dim * 2, false, false);
89+
auto* tmpPtr = buffer_->getData();
90+
91+
int idx = 0;
92+
for (int h = 0; h < layerHeight; ++h) {
93+
for (int w = 0; w < layerWidth; ++w) {
94+
real centerX = (w + 0.5) * stepW;
95+
real centerY = (h + 0.5) * stepH;
96+
int minSize = 0;
97+
for (size_t s = 0; s < minSize_.size(); s++) {
98+
// first prior.
99+
minSize = minSize_[s];
100+
int boxWidth = minSize;
101+
int boxHeight = minSize;
102+
// xmin, ymin, xmax, ymax.
103+
tmpPtr[idx++] = (centerX - boxWidth / 2.) / imageWidth;
104+
tmpPtr[idx++] = (centerY - boxHeight / 2.) / imageHeight;
105+
tmpPtr[idx++] = (centerX + boxWidth / 2.) / imageWidth;
106+
tmpPtr[idx++] = (centerY + boxHeight / 2.) / imageHeight;
107+
// set the variance.
108+
for (int t = 0; t < 4; t++) tmpPtr[idx++] = variance_[t];
109+
110+
if (maxSize_.size() > 0) {
111+
CHECK_EQ(minSize_.size(), maxSize_.size());
112+
// second prior.
113+
for (size_t s = 0; s < maxSize_.size(); s++) {
114+
int maxSize = maxSize_[s];
115+
boxWidth = boxHeight = sqrt(minSize * maxSize);
116+
tmpPtr[idx++] = (centerX - boxWidth / 2.) / imageWidth;
117+
tmpPtr[idx++] = (centerY - boxHeight / 2.) / imageHeight;
118+
tmpPtr[idx++] = (centerX + boxWidth / 2.) / imageWidth;
119+
tmpPtr[idx++] = (centerY + boxHeight / 2.) / imageHeight;
120+
// set the variance.
121+
for (int t = 0; t < 4; t++) tmpPtr[idx++] = variance_[t];
122+
}
123+
}
124+
}
125+
// rest of priors.
126+
for (size_t r = 0; r < aspectRatio_.size(); r++) {
127+
real ar = aspectRatio_[r];
128+
if (fabs(ar - 1.) < 1e-6) continue;
129+
real boxWidth = minSize * sqrt(ar);
130+
real boxHeight = minSize / sqrt(ar);
131+
tmpPtr[idx++] = (centerX - boxWidth / 2.) / imageWidth;
132+
tmpPtr[idx++] = (centerY - boxHeight / 2.) / imageHeight;
133+
tmpPtr[idx++] = (centerX + boxWidth / 2.) / imageWidth;
134+
tmpPtr[idx++] = (centerY + boxHeight / 2.) / imageHeight;
135+
// set the variance.
136+
for (int t = 0; t < 4; t++) tmpPtr[idx++] = variance_[t];
137+
}
138+
}
139+
}
140+
// clip the prior's coordidate such that it is within [0, 1]
141+
for (int d = 0; d < dim * 2; ++d)
142+
if ((d % 8) < 4)
143+
tmpPtr[d] = std::min(std::max(tmpPtr[d], (real)0.), (real)1.);
144+
MatrixPtr outV = getOutputValue();
145+
outV->copyFrom(buffer_->data_, dim * 2);
146+
}
147+
REGISTER_LAYER(priorbox, PriorBoxLayer);
148+
149+
} // namespace paddle

paddle/gserver/tests/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ add_unittest_without_exec(test_ConvTrans
3434

3535
add_test(NAME test_ConvTrans
3636
COMMAND test_ConvTrans)
37+
################# test_PriorBox #######################
38+
add_unittest_without_exec(test_PriorBox
39+
test_PriorBox.cpp
40+
LayerGradUtil.cpp
41+
TestUtil.cpp)
42+
43+
add_test(NAME test_PriorBox
44+
COMMAND test_PriorBox)
3745
################# test_ConvUnify #######################
3846
add_unittest_without_exec(test_ConvUnify
3947
test_ConvUnify.cpp
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License. */
14+
15+
#include <gtest/gtest.h>
16+
#include <string>
17+
#include <vector>
18+
19+
#include "LayerGradUtil.h"
20+
#include "TestUtil.h"
21+
22+
using namespace paddle; // NOLINT
23+
using namespace std; // NOLINT
24+
25+
// Do one forward pass of priorBox layer and check to see if its output
26+
// matches the given result
27+
void doOnePriorBoxTest(size_t feature_map_width,
28+
size_t feature_map_height,
29+
size_t image_width,
30+
size_t image_height,
31+
vector<int> min_size,
32+
vector<int> max_size,
33+
vector<real> aspect_ratio,
34+
vector<real> variance,
35+
bool use_gpu,
36+
MatrixPtr& result) {
37+
// Setting up the priorbox layer
38+
TestConfig configt;
39+
configt.layerConfig.set_type("priorbox");
40+
41+
configt.inputDefs.push_back({INPUT_DATA, "featureMap", 1, 0});
42+
LayerInputConfig* input = configt.layerConfig.add_inputs();
43+
configt.inputDefs.push_back({INPUT_DATA, "image", 1, 0});
44+
configt.layerConfig.add_inputs();
45+
PriorBoxConfig* pb = input->mutable_priorbox_conf();
46+
for (size_t i = 0; i < min_size.size(); i++) pb->add_min_size(min_size[i]);
47+
for (size_t i = 0; i < max_size.size(); i++) pb->add_max_size(max_size[i]);
48+
for (size_t i = 0; i < variance.size(); i++) pb->add_variance(variance[i]);
49+
for (size_t i = 0; i < aspect_ratio.size(); i++)
50+
pb->add_aspect_ratio(aspect_ratio[i]);
51+
52+
// data layer initialize
53+
std::vector<DataLayerPtr> dataLayers;
54+
LayerMap layerMap;
55+
vector<Argument> datas;
56+
initDataLayer(
57+
configt, &dataLayers, &datas, &layerMap, "priorbox", 1, false, use_gpu);
58+
dataLayers[0]->getOutput().setFrameHeight(feature_map_height);
59+
dataLayers[0]->getOutput().setFrameWidth(feature_map_width);
60+
dataLayers[1]->getOutput().setFrameHeight(image_height);
61+
dataLayers[1]->getOutput().setFrameWidth(image_width);
62+
63+
// test layer initialize
64+
std::vector<ParameterPtr> parameters;
65+
LayerPtr priorboxLayer;
66+
initTestLayer(configt, &layerMap, &parameters, &priorboxLayer);
67+
priorboxLayer->forward(PASS_GC);
68+
checkMatrixEqual(priorboxLayer->getOutputValue(), result);
69+
}
70+
71+
TEST(Layer, priorBoxLayerFwd) {
72+
vector<int> minSize;
73+
vector<int> maxSize;
74+
vector<real> aspectRatio;
75+
vector<real> variance;
76+
bool useGpu = false;
77+
78+
minSize.push_back(276);
79+
maxSize.push_back(330);
80+
variance.push_back(0.1);
81+
variance.push_back(0.1);
82+
variance.push_back(0.2);
83+
variance.push_back(0.2);
84+
85+
// CPU case 1.
86+
MatrixPtr result;
87+
real resultData[] = {0.04,
88+
0.04,
89+
0.96,
90+
0.96,
91+
0.1,
92+
0.1,
93+
0.2,
94+
0.2,
95+
0,
96+
0,
97+
1,
98+
1,
99+
0.1,
100+
0.1,
101+
0.2,
102+
0.2};
103+
result = Matrix::create(1, 2 * 8, false, useGpu);
104+
result->setData(resultData);
105+
doOnePriorBoxTest(/* feature_map_width */ 1,
106+
/* feature_map_height */ 1,
107+
/* image_width */ 300,
108+
/* image_height */ 300,
109+
minSize,
110+
maxSize,
111+
aspectRatio,
112+
variance,
113+
useGpu,
114+
result);
115+
// CPU case 2.
116+
variance[1] = 0.2;
117+
variance[3] = 0.1;
118+
maxSize.pop_back();
119+
real resultData2[] = {0, 0, 0.595, 0.595, 0.1, 0.2, 0.2, 0.1,
120+
0.405, 0, 1, 0.595, 0.1, 0.2, 0.2, 0.1,
121+
0, 0.405, 0.595, 1, 0.1, 0.2, 0.2, 0.1,
122+
0.405, 0.405, 1, 1, 0.1, 0.2, 0.2, 0.1};
123+
Matrix::resizeOrCreate(result, 1, 4 * 8, false, useGpu);
124+
result->setData(resultData2);
125+
doOnePriorBoxTest(/* feature_map_width */ 2,
126+
/* feature_map_height */ 2,
127+
/* image_width */ 400,
128+
/* image_height */ 400,
129+
minSize,
130+
maxSize,
131+
aspectRatio,
132+
variance,
133+
useGpu,
134+
result);
135+
// CPU case 3.
136+
aspectRatio.push_back(2);
137+
real resultData3[] = {0.04, 0.04, 0.96, 0.96, 0.1, 0.2,
138+
0.2, 0.1, 0, 0.17473088, 1, 0.825269,
139+
0.1, 0.2, 0.2, 0.1, 0.17473088, 0,
140+
0.825269, 1, 0.1, 0.2, 0.2, 0.1};
141+
Matrix::resizeOrCreate(result, 1, 3 * 8, false, useGpu);
142+
result->setData(resultData3);
143+
doOnePriorBoxTest(/* feature_map_width */ 1,
144+
/* feature_map_height */ 1,
145+
/* image_width */ 300,
146+
/* image_height */ 300,
147+
minSize,
148+
maxSize,
149+
aspectRatio,
150+
variance,
151+
useGpu,
152+
result);
153+
154+
#ifndef PADDLE_ONLY_CPU
155+
// reset the input parameters
156+
variance[1] = 0.1;
157+
variance[3] = 0.2;
158+
maxSize.push_back(330);
159+
aspectRatio.pop_back();
160+
MatrixPtr resultGpu;
161+
useGpu = true;
162+
// GPU case 1.
163+
resultGpu = Matrix::create(1, 2 * 8, false, useGpu);
164+
resultGpu->copyFrom(resultData, 2 * 8);
165+
doOnePriorBoxTest(/* feature_map_width */ 1,
166+
/* feature_map_height */ 1,
167+
/* image_width */ 300,
168+
/* image_height */ 300,
169+
minSize,
170+
maxSize,
171+
aspectRatio,
172+
variance,
173+
useGpu,
174+
resultGpu);
175+
// GPU case 2.
176+
variance[1] = 0.2;
177+
variance[3] = 0.1;
178+
maxSize.pop_back();
179+
Matrix::resizeOrCreate(resultGpu, 1, 4 * 8, false, useGpu);
180+
resultGpu->copyFrom(resultData2, 4 * 8);
181+
doOnePriorBoxTest(/* feature_map_width */ 2,
182+
/* feature_map_height */ 2,
183+
/* image_width */ 400,
184+
/* image_height */ 400,
185+
minSize,
186+
maxSize,
187+
aspectRatio,
188+
variance,
189+
useGpu,
190+
resultGpu);
191+
// GPU case 3.
192+
aspectRatio.push_back(2);
193+
Matrix::resizeOrCreate(resultGpu, 1, 3 * 8, false, useGpu);
194+
resultGpu->copyFrom(resultData3, 3 * 8);
195+
doOnePriorBoxTest(/* feature_map_width */ 1,
196+
/* feature_map_height */ 1,
197+
/* image_width */ 300,
198+
/* image_height */ 300,
199+
minSize,
200+
maxSize,
201+
aspectRatio,
202+
variance,
203+
useGpu,
204+
resultGpu);
205+
#endif
206+
}
207+
208+
int main(int argc, char** argv) {
209+
testing::InitGoogleTest(&argc, argv);
210+
initMain(argc, argv);
211+
return RUN_ALL_TESTS();
212+
}

proto/ModelConfig.proto

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,13 @@ message ImageConfig {
248248
optional uint32 img_size_y = 9;
249249
}
250250

251+
message PriorBoxConfig {
252+
repeated uint32 min_size = 1;
253+
repeated uint32 max_size = 2;
254+
repeated float aspect_ratio = 3;
255+
repeated float variance = 4;
256+
}
257+
251258
message LayerInputConfig {
252259
required string input_layer_name = 1;
253260
optional string input_parameter_name = 2;
@@ -263,6 +270,7 @@ message LayerInputConfig {
263270
optional BilinearInterpConfig bilinear_interp_conf = 10;
264271
optional MaxOutConfig maxout_conf = 11;
265272
optional SppConfig spp_conf = 12;
273+
optional PriorBoxConfig priorbox_conf = 13;
266274
}
267275

268276
message LayerConfig {

0 commit comments

Comments
 (0)