YOLOv5 实例分割 用 OPenCV DNN C++ 部署

这篇具有很好参考价值的文章主要介绍了YOLOv5 实例分割 用 OPenCV DNN C++ 部署。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Pre:

如果之前从没接触过实例分割,建议先了解一下实例分割的输出是什么。

实例分割两个关键输出是:mask系数、mask原型

本文参考自该项目(这么优秀的代码当然要给star!):GitHub - UNeedCryDear/yolov5-seg-opencv-onnxruntime-cpp: yolov5 segmentation with onnxruntime and opencv

目录

Pre:

一、代码总结

1、实例分割输出 与 目标检测输出 的区别?

2、如何获得目标的mask?

二、源码

yolov5_seg_utils.h

yolov5_seg_utils.cpp

yolo_seg.h

yolo_seg.cpp

main.cpp

三、效果


一、代码总结

1、实例分割输出 与 目标检测输出 的区别?

还是写一点实例分割部分的东西吧,互相交流下,嘿嘿。

yolov5n-seg.onnx  一张图片输入会获得两个输出,分别为

output0:  float32[1, 25200,117] 。25200是输出anchor的数量,117是4个anchor坐标信息、1个置信度、80个类别概率、32个mask系数。

output1:  float32[1,32,160,160]。即一张图片输出的一组mask原型 ,你可以理解为网络32张mask。在网络输入图上突出前景的mask,共32张,然后尺寸缩小到尺寸为160*160。

2、如何获得目标的mask?

这段代码读起来还挺废脑细胞的。

首先要指出的是,这个mask是相对于目标框的,不是相对于原图的。

获得目标框的mask:

  1. 检测框区域映射到mask原型上,然后在原型上裁剪下该区域。
  2. mask系数与目标框区域原型作矩阵乘法,其结果然后进行sigmod运算,阈值化(01二值),便获得突出目标的mask
  3. 目标mask映射到原图上

对应源码:

void GetMask2(const Mat& maskProposals, const Mat& mask_protos, OutputSeg& output, const MaskParams& maskParams) {
    int seg_channels = maskParams.segChannels;
    int net_width = maskParams.netWidth;
    int seg_width = maskParams.segWidth;
    int net_height = maskParams.netHeight;
    int seg_height = maskParams.segHeight;
    float mask_threshold = maskParams.maskThreshold;
    Vec4f params = maskParams.params;
    Size src_img_shape = maskParams.srcImgShape;

    Rect temp_rect = output.box;
    // 把已经到原图的检测框坐标信息  映射到  获得mask原型分支的输入尺寸上【160, 160】
    int rang_x = floor((temp_rect.x * params[0] + params[2]) / net_width * seg_width);
    int rang_y = floor((temp_rect.y * params[1] + params[3]) / net_height * seg_height);
    int rang_w = ceil(((temp_rect.x + temp_rect.width) * params[0] + params[2]) / net_width * seg_width) - rang_x;
    int rang_h = ceil(((temp_rect.y  + temp_rect.height) * params[0] + params[3]) / net_width * seg_height) - rang_y;

    //
    rang_w = MAX(rang_w, 1);
    rang_h = MAX(rang_h, 1);
    if (rang_x + rang_w > seg_width){
        if (seg_width - rang_x > 0)
            rang_w =seg_width -rang_x;
        else
            rang_x -= 1;
    }
    if (rang_y + rang_h > seg_height) {
        if (seg_height - rang_y > 0)
            rang_h = seg_height - rang_y;
        else
            rang_y -= 1;
    }

    vector<Range> roi_ranges;
    roi_ranges.push_back(Range(0,1));
    roi_ranges.push_back(Range::all());
    roi_ranges.push_back(Range(rang_y, rang_h+rang_y));
    roi_ranges.push_back(Range(rang_x, rang_w+rang_x));

    // 裁剪mask原型
    Mat temp_mask_protos = mask_protos(roi_ranges).clone(); // 剪裁原型,保存检测框内部的原型,其余位置清零,  以此来获得感兴趣区域(roi)
    Mat protos = temp_mask_protos.reshape(0, { seg_channels, rang_w*rang_h});// 检测至检测框大小?

    // mask系数与mask原型做矩阵乘法
    Mat matmul_res = (maskProposals * protos).t(); // mask系数【1,32】 与 mask原型【32, h*w】进行矩阵相称
    Mat masks_feature = matmul_res.reshape(1,{rang_h, rang_w}); //【1,h,w】
    Mat dest, mask;

    // sigmod
    cv::exp(-masks_feature, dest);
    dest = 1.0 / (1.0 + dest);

    // 检测框坐标 映射到 原图尺寸
    int left = floor((net_width / seg_width * rang_x - params[2]) / params[0]);
    int top = floor((net_width / seg_height * rang_y - params[3]) / params[1]);
    int width = ceil(net_width / seg_height * rang_w / params[0]);
    int height = ceil(net_height / seg_height * rang_h / params[1]);

    // 检测框mask缩放到原图尺寸
    resize(dest, mask, Size(width, height), INTER_NEAREST);

    // 阈值化
    mask = mask(temp_rect - Point(left, top)) > mask_threshold;
    output.boxMask = mask;
}

