Skip to content

Commit 8b16817

Browse files
committed
Merge pull request opencv#9636 from dkurt:duplicate_lp_norm_layer
2 parents 0873ebb + 905a9da commit 8b16817

File tree

5 files changed

+72
-218
lines changed

5 files changed

+72
-218
lines changed

modules/dnn/include/opencv2/dnn/all_layers.hpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,6 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
263263
static Ptr<SoftmaxLayer> create(const LayerParams& params);
264264
};
265265

266-
class CV_EXPORTS LPNormalizeLayer : public Layer
267-
{
268-
public:
269-
float pnorm, epsilon;
270-
271-
static Ptr<LPNormalizeLayer> create(const LayerParams& params);
272-
};
273-
274266
class CV_EXPORTS InnerProductLayer : public Layer
275267
{
276268
public:
@@ -545,9 +537,37 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
545537
static Ptr<DetectionOutputLayer> create(const LayerParams& params);
546538
};
547539

540+
/**
541+
* @brief \f$ L_p \f$ - normalization layer.
542+
* @param p Normalization factor. The most common `p = 1` for \f$ L_1 \f$ -
543+
* normalization or `p = 2` for \f$ L_2 \f$ - normalization or a custom one.
544+
* @param eps Parameter \f$ \epsilon \f$ to prevent a division by zero.
545+
* @param across_spatial If true, normalize an input across all non-batch dimensions.
546+
* Otherwise normalize an every channel separately.
547+
*
548+
* Across spatial:
549+
* @f[
550+
* norm = \sqrt[p]{\epsilon + \sum_{x, y, c} |src(x, y, c)|^p } \\
551+
* dst(x, y, c) = \frac{ src(x, y, c) }{norm}
552+
* @f]
553+
*
554+
* Channel wise normalization:
555+
* @f[
556+
* norm(c) = \sqrt[p]{\epsilon + \sum_{x, y} |src(x, y, c)|^p } \\
557+
* dst(x, y, c) = \frac{ src(x, y, c) }{norm(c)}
558+
* @f]
559+
*
560+
* Where `x, y` - spatial cooridnates, `c` - channel.
561+
*
562+
* An every sample in the batch is normalized separately. Optionally,
563+
* output is scaled by the trained parameters.
564+
*/
548565
class NormalizeBBoxLayer : public Layer
549566
{
550567
public:
568+
float pnorm, epsilon;
569+
bool acrossSpatial;
570+
551571
static Ptr<NormalizeBBoxLayer> create(const LayerParams& params);
552572
};
553573

modules/dnn/src/init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ void initializeLayerFactory()
9292
CV_DNN_REGISTER_LAYER_CLASS(InnerProduct, InnerProductLayer);
9393
CV_DNN_REGISTER_LAYER_CLASS(Softmax, SoftmaxLayer);
9494
CV_DNN_REGISTER_LAYER_CLASS(MVN, MVNLayer);
95-
CV_DNN_REGISTER_LAYER_CLASS(LPNormalize, LPNormalizeLayer);
9695

9796
CV_DNN_REGISTER_LAYER_CLASS(ReLU, ReLULayer);
9897
CV_DNN_REGISTER_LAYER_CLASS(ReLU6, ReLU6Layer);

modules/dnn/src/layers/lp_normalize_layer.cpp

Lines changed: 0 additions & 78 deletions
This file was deleted.

modules/dnn/src/layers/normalize_bbox_layer.cpp

Lines changed: 43 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -43,178 +43,91 @@
4343
#include "../precomp.hpp"
4444
#include "layers_common.hpp"
4545

