第一部分 前言
众位小伙伴,好久没更新博客了,本次为大家带来:如何在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,实现的功能为:
完成对目标特征图的各通道加权,主要应用点在PyTorch实现的EfficientNet中:
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 ¶ms);
};
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