opencv dnn模块 DNN中Layer的动态创建、Net自定义网络搭建示例

本文详细介绍了如何使用OpenCV的DNN模块构建自定义的神经网络,包括Net的定义、DictValue、Dict和LayerParams的使用,以及多个网络模型的搭建和测试,如Conv+Relu、Ave_Pooling和Eltwise层的组合。通过示例代码展示了如何添加和连接层,实现网络的前向计算。
摘要由CSDN通过智能技术生成

一、介绍

opencv的dnn模块整体设计与现有的框架如caffe、tensorflow基本类似。
Net用有向无环图(DAG)表示,每一个节点是一个Layer示例,每条有向边都是一个Blob(一个NCWH的Mat)。

使用opencv的dnn功能,多数为直接加载第三方模型,同其他框架类似,opencv的dnn模块也是可以如同搭积木一样,拼装自己的网络结构(官方支持的层、自己注册的层),只是比其他的略复杂而已。后面给出多个例子说明,讲解如何利用opencv dnn模块搭建自己的网络。(仅前向计算)

理论上,只要知道其他网络模型的 网络结构、权重数据,就完全可以在opencv中自定义实现,注意,这里实现指的不是修改opencv的源代码。

二、Net的定义

class CV_EXPORTS_W_SIMPLE Net
{
   
public:

    CV_WRAP Net();  //!< 默认构造函数
    CV_WRAP ~Net(); //!< 默认析构函数;引用计数为0则析构
    //使用Inter model优化器的中间表示来创建网络;
    //xml是网络拓扑结构的XML配置文件
    //bin是model的model的二进制文件
    //使用Inter model优化器创建网络,OpenCV会使用inter的推理引擎后端进行推理;    
    CV_WRAP static Net readFromModelOptimizer(const String& xml, const String& bin);

    //测试网络中是否有layer,是否为空;若没有layer则返回true
    CV_WRAP bool empty() const;

    //向网络中添加新的layer;
    //name是layer的名字,是唯一的;
    //type是网络的类型,卷积层还是relu等;但是必须是OpenCV支持的层,或者自己实现的,在层注册器中注册过的类型;
    //params是层的参数,用于初始化该层;
    //返回值为该层唯一的整数ID;若返回-1表示添加失败
    int addLayer(const String &name, const String &type, LayerParams &params);

    //添加新层,将其第一个输入与上一层第一个输出相连接;
    //参数与addLayer函数相同;
    int addLayerToPrev(const String &name, const String &type, LayerParams &params);

    //转换layer的string name ;返回整数ID;若为-1,则layer不存在
    CV_WRAP int getLayerId(const String &layer);

	//获取所有layer的string name
    CV_WRAP std::vector<String> getLayerNames() const;

    //字符串和整数及他们的数组的容器
    typedef DictValue LayerId;

	//返回指向网络中指定ID的层的指针,layerId为整数ID或者层名字符串
    CV_WRAP Ptr<Layer> getLayer(LayerId layerId);

    //返回指向特定层的所有输入层的指针
    std::vector<Ptr<Layer> > getLayerInputs(LayerId layerId); // FIXIT: CV_WRAP

    //连接第一个layer的输出到第二个layer的输入
    //outPin 第一个layer输出的描述.
    //inpPin 第二个layer输入的描述.
    //输入的模板为:<layer_name>.[input_num]
    //模板层名称的第一部分是添加层的sting名称。如果该部分为空,则使用网络输入伪层;
    //模板输入编号的第二个可选部分是层输入编号,或者是标签编号。如果省略此部分,则将使 用第一层输入。
    CV_WRAP void connect(String outPin, String inpPin);

    //第一层的输出与第二层输入相连接
    //outLayerId 第一层的标识符
    //outNum 第一层输出的编号(一个层可能会有多个输出)
    //inpLayerId 第二层的标识符
    //inpNum 第二层输入的编号(一个层可能会有多个输入)
    void connect(int outLayerId, int outNum, int inpLayerId, int inpNum);