二、源码

yolov5_seg_utils.h

#pragma once
#include<iostream>
#include <numeric>
#include<opencv2/opencv.hpp>

#define YOLO_P6 false //是否使用P6模型
#define ORT_OLD_VISON 12  //ort1.12.0 之前的版本为旧版本API

struct OutputSeg {
    int id;             //结果类别id
    float confidence;   //结果置信度
    cv::Rect box;       //矩形框
    cv::Mat boxMask;       //矩形框内mask,节省内存空间和加快速度
};
struct MaskParams {
    int segChannels = 32;
    int segWidth = 160;
    int segHeight = 160;
    int netWidth = 640;
    int netHeight = 640;
    float maskThreshold = 0.5;
    cv::Size srcImgShape;
    cv::Vec4d params;

};
bool CheckParams(int netHeight, int netWidth, const int* netStride, int strideSize);
void DrawPred(cv::Mat& img, std::vector<OutputSeg> result, std::vector<std::string> classNames, std::vector<cv::Scalar> color);
void LetterBox(const cv::Mat& image, cv::Mat& outImage,
    cv::Vec4d& params, //[ratio_x,ratio_y,dw,dh]
    const cv::Size& newShape = cv::Size(640, 640),
    bool autoShape = false,
    bool scaleFill = false,
    bool scaleUp = true,
    int stride = 32,
    const cv::Scalar& color = cv::Scalar(114, 114, 114));
void GetMask(const cv::Mat& maskProposals, const cv::Mat& maskProtos, std::vector<OutputSeg>& output, const MaskParams& maskParams);
void GetMask2(const cv::Mat& maskProposals, const cv::Mat& maskProtos, OutputSeg& output, const MaskParams& maskParams);

yolov5_seg_utils.cpp

#pragma once
#include "yolov5_seg_utils.h"
using namespace cv;
using namespace std;
bool CheckParams(int netHeight, int netWidth, const int* netStride, int strideSize) {
    if (netHeight % netStride[strideSize - 1] != 0 || netWidth % netStride[strideSize - 1] != 0)
    {
        cout << "Error:_netHeight and _netWidth must be multiple of max stride " << netStride[strideSize - 1] << "!" << endl;
        return false;
    }
    return true;
}