46-
#include <float.h>
47-
#include <algorithm>
48-
49-
namespace cv
50-
{
51-
namespace dnn
52-
{
53-
54-
namespace
55-
{
56-
const std::string layerName = "NormalizeBBox";
57-
}
46+
namespace cv { namespace dnn {
5847

5948
class NormalizeBBoxLayerImpl : public NormalizeBBoxLayer
6049
{
61-
float _eps;
62-
bool _across_spatial;
63-
bool _channel_shared;
6450
public:
65-
bool getParameterDict(const LayerParams &params,
66-
const std::string &parameterName,
67-
DictValue& result)
68-
{
69-
if (!params.has(parameterName))
70-
{
71-
return false;
72-
}
73-
74-
result = params.get(parameterName);
75-
return true;
76-
}
77-
78-
template<typename T>
79-
T getParameter(const LayerParams &params,
80-
const std::string &parameterName,
81-
const size_t &idx=0,
82-
const bool required=true,
83-
const T& defaultValue=T())
51+
NormalizeBBoxLayerImpl(const LayerParams& params)
8452
{
85-
DictValue dictValue;
86-
bool success = getParameterDict(params, parameterName, dictValue);
87-
if(!success)
88-
{
89-
if(required)
90-
{
91-
std::string message = layerName;
92-
message += " layer parameter does not contain ";
93-
message += parameterName;
94-
message += " parameter.";
95-
CV_Error(Error::StsBadArg, message);
96-
}
97-
else
98-
{
99-
return defaultValue;
100-
}
101-
}
102-
return dictValue.get<T>(idx);
103-
}
104-
105-
NormalizeBBoxLayerImpl(const LayerParams &params)
106-
{
107-
_eps = getParameter<float>(params, "eps", 0, false, 1e-10f);
108-
_across_spatial = getParameter<bool>(params, "across_spatial");
109-
_channel_shared = getParameter<bool>(params, "channel_shared");
11053
setParamsFrom(params);
111-
}
112-
113-
void checkInputs(const std::vector<Mat*> &inputs)
114-
{
115-
CV_Assert(inputs.size() > 0);
116-
CV_Assert(inputs[0]->dims == 4 && inputs[0]->type() == CV_32F);
117-
for (size_t i = 1; i < inputs.size(); i++)
118-
{
119-
CV_Assert(inputs[i]->dims == 4 && inputs[i]->type() == CV_32F);
120-
CV_Assert(inputs[i]->size == inputs[0]->size);
121-
}
122-
CV_Assert(inputs[0]->dims > 2);
54+
pnorm = params.get<float>("p", 2);
55+
epsilon = params.get<float>("eps", 1e-10f);
56+
acrossSpatial = params.get<bool>("across_spatial", true);
57+
CV_Assert(pnorm > 0);
12358
}
12459

12560
bool getMemoryShapes(const std::vector<MatShape> &inputs,
12661
const int requiredOutputs,
12762
std::vector<MatShape> &outputs,
12863
std::vector<MatShape> &internals) const
12964
{
130-
bool inplace = Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
131-
size_t channels = inputs[0][1];
132-
size_t rows = inputs[0][2];
133-
size_t cols = inputs[0][3];
134-
size_t channelSize = rows * cols;
135-
136-
internals.assign(1, shape(channels, channelSize));
137-
internals.push_back(shape(channels, 1));
138-
internals.push_back(shape(1, channelSize));
139-
140-
return inplace;
65+
CV_Assert(inputs.size() == 1);
66+
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
67+
internals.resize(1, inputs[0]);
68+
internals[0][0] = 1; // Batch size.
69+
return true;
14170
}
14271

14372
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
14473
{
14574
CV_TRACE_FUNCTION();
14675
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
14776

148-
checkInputs(inputs);
149-
150-
Mat& buffer = internals[0], sumChannelMultiplier = internals[1],
151-
sumSpatialMultiplier = internals[2];
152-
153-
sumChannelMultiplier.setTo(1.0);
154-
sumSpatialMultiplier.setTo(1.0);
77+
CV_Assert(inputs.size() == 1 && outputs.size() == 1);
78+
CV_Assert(inputs[0]->total() == outputs[0].total());
15579

15680
const Mat& inp0 = *inputs[0];
81+
Mat& buffer = internals[0];
15782
size_t num = inp0.size[0];
15883
size_t channels = inp0.size[1];
159-
size_t channelSize = inp0.size[2] * inp0.size[3];
160-
161-
Mat zeroBuffer(channels, channelSize, CV_32F, Scalar(0));
162-
Mat absDiff;
163-
Mat scale = blobs[0];
164-
for (size_t j = 0; j < inputs.size(); j++)
84+
size_t channelSize = inp0.total() / (num * channels);
85+
for (size_t n = 0; n < num; ++n)
16586
{
166-
for (size_t n = 0; n < num; ++n)
167-
{
168-
Mat src = Mat(channels, channelSize, CV_32F, inputs[j]->ptr<float>(n));
169-
Mat dst = Mat(channels, channelSize, CV_32F, outputs[j].ptr<float>(n));
170-
171-
buffer = src.mul(src);
172-
173-
if (_across_spatial)
174-
{
175-
absdiff(buffer, zeroBuffer, absDiff);
176-
177-
// add eps to avoid overflow
178-
double absSum = sum(absDiff)[0] + _eps;
87+
Mat src = Mat(channels, channelSize, CV_32F, (void*)inp0.ptr<float>(n));
88+
Mat dst = Mat(channels, channelSize, CV_32F, (void*)outputs[0].ptr<float>(n));
17989

180-
float norm = sqrt(absSum);
181-
dst = src / norm;
182-
}
183-
else
184-
{
185-
Mat norm(channelSize, 1, buffer.type()); // 1 x channelSize
186-
187-
// (_channels x channelSize)T * _channels x 1 -> channelSize x 1
188-
gemm(buffer, sumChannelMultiplier, 1, norm, 0, norm, GEMM_1_T);
90+
cv::pow(abs(src), pnorm, buffer);
18991

190-
// compute norm
191-
pow(norm, 0.5f, norm);
92+
if (acrossSpatial)
93+
{
94+
// add eps to avoid overflow
95+
float absSum = sum(buffer)[0] + epsilon;
96+
float norm = pow(absSum, 1.0f / pnorm);
97+
multiply(src, 1.0f / norm, dst);
98+
}
99+
else
100+
{
101+
Mat norm;
102+
reduce(buffer, norm, 0, REDUCE_SUM);
103+
norm += epsilon;
192104

193-
// scale the layer
194-
// _channels x 1 * (channelSize x 1)T -> _channels x channelSize
195-
gemm(sumChannelMultiplier, norm, 1, buffer, 0, buffer, GEMM_2_T);
105+
// compute inverted norm to call multiply instead divide
106+
cv::pow(norm, -1.0f / pnorm, norm);
196107

197-
dst = src / buffer;
198-
}
108+
repeat(norm, channels, 1, buffer);
109+
multiply(src, buffer, dst);
110+
}
199111

112+
if (!blobs.empty())
113+
{
200114
// scale the output
201-
if (_channel_shared)
115+
Mat scale = blobs[0];
116+
if (scale.total() == 1)
202117
{
203118
// _scale: 1 x 1
204119
dst *= scale.at<float>(0, 0);
205120
}
206121
else
207122
{
208123
// _scale: _channels x 1
209-
// _channels x 1 * 1 x channelSize -> _channels x channelSize
210-
gemm(scale, sumSpatialMultiplier, 1, buffer, 0, buffer);
211-
212-
dst = dst.mul(buffer);
124+
CV_Assert(scale.total() == channels);
125+
repeat(scale, 1, dst.cols, buffer);
126+
multiply(dst, buffer, dst);
213127
}
214128
}
215129
}
216130
}
217-
218131
};
219132

220133

modules/dnn/src/torch/torch_importer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ struct TorchImporter : public ::cv::dnn::Importer
706706
if (scalarParams.has("eps"))
707707
layerParams.set("eps", scalarParams.get<float>("eps"));
708708

709-
newModule->apiType = "LPNormalize";
709+
newModule->apiType = "Normalize";
710710
curModule->modules.push_back(newModule);
711711
}
712712
else if (nnName == "Padding")

0 commit comments

Comments
 (0)