    //设置网络输入伪层的输出名称
    //每个网络都有自己的输入伪层,id=0
    //该层仅仅存储user的blobs,不进行任何计算
    //这一层提供了用户数据传递到网络中的唯一方法
    //与任何其他层一样,此层可以标记其输出,而此函数提供了一种简单的方法来实现这一点。
	CV_WRAP void setInputsNames(const std::vector<String> &inputBlobNames);

	//根据指定的层进行网路前向计算,并返回指定层名的输出
	// outputName 需要获取指定层输出的名字
	// 返回指定层首次出现的时输出
	// 默认对整个网络进行前向计算
    CV_WRAP Mat forward(const String& outputName = String());
	
	// 同上,outputBlobs为指定层名的所有输出
	CV_WRAP void forward(OutputArrayOfArrays outputBlobs, const String& outputName = String());

	// 根据给定的多个输出层名进行前向运算,并计算所有给定层名首次出现的输出
	CV_WRAP void forward(OutputArrayOfArrays outputBlobs,
                             const std::vector<String>& outBlobNames);
                             
	// 根据给定的多个输出层名进行前向运算,并计算所有给定层名的所有输出
    CV_WRAP_AS(forwardAndRetrieve) void forward(CV_OUT std::vector<std::vector<Mat> >& outputBlobs,
                                                    const std::vector<String>& outBlobNames);

    //编译Halide layers.<Halide是由MIT、Adobe和Stanford等机构合作实现的图像处理语言,它的核心思想即解耦算法和优化>
    //scheduler : 带有scheduler指令的yaml文件的路径
    //@see setPreferableBackend
    //调度Halide后端支持的层,然后编译
    //对于scheduler中不支持的层,或者完全不使用手动调度的层,会采用自动调度
	CV_WRAP void setHalideScheduler(const String& scheduler);

    //指定使用特定的计算平台运行网络
    //输入是backend的标识符
    //如果使用Intel的推理引擎库编译opencv,则DNN_BACKEND_DEFAULT默认表示
    //DNN_BACKEND_INFERENCE_ENGINE 否则是DNN_BACKEND_OPENCV.
    CV_WRAP void setPreferableBackend(int backendId);

    //指定特定的计算设备
    //输入是目标设备的标识符
    /**
     * List of supported combinations backend / target:
     * |                        | DNN_BACKEND_OPENCV | DNN_BACKEND_INFERENCE_ENGINE | DNN_BACKEND_HALIDE |
     * |------------------------|--------------------|------------------------------|--------------------|
     * | DNN_TARGET_CPU         |                  + |                            + |                  + |
     * | DNN_TARGET_OPENCL      |                  + |                            + |                  + |
     * | DNN_TARGET_OPENCL_FP16 |                  + |                            + |                    |
     * | DNN_TARGET_MYRIAD      |                    |                            + |                    |
    */
    CV_WRAP void setPreferableTarget(int targetId);

	// 设置网络的输入数据
	// blob 数据深度必须是 CV_32F 或 CV_8U 
	// name 设置输入数据的名字,可对应setInputsNames的参数(默认是仅一个输入)
	// scalefactor 可选缩放参数
    // mean        可选均值参数
    // input(n,c,h,w) = scalefactor*input(n,c,h,w) + mean
    // 注意: 
    //     net默认有一个"_input"层, 这里设置是input实际是"_input"层的输出
    CV_WRAP void setInput(InputArray blob, const String& name = "",
                              double scalefactor = 1.0, const Scalar& mean = Scalar());

    //为layer设置新的参数
	// layer     层的name或id
	// numParam  在 Layer::blobs 数组中的索引
	// blob      学习参数数据
    //如果新blob的形状与前一个形状不同,则以下正向传递可能失败
    CV_WRAP void setParam(LayerId layer, int numParam, const Mat &blob);

	//返回指定层参数的blob
	// 参数同setParam
    CV_WRAP Mat getParam(LayerId layer, int numParam = 0);

	//返回所有无输出的层的索引
    CV_WRAP std::vector<int> getUnconnectedOutLayers() const;

	//返回所有无输出的层的name
    CV_WRAP std::vector<String> getUnconnectedOutLayersNames() const;

