前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Yolov5 + Opencv DNN + C++部署

Yolov5 + Opencv DNN + C++部署

原创
作者头像
AI小怪兽
修改2023-11-30 15:50:54
1.4K0
修改2023-11-30 15:50:54
举报
文章被收录于专栏:YOLO大作战

漫谈C++

摘要:深度学习模型如何在C++下进行调用,

本文详细阐述了YOLOv5在C++ & Opencv下进行调用

1.Opencv介绍

OpenCV由各种不同组件组成。OpenCV源代码主要由OpenCV core(核心库)、opencv_contrib和opencv_extra等子仓库组成。近些年,OpenCV的主仓库增加了深度学习相关的子仓库:OpenVINO(即DLDT, Deep Learning Deployment Toolkit)、open_model_zoo,以及标注工具CVAT等。

1.2 Opencv DNN介绍

OpenCV深度学习模块只提供网络推理功能,不支持网络训练。像所有的推理框架一样,加载和运行网络模型是基本的功能。深度学习模块支持TensorFlow、Caffe、Torch、DarkNet、ONNX和OpenVINO格式的网络模型,用户无须考虑原格式的差异。在加载过程中,各种格式的模型被转换成统一的内部网络结构。

1.3 .OpenCV DNN模块支持的不同深度学习功能

  • 图像分类网络
  • Caffe:AlexNet、GoogLeNet、VGG、ResNet、SqueezeNet、DenseNet、ShuffleNet
  • TensorFlow:Inception、MobileNet
  • Darknet:darknet-imagenet
  • ONNX:AlexNet、GoogleNet、CaffeNet、RCNN_ILSVRC13、ZFNet512、VGG16、VGG16_bn、ResNet-18v1、ResNet-50v1、CNN Mnist、MobileNetv2、LResNet100E-IR、Emotion FERPlus、Squeezenet、DenseNet121、Inception-v1/v2、ShuffleNet
  • 对象检测网络
  • Caffe:SSD、VGG、MobileNet-SSD、Faster-RCNN、R-FCN、OpenCV face detector
  • TensorFlow:SSD、Faster-RCNN、Mask-RCNN、EAST
  • Darknet:YOLOv2、Tiny YOLO、YOLOv3、YOLOV4、YOLOV5、YOLOV7
  • ONNX:TinyYOLOv2
  • 语义分割网络:FCN(Caffe)、ENet(Torch)、ResNet101_DUC_HDC(ONNX)
  • 姿势估计网络:openpose(Caffe)
  • 图像处理网络:Colorization(Caffe)、Fast-Neural-Style(Torch)
  • 人脸识别网络:openface(Torch)

2.Opencv DNN YOLOv5导入

参考:GitHub - doleron/yolov5-opencv-cpp-python: Example of using ultralytics YOLO V5 with OpenCV 4.5.4, C++ and Python

​源代码如下:

代码语言:javascript
复制
#include <fstream>

#include <opencv2/opencv.hpp>

std::vector<std::string> load_class_list()
{
    std::vector<std::string> class_list;
    std::ifstream ifs("classes.txt");
    std::string line;
    while (getline(ifs, line))
    {
        class_list.push_back(line);
    }
    return class_list;
}

void load_net(cv::dnn::Net& net, bool is_cuda)
{
    auto result = cv::dnn::readNet(yolov5s.onnx");
    if (is_cuda)
    {
        std::cout << "Attempty to use CUDA\n";
        result.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
        result.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
    }
    else
    {
        std::cout << "Running on CPU\n";
        result.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
        result.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
    }
    net = result;
}

const std::vector<cv::Scalar> colors = { cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 0), cv::Scalar(0, 255, 255), cv::Scalar(255, 0, 0) };

const float INPUT_WIDTH = 640.0;
const float INPUT_HEIGHT = 640.0;
const float SCORE_THRESHOLD = 0.2;
const float NMS_THRESHOLD = 0.4;
const float CONFIDENCE_THRESHOLD = 0.4;

struct Detection
{
    int class_id;
    float confidence;
    cv::Rect box;
};

cv::Mat format_yolov5(const cv::Mat& source) {
    int col = source.cols;
    int row = source.rows;
    int _max = MAX(col, row);
    cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);
    source.copyTo(result(cv::Rect(0, 0, col, row)));
    return result;
}

void detect(cv::Mat& image, cv::dnn::Net& net, std::vector<Detection>& output, const std::vector<std::string>& className) {
    cv::Mat blob;

    auto input_image = format_yolov5(image);

    cv::dnn::blobFromImage(input_image, blob, 1. / 255., cv::Size(INPUT_WIDTH, INPUT_HEIGHT), cv::Scalar(), true, false);
    net.setInput(blob);
    std::vector<cv::Mat> outputs;
    net.forward(outputs, net.getUnconnectedOutLayersNames());

    float x_factor = input_image.cols / INPUT_WIDTH;
    float y_factor = input_image.rows / INPUT_HEIGHT;

    float* data = (float*)outputs[0].data;

    const int dimensions = 85;
    const int rows = 25200;

    std::vector<int> class_ids;
    std::vector<float> confidences;
    std::vector<cv::Rect> boxes;

    for (int i = 0; i < rows; ++i) {

        float confidence = data[4];
        if (confidence >= CONFIDENCE_THRESHOLD) {

            float* classes_scores = data + 5;
            cv::Mat scores(1, className.size(), CV_32FC1, classes_scores);
            cv::Point class_id;
            double max_class_score;
            minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
            if (max_class_score > SCORE_THRESHOLD) {

                confidences.push_back(confidence);

                class_ids.push_back(class_id.x);

                float x = data[0];
                float y = data[1];
                float w = data[2];
                float h = data[3];
                int left = int((x - 0.5 * w) * x_factor);
                int top = int((y - 0.5 * h) * y_factor);
                int width = int(w * x_factor);
                int height = int(h * y_factor);
                boxes.push_back(cv::Rect(left, top, width, height));
            }

        }

        data += 85;

    }

    std::vector<int> nms_result;
    cv::dnn::NMSBoxes(boxes, confidences, SCORE_THRESHOLD, NMS_THRESHOLD, nms_result);
    for (int i = 0; i < nms_result.size(); i++) {
        int idx = nms_result[i];
        Detection result;
        result.class_id = class_ids[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx];
        output.push_back(result);
    }
}

