// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // Copyright (C) 2016, Intel Corporation, all rights reserved. // Third party copyrights are property of their respective owners. /* Implementation of Scale layer. */ #include "../precomp.hpp" #include "layers_common.hpp" #include "op_halide.hpp" #include namespace cv { namespace dnn { class ScaleLayerImpl : public ScaleLayer { public: ScaleLayerImpl(const LayerParams& params) { setParamsFrom(params); hasBias = params.get("bias_term", false); } bool getMemoryShapes(const std::vector &inputs, const int requiredOutputs, std::vector &outputs, std::vector &internals) const { CV_Assert(blobs.size() == 1 + hasBias); Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals); return true; } virtual bool supportBackend(int backendId) { return backendId == DNN_BACKEND_DEFAULT || backendId == DNN_BACKEND_HALIDE && haveHalide(); } void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) { CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); } void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) { CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); for (size_t ii = 0; ii < outputs.size(); ii++) { Mat &inpBlob = *inputs[ii]; Mat &outBlob = outputs[ii]; CV_Assert(inpBlob.size[1] == blobs[0].total()); if (hasBias) CV_Assert(inpBlob.size[1] == blobs[1].total()); CV_Assert(inpBlob.type() == CV_32F && outBlob.type() == CV_32F); for( int cn = 0; cn < inpBlob.size[0]; cn++ ) { for (int n = 0; n < inpBlob.size[1]; n++) { float w = blobs[0].at(n); float b = hasBias ? blobs[1].at(n) : 0; Mat outBlobPlane = slice(outBlob, cn, n); Mat inpBlobPlane = slice(inpBlob, cn, n); inpBlobPlane.convertTo(outBlobPlane, CV_32F, w, b); } } } } virtual Ptr tryAttach(const Ptr& node) { switch (node->backendId) { case DNN_BACKEND_HALIDE: { #ifdef HAVE_HALIDE auto base = node.dynamicCast(); Halide::Func& input = base->funcs.back(); Halide::Var x("x"), y("y"), c("c"), n("n"); Halide::Func top = attachHalide(input(x, y, c, n)); return Ptr(new HalideBackendNode(base, top)); #endif // HAVE_HALIDE break; } } return Ptr(); } virtual Ptr initHalide(const std::vector > &inputs) { #ifdef HAVE_HALIDE Halide::Buffer input = halideBuffer(inputs[0]); Halide::Var x("x"), y("y"), c("c"), n("n"); Halide::Func top = attachHalide(input(x, y, c, n)); return Ptr(new HalideBackendNode(top)); #endif // HAVE_HALIDE return Ptr(); } #ifdef HAVE_HALIDE // attachHalide can work both with Halide::Buffer and Halide::Func. In the // second case it will be a fusion. Halide::Func attachHalide(const Halide::Expr& input) { Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name)); Halide::Var x("x"), y("y"), c("c"), n("n"); const int numChannels = blobs[0].total(); auto weights = wrapToHalideBuffer(blobs[0], {numChannels}); Halide::Expr topExpr = input * weights(c); if (hasBias) { auto bias = wrapToHalideBuffer(blobs[1], {numChannels}); topExpr += bias(c); } top(x, y, c, n) = topExpr; return top; } #endif // HAVE_HALIDE 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; } }; Ptr ScaleLayer::create(const LayerParams& params) { return Ptr(new ScaleLayerImpl(params)); } } // namespace dnn } // namespace cv