    //输出网络中所有layer的input和output的shapes
    //netInputShapes 网络输入层中所有输入块的形状
    // layersIds 返回层的ID
    //inLayersShapes 返回输入层形状 顺序与layersIds的顺序相同
    //outLayersShapes 返回输出层形状 顺序与layersIds的顺序相同
    CV_WRAP void getLayersShapes(const std::vector<MatShape>& netInputShapes,
                     CV_OUT std::vector<int>& layersIds,
                     CV_OUT std::vector<std::vector<MatShape> >& inLayersShapes,
                     CV_OUT std::vector<std::vector<MatShape> >& outLayersShapes) const;

    /** @重载 */
    // 给定输入尺寸,计算网络所有layer的输入和输出形状
    CV_WRAP void getLayersShapes(const MatShape& netInputShape,
     				CV_OUT std::vector<int>& layersIds,
                    CV_OUT std::vector<std::vector<MatShape> >& inLayersShapes,
                    CV_OUT std::vector<std::vector<MatShape> >& outLayersShapes) const;

    //输出网络中指定layer的input和output的shapes
    //netInputShape 网络输入的shapes
    //指定layer的ID
    //inLayerShapes返回指定层input的shapes
    //outLayerShapes返回指定层output的shapes
    void getLayerShapes(const MatShape& netInputShape,
             const int layerId,
             CV_OUT std::vector<MatShape>& inLayerShapes,
             CV_OUT std::vector<MatShape>& outLayerShapes) const; // FIXIT: CV_WRAP

     /** @重载 */
     // 给定输入尺寸,计算指定layer的输入和输出形状
     void getLayerShapes(const std::vector<MatShape>& netInputShapes,
             const int layerId,
             CV_OUT std::vector<MatShape>& inLayerShapes,
             CV_OUT std::vector<MatShape>& outLayerShapes) const; // FIXIT: CV_WRAP

    //计算指定input,运行整个网络的FLOPS
    //netInputShapes 所有输入的shapes
    CV_WRAP int64 getFLOPS(const std::vector<MatShape>& netInputShapes) const;
    /** 重载 */
    CV_WRAP int64 getFLOPS(const MatShape& netInputShape) const;
    //计算指定layer的FLOPS
    CV_WRAP int64 getFLOPS(const int layerId,
                      const std::vector<MatShape>& netInputShapes) const;
    /** 重载 */
    CV_WRAP int64 getFLOPS(const int layerId, const MatShape& netInputShape) const;

    //获取整个model中layer Type的列表
    CV_WRAP void getLayerTypes(CV_OUT std::vector
在C++使用OpenCV版本4.5.3以及其深度神经网络dnn模块加载ONNX模型时,通常需要确保输入的图像尺寸符合模型的期望输入尺寸。以下步骤可以用来获取ONNX模型输入的图像尺寸: 1. 加载ONNX模型:首先需要加载ONNX模型文件到`cv::dnn::Net`对象。 2. 获取输入层信息:通过`Net`对象的`getLayerNames()`方法可以获取所有层的名称,然后使用`getInputName()`或`getInputNames()`方法获取输入层的名称。 3. 获取输入层的尺寸信息:通过获取到的输入层名称,使用`getLayer()`方法获取到输入层的信息,输入层的属性通常包含了输入尺寸的元信息。 下面是一个示例代码片段,展示如何获取输入层的尺寸信息: ```cpp #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> int main() { // 加载ONNX模型 cv::dnn::Net net = cv::dnn::readNetFromONNX("model.onnx"); // 获取网络的输入层名称 std::vector<std::string> inputBlobNames = net.getInputNames(); std::string inputBlobName = inputBlobNames[0]; // 假设模型只有一个输入层 // 获取输入层的尺寸信息 cv::dnn::LayerDetails inputLayer = net.getLayer(inputBlobName); std::vector<int> inputShape = inputLayer.getOutputShape(); // 输出输入层的尺寸信息 std::cout << "输入层尺寸: " << inputShape[2] << " x " << inputShape[3] << std::endl; // 高度和宽度 // ... } ``` 请注意,上述代码的`inputShape[2]`和`inputShape[3]`分别表示输入图像的高度和宽度。某些模型可能还需要批量大小(batch size)和通道数信息,这些可以在`inputShape[0]`和`inputShape[1]`找到。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aworkholic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值