From 172419ea1c25b1f65c9a3e9bcdbe86423b158f00 Mon Sep 17 00:00:00 2001 From: Vitaliy Lyudvichenko Date: Sun, 19 Jul 2015 21:31:22 +0300 Subject: [PATCH] Added Reshape layer and altered importer to correctly handle its params. --- modules/dnn/include/opencv2/dnn/blob.hpp | 7 +- modules/dnn/include/opencv2/dnn/blob.inl.hpp | 11 ++ modules/dnn/include/opencv2/dnn/dict.hpp | 2 +- modules/dnn/src/caffe_importer.cpp | 50 +++---- .../dnn/src/layers/deconvolution_layer.cpp | 0 modules/dnn/src/layers/elementwise_layers.cpp | 2 +- modules/dnn/src/layers/reshape_layer.cpp | 137 ++++++++++++++++++ modules/dnn/src/layers/slice_layer.cpp | 0 modules/dnn/src/layers/split_layer.cpp | 0 9 files changed, 174 insertions(+), 35 deletions(-) create mode 100644 modules/dnn/src/layers/deconvolution_layer.cpp create mode 100644 modules/dnn/src/layers/reshape_layer.cpp create mode 100644 modules/dnn/src/layers/slice_layer.cpp create mode 100644 modules/dnn/src/layers/split_layer.cpp diff --git a/modules/dnn/include/opencv2/dnn/blob.hpp b/modules/dnn/include/opencv2/dnn/blob.hpp index 929b709be..4a4a70597 100644 --- a/modules/dnn/include/opencv2/dnn/blob.hpp +++ b/modules/dnn/include/opencv2/dnn/blob.hpp @@ -10,7 +10,7 @@ namespace dnn { struct BlobShape { - explicit BlobShape(int ndims, int fill = 1); + explicit BlobShape(int ndims = 4, int fill = 1); BlobShape(int num, int cn, int rows, int cols); BlobShape(int ndims, const int *sizes); BlobShape(const std::vector &sizes); @@ -25,15 +25,16 @@ namespace dnn int operator[](int axis) const; int &operator[](int axis); + //same as size(), but size of non-existing dimensions equal to 1 int xsize(int axis) const; + ptrdiff_t total(); + const int *ptr() const; bool equal(const BlobShape &other) const; private: - - BlobShape(); cv::AutoBuffer sz; }; diff --git a/modules/dnn/include/opencv2/dnn/blob.inl.hpp b/modules/dnn/include/opencv2/dnn/blob.inl.hpp index 4f5866a5f..21aafe57d 100644 --- a/modules/dnn/include/opencv2/dnn/blob.inl.hpp +++ b/modules/dnn/include/opencv2/dnn/blob.inl.hpp @@ -79,6 +79,17 @@ inline int &BlobShape::operator[] (int axis) return sz[(axis < 0) ? axis + dims() : axis]; } +inline ptrdiff_t BlobShape::total() +{ + CV_Assert(dims() >= 1); + + ptrdiff_t res = 1; + for (int i = 0; i < dims(); i++) + res *= sz[i]; + return res; +} + + inline const int *BlobShape::ptr() const { return sz; diff --git a/modules/dnn/include/opencv2/dnn/dict.hpp b/modules/dnn/include/opencv2/dnn/dict.hpp index d192654bb..e23da12bd 100644 --- a/modules/dnn/include/opencv2/dnn/dict.hpp +++ b/modules/dnn/include/opencv2/dnn/dict.hpp @@ -274,7 +274,7 @@ inline bool DictValue::isReal() const return (type == Param::REAL || type == Param::INT); } -int DictValue::size() const +inline int DictValue::size() const { switch (type) { diff --git a/modules/dnn/src/caffe_importer.cpp b/modules/dnn/src/caffe_importer.cpp index d673cbc63..31dd41e2e 100644 --- a/modules/dnn/src/caffe_importer.cpp +++ b/modules/dnn/src/caffe_importer.cpp @@ -37,30 +37,6 @@ namespace ReadNetParamsFromBinaryFileOrDie(caffeModel, &netBinary); } - inline bool skipCaffeLayerParam(const FieldDescriptor *fd) - { - const std::string &name = fd->name(); - - if (fd->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) - { - static const char *SKIP_FIELDS[] = { "type", "name", "top", "bottom", NULL }; - - for (int i = 0; SKIP_FIELDS[i]; i++) - { - if (name == SKIP_FIELDS[i]) - return true; - } - - return false; - } - else - { - static const std::string _param("_param"); - bool endsWith_param = (name.size() >= _param.size()) && name.compare(name.size() - _param.size(), _param.size(), _param) == 0; - return !endsWith_param; - } - } - void addParam(const Message &msg, const FieldDescriptor *field, cv::dnn::LayerParams ¶ms) { const Reflection *refl = msg.GetReflection(); @@ -85,6 +61,12 @@ namespace case FieldDescriptor::CPPTYPE_UINT32: SET_UP_FILED(GetUInt32, DictValue::arrayInt, ::google::protobuf::uint32); break; + case FieldDescriptor::CPPTYPE_INT64: + SET_UP_FILED(GetInt32, DictValue::arrayInt, ::google::protobuf::int64); + break; + case FieldDescriptor::CPPTYPE_UINT64: + SET_UP_FILED(GetUInt32, DictValue::arrayInt, ::google::protobuf::uint64); + break; case FieldDescriptor::CPPTYPE_BOOL: SET_UP_FILED(GetBool, DictValue::arrayInt, bool); break; @@ -121,7 +103,13 @@ namespace } } - void extractLayerParams(const Message &msg, cv::dnn::LayerParams ¶ms) + inline static bool ends_with_param(const std::string &str) + { + static const std::string _param("_param"); + return (str.size() >= _param.size()) && str.compare(str.size() - _param.size(), _param.size(), _param) == 0; + } + + void extractLayerParams(const Message &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) { const Descriptor *msgDesc = msg.GetDescriptor(); const Reflection *msgRefl = msg.GetReflection(); @@ -130,19 +118,21 @@ namespace { const FieldDescriptor *fd = msgDesc->field(fieldId); + if (!isInternal && !ends_with_param(fd->name())) + continue; + bool hasData = fd->is_required() || - (fd->is_optional() && (msgRefl->HasField(msg, fd) /*|| fd->has_default_value()*/)) || + (fd->is_optional() && msgRefl->HasField(msg, fd)) || (fd->is_repeated() && msgRefl->FieldSize(msg, fd) > 0); - - if ( !hasData || skipCaffeLayerParam(fd) ) + if (!hasData) continue; if (fd->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (fd->is_repeated()) //Extract only first item! - extractLayerParams(msgRefl->GetRepeatedMessage(msg, fd, 0), params); + extractLayerParams(msgRefl->GetRepeatedMessage(msg, fd, 0), params, true); else - extractLayerParams(msgRefl->GetMessage(msg, fd), params); + extractLayerParams(msgRefl->GetMessage(msg, fd), params, true); } else { diff --git a/modules/dnn/src/layers/deconvolution_layer.cpp b/modules/dnn/src/layers/deconvolution_layer.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 39de20f76..02e63a23e 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -132,7 +132,7 @@ namespace dnn template inline TFloat operator()(TFloat x) { - return log((TFloat)1 + exp(x)); + return log((TFloat)1 + exp(-abs(x))); } }; diff --git a/modules/dnn/src/layers/reshape_layer.cpp b/modules/dnn/src/layers/reshape_layer.cpp new file mode 100644 index 000000000..2c7ba862d --- /dev/null +++ b/modules/dnn/src/layers/reshape_layer.cpp @@ -0,0 +1,137 @@ +#include "../precomp.hpp" +#include "layers_common.hpp" + +namespace cv +{ +namespace dnn +{ + +//TODO: Extend cv::Mat::reshape method +class ReshapeLayer : public Layer +{ +public: + ReshapeLayer(LayerParams ¶ms); + + void allocate(const std::vector &inputs, std::vector &outputs); + + void forward(std::vector&, std::vector&) {} + +protected: + BlobShape shapeDesc; + int inAxis, inNumAxes, autoAxisIdx; + + void computeOutputShape(int startAxis, int endAxis, BlobShape &inpShape, BlobShape &outShape); +}; + +ReshapeLayer::ReshapeLayer(LayerParams ¶ms) +{ + DictValue paramShape = params.get("dim"); + shapeDesc = BlobShape(paramShape.size()); + autoAxisIdx = -1; + + for (int i = 0; i < paramShape.size(); i++) + { + int dim = paramShape.get(i); + CV_Assert(dim >= -1); + + if (dim == -1) + { + if (autoAxisIdx != -1) + CV_Error(Error::StsBadArg, "New shape contains multiple -1 dims"); + autoAxisIdx = i; + } + + shapeDesc[i] = dim; + } + + inAxis = params.get("axis", 0); + inNumAxes = params.get("num_axes", -1); + CV_Assert(inNumAxes >= -1); +} + +void ReshapeLayer::allocate(const std::vector &inputs, std::vector &outputs) +{ + CV_Assert(inputs.size() == 1); + outputs.resize(1); + + Blob &inpBlob = *inputs[0]; + Blob &outBlob = outputs[0]; + BlobShape inpShape = inpBlob.shape(); + + int startAxis = (inAxis >= 0) ? inAxis : inpShape.dims() + 1 + inAxis; + int endAxis = (inNumAxes == -1) ? inpShape.dims() : startAxis + inNumAxes; + CV_Assert(0 <= startAxis && startAxis <= inpShape.dims()); + CV_Assert(0 <= endAxis && endAxis <= inpShape.dims()); + + int newDims = inpShape.dims() - (endAxis - startAxis) + shapeDesc.dims(); + BlobShape outShape(newDims); + + computeOutputShape(startAxis, endAxis, inpShape, outShape); + + outBlob.shareFrom(inpBlob); + outBlob.reshape(outShape); +} + +void ReshapeLayer::computeOutputShape(int startAxis, int endAxis, BlobShape &inpShape, BlobShape &outShape) +{ + int idx = 0; + for (int i = 0; i < startAxis; i++) + outShape[idx++] = inpShape[i]; + + for (int i = 0; i < shapeDesc.dims(); i++) + { + if (shapeDesc[i] == 0) + { + int inpAxisIdx = startAxis + i; + if (inpAxisIdx < 0 || inpShape.dims() <= inpAxisIdx) + CV_Error(Error::StsOutOfRange, "new shape contains a 0, but there was no corresponding bottom axis to copy"); + outShape[idx++] = inpShape[startAxis + i]; + } + else + { + outShape[idx++] = (shapeDesc[i] > 0) ? shapeDesc[i] : 1; + } + } + + for (int i = endAxis; i < inpShape.dims(); i++) + outShape[idx++] = inpShape[i]; + + if (autoAxisIdx >= 0) + { + size_t total = inpShape.total(); + size_t curTotal = 1; + for (int i = 0; i < outShape.dims(); i++) + { + if (i != startAxis + autoAxisIdx) + curTotal *= outShape[i]; + } + + CV_DbgAssert(curTotal <= total && total % curTotal == 0); + + outShape[startAxis + autoAxisIdx] = (int)(total / curTotal); + } + + if (inpShape.total() != outShape.total()) + { + CV_Error(Error::StsBadArg, "Mismatch between input and output blob elements count"); + } +} + + +Ptr createFlattenLayer(LayerParams&) +{ + LayerParams params; + + int shapeDesc[] = {0, -1}; + params.set("dim", DictValue::arrayInt(shapeDesc, 2)); + + return Ptr(new ReshapeLayer(params)); +} + + +REGISTER_LAYER_CLASS(Reshape, ReshapeLayer) +REGISTER_LAYER_FUNC(Flatten, createFlattenLayer) + + +} +} \ No newline at end of file diff --git a/modules/dnn/src/layers/slice_layer.cpp b/modules/dnn/src/layers/slice_layer.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/modules/dnn/src/layers/split_layer.cpp b/modules/dnn/src/layers/split_layer.cpp new file mode 100644 index 000000000..e69de29bb