美文网首页
CV04-01:OpenCV的dnn模块

CV04-01:OpenCV的dnn模块

作者: 杨强AT南京 | 来源:发表于2019-12-02 09:11 被阅读0次

  OpenCV的DNN模块,提供了对其他框架训练模型的加载,目前支持框架有:
  1. *Caffe, http://caffe.berkeleyvision.org/
  2. *TensorFlow, https://www.tensorflow.org/
  3. *Torch, http://torch.ch/
  4. *Darknet, https://pjreddie.com/darknet/
  5. *DLDT, https://software.intel.com/openvino-toolkit
  6. *ONNX, https://onnx.ai/


  本主题按照OpenCV官方的例子撸了两个模型加载的Demo:Darknet与Caffe. 其中使用的预训练模型可以从下面两个URL获取下载地址:
  - Caffe模型:
https://docs.opencv.org/4.1.2/d5/de7/tutorial_dnn_googlenet.html
  - Darknet模型:
https://pjreddie.com/darknet/yolo/


Caffe模型加载

#include <opencv2/opencv.hpp>
#include <iostream>
#include <fstream>


int main(int argc, char const *argv[]){
    /*
        加载别人训练好的模型,用来识别图片的类别;
            - bvlc_googlenet.caffemodel             :  训练好的模型
            - bvlc_googlenet.prototxt               :  训练使用的深度网络结构
            - classification_classes_ILSVRC2012.txt :类别与类别名称
     */

    /*
        框架名:
            *Caffe, http://caffe.berkeleyvision.org/
            *TensorFlow, https://www.tensorflow.org/
            *Torch, http://torch.ch/
            *Darknet, https://pjreddie.com/darknet/
            *DLDT, https://software.intel.com/openvino-toolkit
            *ONNX, https://onnx.ai/
        框架的模型扩展名:
            *.caffemodel (Caffe
            *.pb (TensorFlow)
            *.t7 | *.net (Torch)
            *.weights (Darknet)
            *.bin (DLDT)
            *.onnx (ONNX)
        框架的结构配置扩展名:
            *.prototxt (Caffe)
            *.pbtxt (TensorFlow)
            *.cfg (Darknet)
            *.xml (DLDT)
     */
    // 加载模型
    cv::String model        = "bvlc_googlenet.caffemodel";
    cv::String config       = "bvlc_googlenet.prototxt";
    cv::String framework    = "Caffe";               // 显式指定框架名(可以使用"",会根据扩展名自动确定)
    cv::dnn::Net net = cv::dnn::readNet(model, config, framework);
    // 设置使用的计算推理库
    /*
         : 
        cv::dnn::DNN_BACKEND_DEFAULT,   = DNN_BACKEND_INFERENCE_ENGINE
        cv::dnn::DNN_BACKEND_HALIDE,    = DNN_BACKEND_OPENCV
        cv::dnn::DNN_BACKEND_INFERENCE_ENGINE, 
        cv::dnn::DNN_BACKEND_OPENCV, 
        cv::dnn::DNN_BACKEND_VKCOM      = DNN_BACKEND_OPENCV
}
     */
    net.setPreferableBackend(cv::dnn::DNN_BACKEND_DEFAULT); // 采用默认值
    // 设置运算的目标设备
    /*
        cv::dnn::DNN_TARGET_CPU, 
        cv::dnn::DNN_TARGET_OPENCL, 
        cv::dnn::DNN_TARGET_OPENCL_FP16, 
        cv::dnn::DNN_TARGET_MYRIAD, 
        cv::dnn::DNN_TARGET_VULKAN, 
        cv::dnn::DNN_TARGET_FPGA 
     */
    net.setPreferableBackend(cv::dnn::DNN_TARGET_CPU); // 采用默认值
    // net.setPreferableBackend(cv::dnn::DNN_BACKEND_HALIDE); // 采用OpenGL的管道图像处理
    // 加载图片(或者直接从视频抓取图片识别)
    cv::String imgfile = "space_shuttle.jpg";
    cv::Mat img = cv::imread(imgfile);

    // 图像的预处理(去均值,缩放),并返回服务训练模型的图像格式:NCHW矩阵
    cv::Mat std_img;
    // 注意:bvlc_googlenet.prototxt中的深度网络模型需要的图像必须是224 * 224大小的。
    cv::dnn::blobFromImage(img, std_img, 1.0, cv::Size(224, 224), cv::Scalar(), false, false, CV_32F);
    // 预测
    net.setInput(std_img);             // 输入数据
    // 返回的结果是一个数组(一维矩阵),包含是每类可能的概率,结果当然取概率最高的。
    // 类别的对应清单,从classification_classes_ILSVRC2012.txt获取
    cv::Mat prob = net.forward();       // 返回预测结果

    // 取最大的概率数据与位置
    double  max_prob;
    cv::Point max_pos;
    cv::minMaxLoc(prob, 0, &max_prob, 0, &max_pos);  // 最小值不取,就直接使用空指针(0)。
    std::cout << "识别类别是:" << max_pos.x << "概率是:" << max_prob << std::endl;  // 0行,所以取x列数
    // 从classification_classes_ILSVRC2012.txt文件获取max_pos.x位置的类别名称:space shuttle
    // 注意:矩阵下标从0开始,文件的行数也要按照从0开始计。
    std::ifstream ifs("classification_classes_ILSVRC2012.txt");
    int idx = 0;
    char buf[256] = {0};
    while(ifs.good()){
        bzero(buf, sizeof(buf));
        ifs.getline(buf, sizeof(buf)-1);
        if(idx == max_pos.x)
            break;
        idx++;
    }
    std::cout << "类别名:"<< buf << std::endl;
    return 0;
}

