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

news2024/10/5 7:28:29

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;
}

三、效果

图片输入:

摄像头输入:

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

觉得有帮助点个赞哦!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/508565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL 字段为 NULL 的坑,你踩过吗?

前言 很多小知识点&#xff0c;我以为自己懂了&#xff0c;实际没搞透。 数据库字段允许空值(null)的问题&#xff0c;你遇到过吗&#xff1f; 在验证问题之前&#xff0c;我们先建一张测试表及测试数据。 构建的测试数据&#xff0c;如下图所示&#xff1a; 有了上面的表及…

RHCSA 作业一

[rootserver ~]# mkdir /opt/tmp #在/opt目录下创建一个目录tmp [rootserver ~]# ls /opt tmp [rootserver ~]# touch /opt/tmp/a.txt #在tmp目录下新建一个文件a.txt [rootserver ~]# ls /opt/tmp a.txt [rootserver ~]# cd /opt/tmp #进入tmp目录下 [rootserver tmp]# …

大比拼:讯飞星火大模型将超越ChatGPT?

5月6日&#xff0c;讯飞星火认知大模型成果发布会于合肥举办。会上&#xff0c;备受业界期待的“星火”认知大模型正式发布&#xff0c;讯飞AI学习机、讯飞听见、讯飞智能办公本、讯飞智慧驾舱、讯飞数字员工&#xff0c;四大行业中的五大成果同步演示&#xff0c;发布会全程进…

Hive经典面试题——连续7天登录用户与TopN

目录 一、连续7天登录用户 1.数据准备ulogin.dat 2.建表与加载数据 3. 使用 row_number 在组内给数据编号(rownum) 4.某个值 - rownum gid&#xff0c;得到结果可以作为后面分组计算的依据 5.根据求得的gid&#xff0c;作为分组条件&#xff0c;求最终结果 二、求TopN …

Overcoming catastrophic forgetting in neural networks

目录 预备知识&#xff1a; 论文笔记 1. Introduction 2. Elastic weight consolidation 2.1 EWC allows continual learning in a supervised learning context 2.2 EWC allows continual learning in a reinforcement learning context 3. Conclusion 文章链接&#x…

100ASK_全志V853-PRO开发板支持人形检测和人脸识别

1.前言 V853 芯片内置一颗 NPU核&#xff0c;其处理性能为最大 1 TOPS 并有 128KB 内部高速缓存用于高速数据交换&#xff0c;支持 OpenCL、OpenVX、android NN 与 ONNX 的 API 调用&#xff0c;同时也支持导入大量常用的深度学习模型。本章提供一个例程&#xff0c;展示如何使…

JavaScript基础之数值计算

常见的几种场景 场景一&#xff1a;进行浮点值运算结果的判断 常见错误写法&#xff1a;floatNum1 floatNum2 res 我们在Chrome里测试一下 0.1 0.2 0.3&#xff0c;得出的结果是false&#xff0c;而不是预期结果true&#xff0c;因为 0.1 0.2 0.30000000000000004 场…

【Vue-Treeselect 和 vue3-treeselect】树形下拉框

Vue-Treeselect Vue2树形下拉框 链接 文档&#xff1a;Vue-Treeselect 实现 第一步&#xff1a;安装 npm install --save riophae/vue-treeselect 第二步:实现 import Treeselect from riophae/vue-treeselect import riophae/vue-treeselect/dist/vue-treeselect.css属性…

python数据类型总结

标准数据类型 Python 有以下几种标准数据类型&#xff1a; 整数&#xff08;int&#xff09;&#xff1a;表示整数值&#xff0c;如 1, -5, 0 等。浮点数&#xff08;float&#xff09;&#xff1a;表示小数值&#xff0c;如 3.14, -0.01, 1.0 等。字符串&#xff08;str&…

AI智能音箱高性价比出好音质的功放芯片

近几年人工智能等技术的不断发展&#xff0c;AI智能音箱已成为炙手可热的爆款&#xff1b;众多企业纷纷加入其中&#xff1b;如我们熟知的天猫精灵、小爱同学、小度智能音箱、华为AI音箱、腾讯叮当等等智能音箱&#xff1b;据不完全统计&#xff0c;目前国内做智能音箱的企业已…