void LetterBox(const cv::Mat& image, cv::Mat& outImage, cv::Vec4d& params, const cv::Size& newShape,
    bool autoShape, bool scaleFill, bool scaleUp, int stride, const cv::Scalar& color)
{
    if (false) {
        int maxLen = MAX(image.rows, image.cols);
        outImage = Mat::zeros(Size(maxLen, maxLen), CV_8UC3);
        image.copyTo(outImage(Rect(0, 0, image.cols, image.rows)));
        params[0] = 1;
        params[1] = 1;
        params[3] = 0;
        params[2] = 0;
    }

    cv::Size shape = image.size();
    float r = std::min((float)newShape.height / (float)shape.height,
        (float)newShape.width / (float)shape.width);
    if (!scaleUp)
        r = std::min(r, 1.0f);

    float ratio[2]{ r, r };
    int new_un_pad[2] = { (int)std::round((float)shape.width * r),(int)std::round((float)shape.height * r) };

    auto dw = (float)(newShape.width - new_un_pad[0]);
    auto dh = (float)(newShape.height - new_un_pad[1]);

    if (autoShape)
    {
        dw = (float)((int)dw % stride);
        dh = (float)((int)dh % stride);
    }
    else if (scaleFill)
    {
        dw = 0.0f;
        dh = 0.0f;
        new_un_pad[0] = newShape.width;
        new_un_pad[1] = newShape.height;
        ratio[0] = (float)newShape.width / (float)shape.width;
        ratio[1] = (float)newShape.height / (float)shape.height;
    }

    dw /= 2.0f;
    dh /= 2.0f;

    if (shape.width != new_un_pad[0] && shape.height != new_un_pad[1])
    {
        cv::resize(image, outImage, cv::Size(new_un_pad[0], new_un_pad[1]));
    }
    else {
        outImage = image.clone();
    }

    int top = int(std::round(dh - 0.1f));
    int bottom = int(std::round(dh + 0.1f));
    int left = int(std::round(dw - 0.1f));
    int right = int(std::round(dw + 0.1f));
    params[0] = ratio[0];
    params[1] = ratio[1];
    params[2] = left;
    params[3] = top;
    cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
}

