// 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 <opencv2/dnn/shape_utils.hpp> namespace cv { namespace dnn { class ScaleLayerImpl : public ScaleLayer { public: ScaleLayerImpl(const LayerParams& params) { setParamsFrom(params); hasBias = params.get<bool>("bias_term", false); } bool getMemoryShapes(const std::vector<MatShape> &inputs, const int requiredOutputs, std::vector<MatShape> &outputs, std::vector<MatShape> &internals) const { 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(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals) { CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); CV_Assert(blobs.size() == 1 + hasBias); 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<float>(n); float b = hasBias ? blobs[1].at<float>(n) : 0; Mat outBlobPlane = getPlane(outBlob, cn, n); Mat inpBlobPlane = getPlane(inpBlob, cn, n); inpBlobPlane.convertTo(outBlobPlane, CV_32F, w, b); } } } } virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node) { switch (node->backendId) { case DNN_BACKEND_HALIDE: { #ifdef HAVE_HALIDE auto base = node.dynamicCast<HalideBackendNode>(); 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<BackendNode>(new HalideBackendNode(base, top)); #endif // HAVE_HALIDE break; } } return Ptr<BackendNode>(); } virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs) { #ifdef HAVE_HALIDE Halide::Buffer<float> 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<BackendNode>(new HalideBackendNode(top)); #endif // HAVE_HALIDE return Ptr<BackendNode>(); } #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<MatShape> &inputs, const std::vector<MatShape> &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> ScaleLayer::create(const LayerParams& params) { return Ptr<ScaleLayer>(new ScaleLayerImpl(params)); } } // namespace dnn } // namespace cv