OpenCV-Dnn添加自定义的网络层

第一部分 前言

众位小伙伴,好久没更新博客了,本次为大家带来:如何在OpenCV-Dnn中添加自定义的网络层。

第二部分 OpenCV_Dnn说明

1、OpenCV-Dnn支持的深度学习网络层颇多,主要有:

  • Convolution
  • Deconvolution
  • Pooling
  • InnerProduct
  • TanH, ReLU, Sigmoid, BNLL, Power, AbsVal
  • Softmax
  • Reshape, Flatten, Slice, Split
  • LRN
  • MVN

2、本次添加一个网络层,称之为BroadcastLayer,实现的功能为:

shape(n, c, h, w) * shape(n, c, 1, 1) = shape(n, c, h, w)

完成对目标特征图的各通道加权,主要应用点在PyTorch实现的EfficientNet中:

x = torch.sigmoid(torch.AdaptiveAvgPool2d(x))*x

3、如果自定义其他层,实现方法类似

4、若是使用caffe的小伙伴,在层结构中,该层的书写方式为:type: "Broadcast"

第三部分 版本说明

OpenCV版本:4.2.0

VS版本:VS2015

第四部分:代码实现

1、init.cpp中注册网络层BroadcastLayer

CV_DNN_REGISTER_LAYER_CLASS(Broadcast, BroadcastLayer);

2、all_layers.hpp中定义网络层BroadcastLayer的类

class CV_EXPORTS BroadcastLayer : public Layer
{
public:
	static Ptr<BroadcastLayer> create(const LayerParams &params);
};

3、添加layers/boradcast_layer.cpp实现类

#include "../precomp.hpp"
#include "layers_common.hpp"
#include "../op_cuda.hpp"
#include "../op_halide.hpp"
#include "../op_inf_engine.hpp"
#include "../ie_ngraph.hpp"

namespace cv
{
namespace dnn
{

class BroadcastLayerImpl CV_FINAL : public BroadcastLayer
{
public:
	BroadcastLayerImpl(const LayerParams& params)
    {
        setParamsFrom(params);
    }

    virtual bool supportBackend(int backendId) CV_OVERRIDE
    {
        return backendId == DNN_BACKEND_OPENCV ||
               backendId == DNN_BACKEND_CUDA;
    }

    bool getMemoryShapes(const std::vector<MatShape> &inputs,
                         const int requiredOutputs,
                         std::vector<MatShape> &outputs,
                         std::vector<MatShape> &internals) const CV_OVERRIDE
    {
        CV_Assert(inputs.size() >= 2);
        CV_Assert(inputs[0].size() >= 2);

        // Number of channels in output shape is determined by the first input tensor.
        int numChannels = inputs[0][1];
        for (size_t i = 1; i < inputs.size(); i++)
        {
            CV_Assert(inputs[0][0] == inputs[i][0]);  // batch sizes are equal
        }

        outputs.assign(1, inputs[0]);
        outputs[0][1] = numChannels;
        return false;
    }

    void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
    {
        CV_TRACE_FUNCTION();
        CV_TRACE_ARG_VALUE(name, "name", name.c_str());

        std::vector<Mat> inputs, outputs;
        inputs_arr.getMatVector(inputs);
        outputs_arr.getMatVector(outputs);

        CV_Assert(outputs.size() == 1);

		float *bottom1 = (float *)(inputs[0].data);
		float *bottom2 = (float *)(inputs[1].data);
		float *top = (float *)(outputs[0].data);

		int channel = inputs[0].size[1];
		int height = inputs[0].size[2];
		int width = inputs[0].size[3];
		for (int c = 0; c < channel; c++)
		{
			int c_idx = c*height*width;
			for (int h = 0; h < height; h++)
			{
				int h_idx = h*width;
				for (int w = 0; w < width; w++)
				{
					int data_idx = c_idx + h_idx + w;
					top[data_idx] = bottom1[data_idx] * bottom2[c];
				}
			}
		}
    }

    virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &input) CV_OVERRIDE
    {
        return Ptr<BackendNode>();
    }

    virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
                           const std::vector<MatShape> &outputs) const CV_OVERRIDE
    {
        CV_UNUSED(outputs); // suppress unused variable warning
        CV_Assert(inputs.size());

        // FIXIT: handle inputs with different number of channels
        long flops = inputs.size() * total(inputs[0]);

        return flops;
    }

    bool setActivation(const Ptr<ActivationLayer>& layer) CV_OVERRIDE
    {
        if (activ.empty() || layer.empty())
        {
            activ = layer;
            return !activ.empty();
        }
        else
            return false;
    }

    Ptr<ActivationLayer> activ;
};

Ptr<BroadcastLayer> BroadcastLayer::create(const LayerParams& params)
{
    return Ptr<BroadcastLayer>(new BroadcastLayerImpl(params));
}

}
}

第五部分 代码说明

1、必须实现create函数,该函数完成网络的初始化创建

2、必须实现forward函数,该函数实现网络前向功能

3、必须实现getMemoryShapes函数,在forward前,该函数计算输入输出的shape

 

任何问题请加唯一QQ2258205918(名称samylee)!

唯一VX:samylee_csdn

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页