void GetMask(const cv::Mat& maskProposals, const cv::Mat& maskProtos, std::vector<OutputSeg>& output, const MaskParams& maskParams) {
    //cout << maskProtos.size << endl;

    int seg_channels = maskParams.segChannels;
    int net_width = maskParams.netWidth;
    int seg_width = maskParams.segWidth;
    int net_height = maskParams.netHeight;
    int seg_height = maskParams.segHeight;
    float mask_threshold = maskParams.maskThreshold;
    Vec4f params = maskParams.params;
    Size src_img_shape = maskParams.srcImgShape;

    Mat protos = maskProtos.reshape(0, { seg_channels,seg_width * seg_height });

    Mat matmul_res = (maskProposals * protos).t();
    Mat masks = matmul_res.reshape(output.size(), { seg_width,seg_height });
    vector<Mat> maskChannels;
    split(masks, maskChannels);
    for (int i = 0; i < output.size(); ++i) {
        Mat dest, mask;
        //sigmoid
        cv::exp(-maskChannels[i], dest);
        dest = 1.0 / (1.0 + dest);

        Rect roi(int(params[2] / net_width * seg_width), int(params[3] / net_height * seg_height), int(seg_width - params[2] / 2), int(seg_height - params[3] / 2));
        dest = dest(roi);
        resize(dest, mask, src_img_shape, INTER_NEAREST);

        //crop
        Rect temp_rect = output[i].box;
        mask = mask(temp_rect) > mask_threshold;
        output[i].boxMask = mask;
    }
}
void GetMask2(const Mat& maskProposals, const Mat& mask_protos, OutputSeg& output, const MaskParams& maskParams) {
    int seg_channels = maskParams.segChannels;
    int net_width = maskParams.netWidth;
    int seg_width = maskParams.segWidth;
    int net_height = maskParams.netHeight;
    int seg_height = maskParams.segHeight;
    float mask_threshold = maskParams.maskThreshold;
    Vec4f params = maskParams.params;
    Size src_img_shape = maskParams.srcImgShape;

    Rect temp_rect = output.box;
    // 把已经到原图的检测框坐标信息  映射到  获得mask原型分支的输入尺寸上【160, 160】
    int rang_x = floor((temp_rect.x * params[0] + params[2]) / net_width * seg_width);
    int rang_y = floor((temp_rect.y * params[1] + params[3]) / net_height * seg_height);
    int rang_w = ceil(((temp_rect.x + temp_rect.width) * params[0] + params[2]) / net_width * seg_width) - rang_x;
    int rang_h = ceil(((temp_rect.y  + temp_rect.height) * params[0] + params[3]) / net_width * seg_height) - rang_y;

    //
    rang_w = MAX(rang_w, 1);
    rang_h = MAX(rang_h, 1);
    if (rang_x + rang_w > seg_width){
        if (seg_width - rang_x > 0)
            rang_w =seg_width -rang_x;
        else
            rang_x -= 1;
    }
    if (rang_y + rang_h > seg_height) {
        if (seg_height - rang_y > 0)
            rang_h = seg_height - rang_y;
        else
            rang_y -= 1;
    }

    vector<Range> roi_ranges;
    roi_ranges.push_back(Range(0,1));
    roi_ranges.push_back(Range::all());
    roi_ranges.push_back(Range(rang_y, rang_h+rang_y));
    roi_ranges.push_back(Range(rang_x, rang_w+rang_x));

    // 裁剪mask原型
    Mat temp_mask_protos = mask_protos(roi_ranges).clone(); // 剪裁原型,保存检测框内部的原型,其余位置清零,  以此来获得感兴趣区域(roi)
    Mat protos = temp_mask_protos.reshape(0, { seg_channels, rang_w*rang_h});// 检测至检测框大小?

    // mask系数与mask原型做矩阵乘法
    Mat matmul_res = (maskProposals * protos).t(); // mask系数【1,32】 与 mask原型【32, h*w】进行矩阵相称
    Mat masks_feature = matmul_res.reshape(1,{rang_h, rang_w}); //【1,h,w】
    Mat dest, mask;

    // sigmod
    cv::exp(-masks_feature, dest);
    dest = 1.0 / (1.0 + dest);

    // 检测框坐标 映射到 原图尺寸
    int left = floor((net_width / seg_width * rang_x - params[2]) / params[0]);
    int top = floor((net_width / seg_height * rang_y - params[3]) / params[1]);
    int width = ceil(net_width / seg_height * rang_w / params[0]);
    int height = ceil(net_height / seg_height * rang_h / params[1]);

    // 检测框mask缩放到原图尺寸
    resize(dest, mask, Size(width, height), INTER_NEAREST);

    // 阈值化
    mask = mask(temp_rect - Point(left, top)) > mask_threshold;
    output.boxMask = mask;
}

void DrawPred(Mat& img, vector<OutputSeg> result, std::vector<std::string> classNames, vector<Scalar> color) {
    Mat mask = img.clone();
    for (int i=0; i< result.size(); i++){
        int left, top;
        left = result[i].box.x;
        top = result[i].box.y;
        int color_num =i;

        // 目标画框
        rectangle(img, result[i].box, color[result[i].id], 2, 8);

        // 目标mask,这里非目标像素值为0
        mask(result[i].box).setTo(color[result[i].id], result[i].boxMask);
        string label = classNames[result[i].id] + ":" + to_string(result[i].confidence);

        // 框左上角打印信息
        int baseLine;
        Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
        top = max(top, labelSize.height);
        putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.5, color[result[i].id], 2);
    }

    addWeighted(img, 0.5, mask, 0.5, 0, img);
}

yolo_seg.h

#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>
#include "yolov5_seg_utils.h"

