opencv的dnn模块整体设计与现有的框架如caffe、tensorflow基本类似。
Net用有向无环图(DAG)表示,每一个节点是一个Layer示例,每条有向边都是一个Blob(一个NCWH的Mat)。
使用opencv的dnn功能,多数为直接加载第三方模型,同其他框架类似,opencv的dnn模块也是可以如同搭积木一样,拼装自己的网络结构(官方支持的层、自己注册的层),只是比其他的略复杂而已。后面给出多个例子说明,讲解如何利用opencv dnn模块搭建自己的网络。(仅前向计算)
理论上,只要知道其他网络模型的 网络结构、权重数据,就完全可以在opencv中自定义实现,注意,这里实现指的不是修改opencv的源代码。
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 ¶ms);
//添加新层,将其第一个输入与上一层第一个输出相连接;
//参数与addLayer函数相同;
int addLayerToPrev(const String &name, const String &type, LayerParams ¶ms);
//转换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输入的描述.
//输入的模板为:.[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.
//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;