diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 0a940c753e..83d9a39765 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -135,6 +135,8 @@ namespace dnn //! This namespace is used for dnn module functionlaity. const int requiredOutputs, std::vector &outputs, std::vector &internals) const; + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const {(void)inputs; (void)outputs; return 0;} CV_PROP String name; //!< Name of the layer instance, can be used for logging or other internal purposes. CV_PROP String type; //!< Type name which was used for creating layer by layer factory. @@ -323,6 +325,50 @@ namespace dnn //! This namespace is used for dnn module functionlaity. const int layerId, std::vector* inLayerShapes, std::vector* outLayerShapes) const; + /** @brief Computes FLOP for whole loaded model with specified input shapes. + * @param netInputShapes vector of shapes for all net inputs. + * @returns computed FLOP. + */ + CV_WRAP int64 getFLOPS(const std::vector& netInputShapes) const; + /** @overload */ + CV_WRAP int64 getFLOPS(const MatShape& netInputShape) const; + /** @overload */ + CV_WRAP int64 getFLOPS(const int layerId, + const std::vector& netInputShapes) const; + /** @overload */ + CV_WRAP int64 getFLOPS(const int layerId, + const MatShape& netInputShape) const; + + /** @brief Returns list of types for layer used in model. + * @param layersTypes output parameter for returning types. + */ + CV_WRAP void getLayerTypes(std::vector& layersTypes) const; + + /** @brief Returns count of layers of specified type. + * @param layerType type. + * @returns count of layers + */ + CV_WRAP int getLayersCount(const String& layerType) const; + + /** @brief Computes bytes number which are requered to store + * all weights and intermediate blobs for model. + * @param netInputShapes vector of shapes for all net inputs. + * @param weights output parameter to store resulting bytes for weights. + * @param blobs output parameter to store resulting bytes for intermediate blobs. + */ + CV_WRAP void getMemoryConsumption(const std::vector& netInputShapes, + size_t& weights, size_t& blobs) const; + /** @overload */ + CV_WRAP void getMemoryConsumption(const MatShape& netInputShape, + size_t& weights, size_t& blobs) const; + /** @overload */ + CV_WRAP void getMemoryConsumption(const int layerId, + const std::vector& netInputShapes, + size_t& weights, size_t& blobs) const; + /** @overload */ + CV_WRAP void getMemoryConsumption(const int layerId, + const MatShape& netInputShape, + size_t& weights, size_t& blobs) const; private: struct Impl; diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 34494223e1..797b796049 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -876,6 +876,144 @@ void Net::getLayerShapes(const Net::Impl::ShapesVec& netInputShapes, *outLayerShapes = shapes.out; } +int64 Net::getFLOPS(const std::vector& netInputShapes) const +{ + int64 flops = 0; + std::vector ids; + std::vector > inShapes, outShapes; + getLayersShapes(netInputShapes, &ids, &inShapes, &outShapes); + CV_Assert(inShapes.size() == outShapes.size()); + CV_Assert(inShapes.size() == ids.size()); + + for(int i = 0; i < ids.size(); i++) + { + flops += impl->layers[ids[i]].getLayerInstance()->getFLOPS(inShapes[i], + outShapes[i]); + } + + return flops; +} + +int64 Net::getFLOPS(const MatShape& netInputShape) const +{ + return getFLOPS(std::vector(1, netInputShape)); +} + +int64 Net::getFLOPS(const int layerId, + const std::vector& netInputShapes) const +{ + Impl::MapIdToLayerData::iterator layer = impl->layers.find(layerId); + CV_Assert(layer != impl->layers.end()); + + Impl::LayerShapes shapes; + impl->getLayerShapes(netInputShapes, layerId, shapes); + + return layer->second.getLayerInstance()->getFLOPS(shapes.in, shapes.out); +} + +int64 Net::getFLOPS(const int layerId, + const MatShape& netInputShape) const +{ + return getFLOPS(layerId, std::vector(1, netInputShape)); +} + +void Net::getLayerTypes(std::vector& layersTypes) const +{ + layersTypes.clear(); + + std::map layers; + for (Impl::MapIdToLayerData::iterator it = impl->layers.begin(); + it != impl->layers.end(); it++) + { + if (layers.find(it->second.type) == layers.end()) + layers[it->second.type] = 0; + layers[it->second.type]++; + } + + for (std::map::iterator it = layers.begin(); + it != layers.end(); it++) + { + layersTypes.push_back(it->first); + } +} + +int Net::getLayersCount(const String& layerType) const +{ + int count = 0; + for (Impl::MapIdToLayerData::iterator it = impl->layers.begin(); + it != impl->layers.end(); it++) + { + if (it->second.type == layerType) + count++; + } + return count; +} + +void Net::getMemoryConsumption(const int layerId, + const std::vector& netInputShapes, + size_t& weights, size_t& blobs) const +{ + Impl::MapIdToLayerData::iterator layer = impl->layers.find(layerId); + CV_Assert(layer != impl->layers.end()); + + weights = blobs = 0; + + for(int i = 0; i < layer->second.params.blobs.size(); i++) + { + const Mat& weightsBlob = layer->second.params.blobs[i]; + weights += weightsBlob.total()*weightsBlob.elemSize(); + } + + std::vector outLayerShapes; + getLayerShapes(netInputShapes, layerId, 0, &outLayerShapes); + for(int i = 0; i < outLayerShapes.size(); i++) + { + blobs += total(outLayerShapes[i]) * sizeof(float); + } +} + +void Net::getMemoryConsumption(const std::vector& netInputShapes, + size_t& weights, size_t& blobs) const +{ + std::vector layerIds; + std::vector > outLayerShapes; + + getLayersShapes(netInputShapes, &layerIds, 0, &outLayerShapes); + + weights = blobs = 0; + for(int i = 0; i < layerIds.size(); i++) + { + Impl::MapIdToLayerData::iterator layer = impl->layers.find(layerIds[i]); + CV_Assert(layer != impl->layers.end()); + + for(int j = 0; j < layer->second.params.blobs.size(); j++) + { + const Mat& weightsBlob = layer->second.params.blobs[j]; + weights += weightsBlob.total()*weightsBlob.elemSize(); + } + + for(int j = 0; j < outLayerShapes[i].size(); j++) + { + blobs += total(outLayerShapes[i][j]) * sizeof(float); + } + } +} + +void Net::getMemoryConsumption(const int layerId, + const MatShape& netInputShape, + size_t& weights, size_t& blobs) const +{ + getMemoryConsumption(layerId, std::vector(1, netInputShape), + weights, blobs); +} + +void Net::getMemoryConsumption(const MatShape& netInputShape, + size_t& weights, size_t& blobs) const +{ + getMemoryConsumption(std::vector(1, netInputShape), + weights, blobs); +} + ////////////////////////////////////////////////////////////////////////// Importer::~Importer() {} diff --git a/modules/dnn/src/layers/batch_norm_layer.cpp b/modules/dnn/src/layers/batch_norm_layer.cpp index ba634753e2..37959f65ab 100644 --- a/modules/dnn/src/layers/batch_norm_layer.cpp +++ b/modules/dnn/src/layers/batch_norm_layer.cpp @@ -10,6 +10,7 @@ Implementation of Batch Normalization layer. */ #include "../precomp.hpp" +#include namespace cv { @@ -78,6 +79,19 @@ class BatchNormLayerImpl : public BatchNormLayer } } + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + + int64 flops = 0; + for(int i = 0; i < inputs.size(); i++) + { + flops += 3*total(inputs[i]); + } + return flops; + } + bool hasWeights, hasBias; float epsilon; }; diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index e368990d63..1dc3db49b7 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -224,6 +224,20 @@ class ConvolutionLayerImpl : public BaseConvolutionLayerImpl dilation.height, dilation.width, outH, outW, dstRow.ptr()); } } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + CV_Assert(inputs.size() == outputs.size()); + + int64 flops = 0; + for (int i = 0; i < inputs.size(); i++) + { + flops += total(outputs[i])*(2*kernel.area()*inputs[i][1] + 1); + } + + return flops; + } }; class DeConvolutionLayerImpl : public BaseConvolutionLayerImpl @@ -339,6 +353,22 @@ class DeConvolutionLayerImpl : public BaseConvolutionLayerImpl dilation.height, dilation.width, dstImg.ptr(), &ofsbuf[0]); } + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + CV_Assert(inputs.size() == outputs.size()); + + float flops = 0; + int outChannels = blobs[0].size[0]; + + for (int i = 0; i < inputs.size(); i++) + { + flops += 2*outChannels*kernel.area()*total(inputs[i]); + } + + return flops; + } + std::vector ofsbuf; }; diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 0ffa4b855f..cf5bcb1bf3 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -63,6 +63,17 @@ class ElementWiseLayer : public Func::Layer } } + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + long flops = 0; + for (int i = 0; i < outputs.size(); i++) + { + flops += total(outputs[i]) * func.getFLOPSPerElement(); + } + return flops; + } + Func func; bool run_parallel; }; @@ -79,6 +90,8 @@ struct ReLUFunctor { return (x >= (TFloat)0) ? x : (TFloat)slope * x; } + + int64 getFLOPSPerElement() const {return 1;} }; struct TanHFunctor @@ -90,6 +103,8 @@ struct TanHFunctor { return tanh(x); } + + int64 getFLOPSPerElement() const {return 1;} }; struct SigmoidFunctor @@ -101,6 +116,8 @@ struct SigmoidFunctor { return (TFloat)1 / ((TFloat)1 + exp(-x)); } + + int64 getFLOPSPerElement() const {return 3;} }; struct AbsValFunctor @@ -112,6 +129,8 @@ struct AbsValFunctor { return abs(x); } + + int64 getFLOPSPerElement() const {return 1;} }; struct BNLLFunctor @@ -123,6 +142,8 @@ struct BNLLFunctor { return log((TFloat)1 + exp(-abs(x))); } + + int64 getFLOPSPerElement() const {return 5;} }; struct PowerFunctor @@ -141,6 +162,8 @@ struct PowerFunctor { return pow((TFloat)shift + (TFloat)scale * x, (TFloat)power); } + + int64 getFLOPSPerElement() const {return 3;} }; struct PowerFunctor1 @@ -158,6 +181,8 @@ struct PowerFunctor1 { return (TFloat)shift + (TFloat)scale * x; } + + int64 getFLOPSPerElement() const {return 2;} }; class ChannelsPReLULayerImpl : public ChannelsPReLULayer @@ -210,6 +235,20 @@ class ChannelsPReLULayerImpl : public ChannelsPReLULayer } } } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)inputs; // suppress unused variable warning + long flops = 0; + + for (int i = 0; i < outputs.size(); i++) + { + flops += total(outputs[i]) * 3; + } + + return flops; + } }; #define ACTIVATION_CREATOR_FOR(_Layer, _Functor, ...) \ diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index 0788bd89ac..3d3e64e40f 100755 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -143,6 +143,17 @@ class EltwiseLayerImpl : public EltwiseLayer break; } } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + CV_Assert(inputs.size()); + + long flops = inputs.size() * total(inputs[0]); + + return flops; + } }; Ptr EltwiseLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 2f611135a7..1915851544 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -117,6 +117,22 @@ class FullyConnectedLayerImpl : public InnerProductLayer } } + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)inputs; // suppress unused variable warning + long flops = 0; + + int innerSize = blobs[0].size[1]; + for(int i = 0; i < outputs.size(); i++) + { + flops += 3*innerSize*total(outputs[i]); + } + + return flops; + + } + bool bias; }; diff --git a/modules/dnn/src/layers/lrn_layer.cpp b/modules/dnn/src/layers/lrn_layer.cpp index ac703fb2f4..bff39f62c5 100644 --- a/modules/dnn/src/layers/lrn_layer.cpp +++ b/modules/dnn/src/layers/lrn_layer.cpp @@ -171,6 +171,35 @@ class LRNLayerImpl : public LRNLayer } } } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + CV_Assert(inputs.size() > 0); + long flops = 0; + + for(int i = 0; i < inputs.size(); i++) + { + if (type == CHANNEL_NRM) + { + int channels = inputs[i][1]; + int ksize = (size - 1) / 2; + + flops += inputs[i][0]*(std::min(ksize, channels)*2*total(inputs[i], 2) + channels*4*total(inputs[i], 2)); + + if (ksize < channels) + { + flops += (size + 2*(channels - size))*total(inputs[i], 2); + } + } + else + { + flops += total(inputs[i])*(2*size*size + 2); + } + } + return flops; + } }; Ptr LRNLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/mvn_layer.cpp b/modules/dnn/src/layers/mvn_layer.cpp index 7e3f763337..e5bdf9ba78 100644 --- a/modules/dnn/src/layers/mvn_layer.cpp +++ b/modules/dnn/src/layers/mvn_layer.cpp @@ -85,6 +85,18 @@ class MVNLayerImpl : public MVNLayer } } } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + long flops = 0; + for(int i = 0; i < inputs.size(); i++) + { + flops += 6*total(inputs[i]) + 3*total(inputs[i], 0, normVariance ? 2 : 1); + } + return flops; + } }; Ptr MVNLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/pooling_layer.cpp b/modules/dnn/src/layers/pooling_layer.cpp index a80d4f4609..c378a68e8d 100644 --- a/modules/dnn/src/layers/pooling_layer.cpp +++ b/modules/dnn/src/layers/pooling_layer.cpp @@ -241,6 +241,27 @@ class PoolingLayerImpl : public PoolingLayer return false; } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)inputs; // suppress unused variable warning + long flops = 0; + + for(int i = 0; i < outputs.size(); i++) + { + if (type == MAX) + { + if (i%2 == 0) + flops += total(outputs[i])*kernel.area(); + } + else + { + flops += total(outputs[i])*(kernel.area() + 1); + } + } + return flops; + } }; Ptr PoolingLayer::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 c3084c834e..f774afc5e7 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -312,6 +312,20 @@ class PriorBoxLayerImpl : public PriorBoxLayer } } + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + long flops = 0; + + for (int i = 0; i < inputs.size(); i++) + { + flops += total(inputs[i], 2) * _numPriors * 4; + } + + return flops; + } + float _minSize; float _maxSize; diff --git a/modules/dnn/src/layers/scale_layer.cpp b/modules/dnn/src/layers/scale_layer.cpp index c54cd417af..e47cc9f253 100644 --- a/modules/dnn/src/layers/scale_layer.cpp +++ b/modules/dnn/src/layers/scale_layer.cpp @@ -56,6 +56,18 @@ class ScaleLayerImpl : public ScaleLayer } } + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + long flops = 0; + for(int i = 0; i < inputs.size(); i++) + { + flops += 2*total(inputs[i]); + } + return flops; + } + bool hasBias; }; diff --git a/modules/dnn/src/layers/shift_layer.cpp b/modules/dnn/src/layers/shift_layer.cpp index f7a5fdbbc9..6fbb33a276 100644 --- a/modules/dnn/src/layers/shift_layer.cpp +++ b/modules/dnn/src/layers/shift_layer.cpp @@ -81,6 +81,20 @@ class ShiftLayerImpl : public ShiftLayer } } } + + virtual int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + long flops = 0; + + for(int i= 0; i < inputs.size(); i++) + { + flops += total(inputs[i]); + } + + return flops; + } }; Ptr ShiftLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/softmax_layer.cpp b/modules/dnn/src/layers/softmax_layer.cpp index e8218d171e..a46894476f 100644 --- a/modules/dnn/src/layers/softmax_layer.cpp +++ b/modules/dnn/src/layers/softmax_layer.cpp @@ -146,6 +146,20 @@ class SoftMaxLayerImpl : public SoftmaxLayer } } + int64 getFLOPS(const std::vector &inputs, + const std::vector &outputs) const + { + (void)outputs; // suppress unused variable warning + int64 flops = 0; + + for (int i = 0; i < inputs.size(); i++) + { + flops += 4*total(inputs[i]); + } + + return flops; + } + int axisRaw; }; diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index aef9941949..52869badff 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -41,6 +41,7 @@ #include "test_precomp.hpp" #include "npy_blob.hpp" +#include namespace cvtest { diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index a9f122e5c0..e9af98b155 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -43,6 +43,7 @@ #include "test_precomp.hpp" #include "npy_blob.hpp" +#include namespace cvtest {