class YoloSeg {
public:
    YoloSeg() {
    }
    ~YoloSeg() {}
    /** \brief Read onnx-model
    * \param[out] read onnx file into cv::dnn::Net
    * \param[in] modelPath:onnx-model path
    * \param[in] isCuda:if true and opencv built with CUDA(cmake),use OpenCV-GPU,else run it on cpu.
    */
    bool ReadModel(cv::dnn::Net& net, std::string& netPath, bool isCuda);
    /** \brief  detect.
    * \param[in] srcImg:a 3-channels image.
    * \param[out] output:detection results of input image.
    */
    bool Detect(cv::Mat& srcImg, cv::dnn::Net& net, std::vector<OutputSeg>& output);

#if(defined YOLO_P6 && YOLO_P6==true)

    const int _netWidth = 1280;  //ONNX图片输入宽度
    const int _netHeight = 1280; //ONNX图片输入高度
    const int _segWidth = 320;  //_segWidth=_netWidth/mask_ratio
    const int _segHeight = 320;
    const int _segChannels = 32;
#else

    const int _netWidth = 640;   //ONNX图片输入宽度
    const int _netHeight = 640;  //ONNX图片输入高度
    const int _segWidth = 160;    //_segWidth=_netWidth/mask_ratio
    const int _segHeight = 160;
    const int _segChannels = 32;

#endif // YOLO_P6

    float _classThreshold = 0.25;
    float _nmsThreshold = 0.45;
    float _maskThreshold = 0.5;

public:
    std::vector<std::string> _className = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
        "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
        "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
        "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
        "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
        "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
        "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
        "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
        "hair drier", "toothbrush" };//类别名,换成自己的模型需要修改此项
};

yolo_seg.cpp

#include"yolo_seg.h"
using namespace std;
using namespace cv;
using namespace cv::dnn;

bool YoloSeg::ReadModel(Net& net, string& netPath, bool isCuda = false) {
    try {
        net = readNet(netPath);
#if CV_VERSION_MAJOR==4 &&CV_VERSION_MINOR==7&&CV_VERSION_REVISION==0
        net.enableWinograd(false);  //bug of opencv4.7.x in AVX only platform ,https://github.com/opencv/opencv/pull/23112 and https://github.com/opencv/opencv/issues/23080
        //net.enableWinograd(true);		//If your CPU supports AVX2, you can set it true to speed up
#endif
    }
    catch (const std::exception&) {
        return false;
    }
    if (isCuda) {
        //cuda
        net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
        net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); //or DNN_TARGET_CUDA_FP16
    }
    else {
        //cpu
        cout << "Inference device: CPU" << endl;
        net.setPreferableBackend(cv::dnn::DNN_BACKEND_DEFAULT);
        net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
    }
    return true;
}


bool YoloSeg::Detect(Mat& srcImg, Net& net, vector<OutputSeg>& output) {
    Mat blob;
    output.clear();
    int col = srcImg.cols;
    int row = srcImg.rows;
    Mat netInputImg;
    Vec4d params;
    LetterBox(srcImg, netInputImg, params, cv::Size(_netWidth, _netHeight));
    blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(_netWidth, _netHeight), cv::Scalar(0, 0, 0), true, false);
    //**************************************************************************************************************************************************/
    //如果在其他设置没有问题的情况下但是结果偏差很大,可以尝试下用下面两句语句
    // If there is no problem with other settings, but results are a lot different from  Python-onnx , you can try to use the following two sentences
    //
    //$ blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(_netWidth, _netHeight), cv::Scalar(104, 117, 123), true, false);
    //$ blobFromImage(netInputImg, blob, 1 / 255.0, cv::Size(_netWidth, _netHeight), cv::Scalar(114, 114,114), true, false);
    //****************************************************************************************************************************************************/
    net.setInput(blob);
    std::vector<cv::Mat> net_output_img;
    //*********************************************************************************************************************************
    //net.forward(net_output_img, net.getUnconnectedOutLayersNames());
    //opencv4.5.x和4.6.x这里输出不一致,推荐使用下面的固定名称输出
    // 如果使用net.forward(net_output_img, net.getUnconnectedOutLayersNames()),需要确认下net.getUnconnectedOutLayersNames()返回值中output0在前,output1在后,否者出错
    //
    // The outputs of opencv4.5.x and 4.6.x are inconsistent.Please make sure "output0" is in front of "output1" if you use net.forward(net_output_img, net.getUnconnectedOutLayersNames())
    //*********************************************************************************************************************************
    vector<string> output_layer_names{ "output0","output1" };
    net.forward(net_output_img, output_layer_names); //获取output的输出

    std::vector<int> class_ids;//结果id数组
    std::vector<float> confidences;//结果每个id对应置信度数组
    std::vector<cv::Rect> boxes;//每个id矩形框
    std::vector<vector<float>> picked_proposals;  //output0[:,:, 5 + _className.size():net_width]===> for mask
    int net_width = _className.size() + 5 + _segChannels;// 80 + 5 + 32 = 117
    int out0_width= net_output_img[0].size[2];