Darknet模型加载

#include <fstream>
#include <iostream>
#include <opencv2/opencv.hpp>

// 调用预先训练好的yolo模型,用来侦测识别目标。
/**
 * 深度网络模型 :yolov3.cfg
 * 预先训练好的权重:yolov3.weights
 * 分类的类别文件:coco.names
 * 用来测试的侦测识别图像:dog.jpg
 */
int main(int argc, char** argv){
    // 加载深度网络模型
    cv::String model = "../yolov3.weights";     // 使用darknet框架训练出来的模型
    cv::String config = "../yolov3.cfg";
    cv::dnn::Net net = cv::dnn::readNet(model, config, "Darknet");   // 如果不知道使用的什么框架训练,最后参数使用空字符串,函数自动识别;
    // 设置运算方式
    net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);          // cv::dnn::DNN_BACKEND_INFERENCE_ENGINE
    net.setPreferableBackend(cv::dnn::DNN_TARGET_CPU);              // 使用CPU计算
    // 获取输出层名字
    std::vector<cv::String> outNames = net.getUnconnectedOutLayersNames();
    // 打印输出层名字
    // for(cv::String name : outNames){
    //     std::cout<< name << std::endl;
    // }
    // 打开需要测试的图片(可以使用VideoCapture打开视频,来动态抓取图像)
    cv::Mat img = cv::imread("../road.jpg");

    // 对图像进行预处理
    cv::Mat blob;
    cv::dnn::blobFromImage(
        img,        // 输入
        blob,       // 输出
        1.0,        // 图像的像素的控制系数
        // cv::Size(img.rows, img.cols),    // 测试过,图片太大,会产生错误,1008测试也会出错,768宽可以
        cv::Size(416, 416), 
        cv::Scalar(),       // 均值处理(如果不指定,策使用图像的均值)
        true,       // 第1通道与第3通道交换(就是RGB与BGR的区别)
        false,      // 图像resize后是否裁剪
        CV_8U);     // 输出图像的深度

    net.setInput(
        blob,       // 输入的数据
        "",         // 输入层的名字,采用默认
        0.00392,        // 对输入的控制系数
        cv::Scalar());  // 对输入的均值处理(上面已经处理,这个函数只需要第一个参数即可)
    // 处理Faster-RCNN与R-FCN的情况
    if (net.getLayer(0)->outputNameToIndex("im_info") != -1){
        cv::Mat imInfo = (cv::Mat_<float>(1, 3) << img.rows, img.cols, 1.6f);
        net.setInput(imInfo, "im_info");
    }
    // 输出预测
    std::vector<cv::Mat> outs;          // 输出层可能多个
    net.forward(outs, outNames);
    // 对预测的结果处理:类别,概率,图像区域
    // 得到输出层数量
    std::vector<int> outLayers = net.getUnconnectedOutLayers();
    // 得到第一个输出层类型
    std::string outLayerType = net.getLayer(outLayers[2])->type;

    // 定义解析的侦测类别结果
    std::vector<int> classIds;      // 类别id
    std::vector<float> confidences; // 类别的概率(置信度)
    std::vector<cv::Rect> boxes;        // 侦测对象的图像区域

    // 输出层类型两类:DetectionOutput或者Region,表示两种不通的输出格式
    if(outLayerType == "Region"){
        // 格式:N * C,N表示侦测的对象数,C表示总得类别数 + 4,其中最前面4个参数是识别的对象区域[center-x, center-y, w, h]
        //循环解析数据
        for(cv::Mat an_out: outs){
            // 每行表示一个侦测到的对象
            for(int row = 0; row < an_out.rows; row++){
                // 取坐标
                cv::Mat rect = an_out.row(row).colRange(0,4);
                // 取侦测的概率
                cv::Mat prob = an_out.row(row).colRange(5, an_out.cols);
                // 取概率最大值
                cv::Point classIdPoint;
                double confidence;
                cv::minMaxLoc(prob, 0, &confidence, 0, &classIdPoint);
                if(confidence > 0.5){
                    // 类别
                    classIds.push_back(classIdPoint.x);
                    // 概率
                    confidences.push_back((float)confidence);
                    // 区域
                    int centerX = (int)(rect.at<float>(0,0) * img.cols);
                    int centerY = (int)(rect.at<float>(0,1) * img.rows);
                    int width = (int)(rect.at<float>(0,2) * img.cols);
                    int height = (int)(rect.at<float>(0,3) * img.rows);
                    int left = centerX - width / 2;
                    int top = centerY - height / 2;
                    boxes.push_back(cv::Rect(left, top, width, height));
                }
            }
        }
    }
    else if(outLayerType == "DetectionOutput"){
        //
        std::cout << "DetectionOutput" << std::endl;
    }
    std::vector<int> indices;
    cv::dnn::NMSBoxes(boxes, confidences, 0.5, 0.4, indices);
    for(cv::Rect b : boxes){
        cv::rectangle(
            img, 
            cv::Point(b.x, b.y), 
            cv::Point(b.x + b.width, b.y + b.height), 
            cv::Scalar(0, 0, 255),
            4);
    }
    cv::imwrite("../road_out.png", img);

    return 0;
}
  • 图片效果


    目标侦测

附录:

  • 有OpenCV的dnn模型加载,Python训练的模型可以很轻易的使用到任何设备与平台。

相关文章

网友评论

      本文标题:CV04-01:OpenCV的dnn模块

      本文链接:https://www.haomeiwen.com/subject/aflxgctx.html