int main(int argc, char** argv)
{

    std::vector<std::string> class_list = load_class_list();

    cv::Mat frame;
    cv::VideoCapture capture("sample.mp4");
    if (!capture.isOpened())
    {
        std::cerr << "Error opening video file\n";
        return -1;
    }

    bool is_cuda =false;

    cv::dnn::Net net;
    load_net(net, is_cuda);

    auto start = std::chrono::high_resolution_clock::now();
    int frame_count = 0;
    float fps = -1;
    int total_frames = 0;

    while (true)
    {
        capture.read(frame);
        if (frame.empty())
        {
            std::cout << "End of stream\n";
            break;
        }

        std::vector<Detection> output;
        detect(frame, net, output, class_list);

        frame_count++;
        total_frames++;

        int detections = output.size();

        for (int i = 0; i < detections; ++i)
        {

            auto detection = output[i];
            auto box = detection.box;
            auto classId = detection.class_id;
            const auto color = colors[classId % colors.size()];
            cv::rectangle(frame, box, color, 3);

            cv::rectangle(frame, cv::Point(box.x, box.y - 20), cv::Point(box.x + box.width, box.y), color, cv::FILLED);
            cv::putText(frame, class_list[classId].c_str(), cv::Point(box.x, box.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
        }

        if (frame_count >= 30)
        {

            auto end = std::chrono::high_resolution_clock::now();
            fps = frame_count * 1000.0 / std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

            frame_count = 0;
            start = std::chrono::high_resolution_clock::now();
        }

        if (fps > 0)
        {

            std::ostringstream fps_label;
            fps_label << std::fixed << std::setprecision(2);
            fps_label << "FPS: " << fps;
            std::string fps_label_str = fps_label.str();

            cv::putText(frame, fps_label_str.c_str(), cv::Point(10, 25), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);
        }

        cv::imshow("output", frame);

        if (cv::waitKey(1) != -1)
        {
            capture.release();
            std::cout << "finished by user\n";
            break;
        }
    }

    std::cout << "Total frames: " << total_frames << "\n";

    return 0;
}

2.1 配置opencv环境

包含目录:D:\Program Files\Opencv\opencv-4.5.2\build\include

库目录:D:\Program Files\Opencv\opencv-4.5.2\build\x64\vc15\lib

链接器-输入: opencv_world452.lib

2.2 VS2019编译

2.3 如何得到.ONNX

GitHub - ultralytics/yolov5: YOLOv5 🚀 in PyTorch > ONNX > CoreML > TFLite进行模型转换

代码语言:javascript
复制
python export.py --weights weights\yolov5s.pt --include  onnx

2.4 Bug解决

代码语言:javascript
复制
[ERROR:0] global C:\build\master_winpack-build-win64-vc15\opencv\modules\dnn\src\onnx\onnx_importer.cpp (2110) cv::dnn::dnn4_v20210301::ONNXImporter::handleNode DNN/ONNX: ERROR during processing node with 1 inputs and 1 outputs: [Identity]:(onnx::Resize_445)
OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(4.5.2) Error: Unspecified error (> Node [Identity]:(onnx::Resize_445) parse error: OpenCV(4.5.2) C:\build\master_winpack-build-win64-vc15\opencv\modules\dnn\src\dnn.cpp:5298: error: (-215:Assertion failed) inputs.size() in function 'cv::dnn::dnn4_v20210301::Layer::getMemoryShapes'
> ) in cv::dnn::dnn4_v20210301::ONNXImporter::handleNode, file C:\build\master_winpack-build-win64-vc15\opencv\modules\dnn\src\onnx\onnx_importer.cpp, line 2129​

解决方案:export.py中do_constant_folding=True改为do_constant_folding=False2

代码语言:javascript
复制
torch.onnx.export(
        model.cpu() if dynamic else model,  # --dynamic only compatible with cpu
        im.cpu() if dynamic else im,
        f,
        verbose=False,
        opset_version=opset,
        do_constant_folding=False,
        input_names=['images'],
        output_names=output_names,
        dynamic_axes=dynamic or None)

2.5 opencv dnn推理性能

yolov5s 输入:640*640 FPS:3.10

yolov5n 输入:640*640 FPS:4.33

by CSDN AI小怪兽 http://cv2023.blog.csdn.net

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 漫谈C++
  • 1.Opencv介绍
    • 1.2 Opencv DNN介绍
      • 1.3 .OpenCV DNN模块支持的不同深度学习功能
      • 2.Opencv DNN YOLOv5导入
        • 2.1 配置opencv环境
          • 2.2 VS2019编译
            • 2.3 如何得到.ONNX
              • 2.4 Bug解决
                • 2.5 opencv dnn推理性能
                相关产品与服务
                人脸识别
                腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档