//    assert(net_width == out0_width, "Error Wrong number of _className or _segChannels");  //模型类别数目不对或者_segChannels设置错误
    int net_height = net_output_img[0].size[1];// 25200
    float* pdata = (float*)net_output_img[0].data;
    for (int r = 0; r < net_height; r++) {    //lines
        float box_score = pdata[4];
        if (box_score >= _classThreshold) {
            cv::Mat scores(1, _className.size(), CV_32FC1, pdata + 5); //  可是 后面不只是有80个类别的概率;
            Point classIdPoint;
            double max_class_socre;
            minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
            max_class_socre = (float)max_class_socre;
            if (max_class_socre >= _classThreshold) {

                vector<float> temp_proto(pdata + 5 + _className.size(), pdata + net_width); // Mask Coeffcients,mask的掩码系数
                picked_proposals.push_back(temp_proto);
                //rect [x,y,w,h]
                float x = (pdata[0] - params[2]) / params[0];  //x
                float y = (pdata[1] - params[3]) / params[1];  //y
                float w = pdata[2] / params[0];  //w
                float h = pdata[3] / params[1];  //h
                int left = MAX(int(x - 0.5 * w + 0.5), 0);
                int top = MAX(int(y - 0.5 * h + 0.5), 0);
                class_ids.push_back(classIdPoint.x);
                confidences.push_back(max_class_socre * box_score);
                boxes.push_back(Rect(left, top, int(w + 0.5), int(h + 0.5)));
            }
        }
        pdata += net_width;//下一行

    }

    //NMS
    vector<int> nms_result;
    cv::dnn::NMSBoxes(boxes, confidences, _classThreshold, _nmsThreshold, nms_result);
    std::vector<vector<float>> temp_mask_proposals;
    Rect holeImgRect(0, 0, srcImg.cols, srcImg.rows);
    for (int i = 0; i < nms_result.size(); ++i) {

        int idx = nms_result[i];
        OutputSeg result;
        result.id = class_ids[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx] & holeImgRect;
        temp_mask_proposals.push_back(picked_proposals[idx]);
        output.push_back(result);
    }

    MaskParams mask_params;
    mask_params.params = params;
    mask_params.srcImgShape = srcImg.size();
    for (int i = 0; i < temp_mask_proposals.size(); ++i) {
        GetMask2(Mat(temp_mask_proposals[i]).t(), net_output_img[1], output[i], mask_params); // 注意这里是net_output_img[1],为原型mask
    }


    //******************** ****************
    // 老版本的方案,如果上面GetMask2出错,建议使用这个。
    // If the GetMask2() still reports errors , it is recommended to use GetMask().
    // Mat mask_proposals;
    //for (int i = 0; i < temp_mask_proposals.size(); ++i)
    //	mask_proposals.push_back(Mat(temp_mask_proposals[i]).t());
    //GetMask(mask_proposals, net_output_img[1], output, mask_params);
    //*****************************************************/


    if (output.size())
        return true;
    else
        return false;
}

