diff --git a/modules/dnn/src/caffe/caffe_importer.cpp b/modules/dnn/src/caffe/caffe_importer.cpp index 106793cd1f..c13d3a55cc 100644 --- a/modules/dnn/src/caffe/caffe_importer.cpp +++ b/modules/dnn/src/caffe/caffe_importer.cpp @@ -335,6 +335,28 @@ public: } continue; } + else if (type == "BatchNorm") + { + if (!layerParams.get("use_global_stats", true)) + { + CV_Assert(layer.bottom_size() == 1, layer.top_size() == 1); + + LayerParams mvnParams; + mvnParams.set("eps", layerParams.get("eps", 1e-5)); + std::string mvnName = name + "/mvn"; + + int repetitions = layerCounter[mvnName]++; + if (repetitions) + mvnName += String("_") + toString(repetitions); + + int mvnId = dstNet.addLayer(mvnName, "MVN", mvnParams); + addInput(layer.bottom(0), mvnId, 0, dstNet); + addOutput(layer, mvnId, 0); + net.mutable_layer(li)->set_bottom(0, layer.top(0)); + layerParams.blobs[0].setTo(0); // mean + layerParams.blobs[1].setTo(1); // std + } + } int id = dstNet.addLayer(name, type, layerParams); diff --git a/modules/dnn/src/layers/batch_norm_layer.cpp b/modules/dnn/src/layers/batch_norm_layer.cpp index 504d888a04..2a9e716676 100644 --- a/modules/dnn/src/layers/batch_norm_layer.cpp +++ b/modules/dnn/src/layers/batch_norm_layer.cpp @@ -36,6 +36,7 @@ public: hasWeights = params.get("has_weight", false); hasBias = params.get("has_bias", false); + useGlobalStats = params.get("use_global_stats", true); if(params.get("scale_bias", false)) hasWeights = hasBias = true; epsilon = params.get("eps", 1E-5); @@ -46,7 +47,7 @@ public: blobs[0].type() == CV_32F && blobs[1].type() == CV_32F); float varMeanScale = 1.f; - if (!hasWeights && !hasBias && blobs.size() > 2) { + if (!hasWeights && !hasBias && blobs.size() > 2 && useGlobalStats) { CV_Assert(blobs.size() == 3, blobs[2].type() == CV_32F); varMeanScale = blobs[2].at(0); if (varMeanScale != 0) @@ -100,6 +101,8 @@ public: std::vector &outputs, std::vector &internals) const { + if (!useGlobalStats && inputs[0][0] != 1) + CV_Error(Error::StsNotImplemented, "Batch normalization in training mode with batch size > 1"); Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals); return true; } @@ -304,6 +307,9 @@ public: } return flops; } + +private: + bool useGlobalStats; }; Ptr BatchNormLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/prior_box_layer.cpp b/modules/dnn/src/layers/prior_box_layer.cpp index d8ea5b6042..37a303b5ef 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -109,15 +109,11 @@ public: for (int i = 0; i < aspectRatioParameter.size(); ++i) { float aspectRatio = aspectRatioParameter.get(i); - bool alreadyExists = false; + bool alreadyExists = fabs(aspectRatio - 1.f) < 1e-6f; - for (size_t j = 0; j < _aspectRatios.size(); ++j) + for (size_t j = 0; j < _aspectRatios.size() && !alreadyExists; ++j) { - if (fabs(aspectRatio - _aspectRatios[j]) < 1e-6) - { - alreadyExists = true; - break; - } + alreadyExists = fabs(aspectRatio - _aspectRatios[j]) < 1e-6; } if (!alreadyExists) { @@ -215,7 +211,7 @@ public: } else { - CV_Assert(!_aspectRatios.empty(), _minSize > 0); + CV_Assert(_minSize > 0); _boxWidths.resize(1 + (_maxSize > 0 ? 1 : 0) + _aspectRatios.size()); _boxHeights.resize(_boxWidths.size()); _boxWidths[0] = _boxHeights[0] = _minSize; @@ -492,10 +488,12 @@ public: ieLayer->params["min_size"] = format("%f", _minSize); ieLayer->params["max_size"] = _maxSize > 0 ? format("%f", _maxSize) : ""; - CV_Assert(!_aspectRatios.empty()); - ieLayer->params["aspect_ratio"] = format("%f", _aspectRatios[0]); - for (int i = 1; i < _aspectRatios.size(); ++i) - ieLayer->params["aspect_ratio"] += format(",%f", _aspectRatios[i]); + if (!_aspectRatios.empty()) + { + ieLayer->params["aspect_ratio"] = format("%f", _aspectRatios[0]); + for (int i = 1; i < _aspectRatios.size(); ++i) + ieLayer->params["aspect_ratio"] += format(",%f", _aspectRatios[i]); + } ieLayer->params["flip"] = _flip ? "1" : "0"; ieLayer->params["clip"] = _clip ? "1" : "0"; diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 784c13c8b5..703f8320b2 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -252,6 +252,11 @@ TEST(Layer_Test_BatchNorm, Accuracy) testLayerUsingCaffeModels("layer_batch_norm", DNN_TARGET_CPU, true); } +TEST(Layer_Test_BatchNorm, local_stats) +{ + testLayerUsingCaffeModels("layer_batch_norm_local_stats", DNN_TARGET_CPU, true, false); +} + TEST(Layer_Test_ReLU, Accuracy) { testLayerUsingCaffeModels("layer_relu"); @@ -831,4 +836,33 @@ TEST(Layer_Test_Average_pooling_kernel_area, Accuracy) normAssert(out, blobFromImage(target)); } +// Test PriorBoxLayer in case of no aspect ratios (just squared proposals). +TEST(Layer_PriorBox, squares) +{ + LayerParams lp; + lp.name = "testPriorBox"; + lp.type = "PriorBox"; + lp.set("min_size", 32); + lp.set("flip", true); + lp.set("clip", true); + float variance[] = {0.1f, 0.1f, 0.2f, 0.2f}; + float aspectRatios[] = {1.0f}; // That should be ignored. + lp.set("variance", DictValue::arrayReal(&variance[0], 4)); + lp.set("aspect_ratio", DictValue::arrayReal(&aspectRatios[0], 1)); + + Net net; + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 0, id, 1); // The second input is an input image. Shapes are used for boxes normalization. + Mat inp(1, 2, CV_32F); + randu(inp, -1, 1); + net.setInput(blobFromImage(inp)); + Mat out = net.forward(); + + Mat target = (Mat_(4, 4) << -7.75f, -15.5f, 8.25f, 16.5f, + -7.25f, -15.5f, 8.75f, 16.5f, + 0.1f, 0.1f, 0.2f, 0.2f, + 0.1f, 0.1f, 0.2f, 0.2f); + normAssert(out.reshape(1, 4), target); +} + }} // namespace