SpringBoot——创建一个SpringBoot工程

简单介绍&#xff1a; 在之前我们学习JavaEE的时候&#xff0c;是直接使用Spring进行操作&#xff0c;以比较原始的方式进行了SSM的整合&#xff0c;这次我们就来学习一个强大的框架——SpringBoot&#xff0c;这个框架是用来简化Spring应用的初始化创建过程&#xff0c;以及开…

APP外包项目的代码规范

APP项目在工作中使用越来越多&#xff0c;2C的APP项目基本饱和&#xff0c;2B的AP项目P还有很大的发展空间。越来越多的企业希望通过APP来提升工作效率或加强和客户的沟通&#xff0c;但这些企业大多数不是专业的软件公司&#xff0c;开发软件时需要找软件外包开发公司&#xf…

C/C++每日一练(20230510) 编辑距离、多数元素、数列累和

目录 1. 编辑距离 &#x1f31f;&#x1f31f;&#x1f31f; 2. 多数元素 &#x1f31f; 3. 求分数数列的前N项和 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 编辑距离 给你…

OpenCV教程——Mat对象

1.Mat对象和IplIamge对象 Mat对象是OpenCV2.0之后引进的图像数据结构、自动分配内存、不存在内存泄漏的问题&#xff0c;是面向对象的数据结构。分为两个部分&#xff1a;头部和数据部分。IplIamge是从2001年OpenCV发布之后就一直存在&#xff0c;是C语言风格的数据结构&#…

笔记本电脑没有声音怎么办?5个必会方法分享

案例&#xff1a;笔记本电脑没有声音怎么办&#xff1f; 【我的笔记本电脑为什么会没有声音呢&#xff1f;看视频听音乐一点声音都没有&#xff0c;实在太烦人了&#xff01;应该怎么解决呢&#xff1f;】 笔记本电脑逐渐成为人们工作生活必备的工具。如果笔记本电脑没有声音…

怎样检测和维护LED显示屏系统

检测和维护LED显示屏系统是确保其正常运行和延长寿命的重要步骤。以下是一些常见的检测和维护LED显示屏系统的方法&#xff1a; 视觉检查&#xff1a;定期进行视觉检查以确保LED显示屏没有明显的损坏或故障。检查显示屏表面是否有损坏、裂纹或漏光等情况。如果发现任何问题&…

ChatGPT作者John Schulman:通往TruthGPT之路

OneFlow编译 翻译&#xff5c;贾川、徐佳渝、杨婷 大型语言模型&#xff08;LLM&#xff09;有一个众所周知的“硬伤”——它们经常会一本正经编造貌似真实的内容。 OpenAI团队希望通过改进强化学习反馈步骤“原生地”阻止神经网络产生幻觉&#xff0c;OpenAI首席科学家Ilya …

spark-sql 报错:Exception thrown flushing changes to datastore

报错背景 hive创建数据库时添加中文备注信息报错。 命令&#xff1a;CREATE DATABASE IF NOT EXISTS hive_ods_db COMMENT Hive ODS层数据库; 报错现象 FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. MetaException(message:Excep…

递归行为与归并排序

master公式 T(N)a*T(N/b)O(N^d) T(N)&#xff1a;问题的规模是N个数据 N/b&#xff1a;子过程的规模 a&#xff1a;调用的次数 O(N^d) &#xff1a;除子问题的调用之外&#xff0c;剩余的代码的时间复杂度 使用条件&#xff1a;满足子问题等规模的递归 arr[L,R]范围…

49天精通Java,第27天,队列、双端队列、优先队列

目录 一、队列与双端队列二、Queue和Deque三、api对比1、add和offer区别2、remove和poll3、element和peek 四、优先队列1、PriorityQueue常用方法2、ArrayDeque常用方法 大家好&#xff0c;我是哪吒。 一、队列与双端队列 双端队列是一种特殊的队列&#xff0c;它的两端都可以…