main.cpp

#include <iostream>
#include<opencv2/opencv.hpp>
#include "yolo_seg.h"

#include<sys/time.h>

using namespace std;
using namespace cv;
using namespace dnn;




int main() {
    //yolov5(); //https://github.com/UNeedCryDear/yolov5-opencv-dnn-cpp

    string model_path = "/home/jason/PycharmProjects/pytorch_learn/yolo/yolov5-7.0/yolov5n-seg.onnx";
    YoloSeg test;
    Net net;
    if (test.ReadModel(net, model_path, true)) {
        cout << "read net ok!" << endl;
    }
    else {
        return -1;
    }
    //生成随机颜色
    vector<Scalar> color;
    srand(time(0));
    for (int i = 0; i < 80; i++) {
        int b = rand() % 256;
        int g = rand() % 256;
        int r = rand() % 256;
        color.push_back(Scalar(b, g, r));
    }


    VideoCapture capture(0);

    struct timeval t1, t2;
    double timeuse;



    Mat img;
    while (1) {
        capture >> img;
       vector<OutputSeg> result;


       gettimeofday(&t1, NULL);
       bool find = test.Detect(img, net, result);
       gettimeofday(&t2, NULL);
       if (find) {
            DrawPred(img, result, test._className, color);
        }
        else {
            cout << "Detect Failed!"<<endl;
        }

       timeuse = (t2.tv_sec - t1.tv_sec) + (double)(t2.tv_usec - t1.tv_usec)/1000000; //s
       string label = "duration:" + to_string(timeuse*1000); //ms
       putText(img, label, Point(30,30), FONT_HERSHEY_SIMPLEX,0.5, Scalar(0,0,255), 2, 8);


        imshow("result", img);
        if(waitKey(1)=='q') break;

    }
    return 0;
}

三、效果

图片输入:

yolov5图像分割,# 图像分割,opencv,YOLO,dnnyolov5图像分割,# 图像分割,opencv,YOLO,dnn

摄像头输入:

yolov5图像分割,# 图像分割,opencv,YOLO,dnn

 Yolov5n-seg用OpenCV DNN 进行C++部署,在CPU上居然200多ms,我承认与yolov5 检测相比输出变大,运算量也会增加。但是Yolov5n-seg在Python推理才100ms左右,可能是python的矩阵运算确实很快??有大佬说下原因吗?

觉得有帮助点个赞哦!

 文章来源地址https://www.toymoban.com/news/detail-527537.html

到了这里,关于YOLOv5 实例分割 用 OPenCV DNN C++ 部署的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【深度学习】YOLOv5实例分割 数据集制作、模型训练以及TensorRT部署

    yolov5-seg:官方地址:https://github.com/ultralytics/yolov5/tree/v6.2 TensorRT:8.x.x 语言:C++ 系统:ubuntu18.04 前言:由于yolo仓中提供了标准coco的json文件转txt代码,因此需要将labelme的json文件转为coco json. labelme JSON 转COCO JSON 使用labelme的CreatePolygons按钮开始绘制多边形,然后保存为json格式。

    2024年02月06日
    浏览(40)
  • OpenCV DNN模块推理YOLOv5 ONNX模型方法

    本文档主要描述 python 平台,使用 opencv-python 深度神经网络模块 dnn ,推理 YOLOv5 模型的方法。 文档主要包含以下内容: opencv-python 模块的安装 YOLOv5 模型格式的说明 ONNX 格式模型的加载 图片数据的预处理 模型推理 推理结果后处理,包括 NMS , cxcywh 坐标转换为 xyxy 坐标等 关键方

    2024年02月16日
    浏览(39)
  • YOLOv5 实例分割入门

    YOLOv5 目标检测模型以其出色的性能和优化的推理速度而闻名。因此, YOLOv5 实例分割 模型已成为实时实例分割中最快、最准确的模型之一。  在这篇文章中,我们将回答以下关于 YOLOv5 实例分割的问题: YOLOv5检测模型做了哪些改动,得到了YOLOv5实例分割架构? 使用的 ProtoN

    2024年02月05日
    浏览(30)
  • YOLOv5系列全新升级——yolov5-v7.0实时实例分割全面集成

    自从YOLOv5诞生依赖,社区就很活动,官方的更新频度也很高,检测系列一路迭代升级,集成融合了各种新颖的技术和tricks,目前最新已经更新到了v6.1版本,在我之前的博客里面也有详细教程讲解,感兴趣的话可以自行移步,文章如下: 《基于自建数据集【海底生物检测】使

    2024年02月01日
    浏览(44)
  • C++模型部署:qt+yolov5/6+onnxruntime+opencv

    作者平时主要是写 c++ 库的,界面方面了解不多,也没有发现“美”的眼镜,界面有点丑,大家多包涵。 本次介绍的项目主要是通过 cmake 构建一个 基于 c++ 语言的,以 qt 为框架的,包含 opencv 第三方库在内的,跨平台的,使用 ONNX RUNTIME 进行前向推理的 yolov5/6 演示平台。文章

    2024年02月05日
    浏览(37)
  • 【目标检测】YOLOv5-7.0:加入实例分割

    前段时间,YOLOv5推出7.0版本,主要更新点是在目标检测的同时引入了实例分割。 目前,YOLOv5团队已经转向了YOLOv8的更新,因此,7.0版本大概率是YOLOv5的最终稳定版。 官方公告中给出了YOLOv5-7.0的更新要点: 推出了基于coco-seg的实例分割预训练模型 支持Paddle Paddle模型导出 自动

    2024年02月11日
    浏览(26)
  • Opencv C++实现yolov5部署onnx模型完成目标检测

    头文件 命名空间 结构体 Net_config 里面存了三个阈值和模型地址,其中 置信度 ,顾名思义,看检测出来的物体的精准度。以测量值为中心,在一定范围内,真值出现在该范围内的几率。 endsWith()函数 判断sub是不是s的子串 anchors_640图像接收数组 根据图像大小,选择相应长度的

    2024年02月13日
    浏览(29)
  • 2021.11.01 c++下 opencv部署yolov5-6.0版本 (四)

    ----2022.10.10 更新yolov5-seg实例分割模型: 2022.09.29更新 c++下面使用opencv部署yolov5和yolov7实例分割模型(六)_爱晚乏客游的博客-CSDN博客  -----2022.07.25 更新了下yolov7的部署,有需要的自取 2022.07.25 C++下使用opencv部署yolov7模型(五)_爱晚乏客游的博客-CSDN博客 此篇文章针对yolov5的

    2024年02月04日
    浏览(32)
  • yolov5-v7.0实例分割快速体验

    🚀 yolov5-v7.0 版本正式发布,本次更新的 v7.0 则是全面的大版本升级,最主要的功能就是全面集成支持了实例分割,yolov5已经集成检测、分类、分割任务。 前面几篇文章已经介绍过关于Yolov5的一些方面 yolov5目标检测:https://blog.csdn.net/qq_45066628/article/details/129470290?spm=1001.2014.30

    2024年02月09日
    浏览(26)
  • 用opencv的DNN模块做Yolov5目标检测(纯干货,源码已上传Github)

    最近在微信公众号里看到多篇讲解yolov5在openvino部署做目标检测文章,但是没看到过用opencv的dnn模块做yolov5目标检测的。于是,我就想着编写一套用opencv的dnn模块做yolov5目标检测的程序。在编写这套程序时,遇到的bug和解决办法,在这篇文章里讲述一下。 在yolov5之前的yolov3和

    2024年02月02日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包