win10系统下使用onnxruntime部署yolov5模型

news2024/11/25 6:56:19

文章目录

  • 前言
  • 一、环境
    • 1、硬件
    • 2、软件
  • 二、YOLO模型
  • 三、新建Qt项目
    • 1、pro文件
    • 2、mainwindow.h
    • 3、mainwindow.cpp
  • 四、YOLO 类封装
    • 1、yolov5.h
    • 2、yolov5.cpp
    • 3、class.names
  • 五、效果


前言

  上一篇介绍过使用opencv-dnn模块实现模型推理部署,但视频效果较差,本篇介绍使用onnxruntime完成模型推理部署。

一、环境

1、硬件

Intel® Core i5-7400 CPU @ 3.00GHZ
Intel® HD Graphics 630 内存4G 核显
内存 8G
win10 64位系统

2、软件

opencv4.2.0
yolov5 6.2版本
qt5.6.2
onnxruntime-win-x86-1.11.1

二、YOLO模型

我使用的是onnx模型,如果没有训练过自己的模型,可以使用官方的yolov5s.pt模型,将其转化为yolov5s.onnx模型,转化方法如下:

python export.py

在yolov5-master目录下,可以看到yolov5s.onnx模型已生成。

三、新建Qt项目

Qt项目文件结构如下:
在这里插入图片描述

1、pro文件

在pro文件中,添加opencv相关配置,内容如下:

#-------------------------------------------------
#
# Project created by QtCreator 2022-10-17T11:16:52
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = yolov5-onnxruntime-cpp
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp \
    algorithm/process_common.cpp \
    algorithm/qt_convert.cpp \
    algorithm/video.cpp \
    qt_extension/display.cpp \
    yolo/utils.cpp \
    yolo/yolov5/yolov5.cpp \
    yolo/yolov6/yolov6.cpp

HEADERS  += mainwindow.h \
    algorithm/process_common.hpp \
    algorithm/qt_convert.hpp \
    algorithm/video.hpp \
    common/cmdline.h \
    common/common.hpp \
    common/debug_printf.hpp \
    common/my_exception.hpp \
    qt_extension/display.hpp \
    yolo/utils.hpp \
    yolo/yolov5/yolov5.hpp \
    yolo/yolov6/yolov6.hpp

FORMS    += mainwindow.ui


#INCLUDEPATH += C:/opencv4.6.0/build/include
#               C:/opencv4.6.0/build/include/opencv2
#LIBS += -LC:/opencv4.6.0/build/x64/vc14/lib/ -lopencv_world460

#INCLUDEPATH += C:/onnxruntime-win-x64-gpu-1.11.1/include
#LIBS += -LC:/onnxruntime-win-x64-gpu-1.11.1/lib/ -lonnxruntime
#LIBS += -LC:/onnxruntime-win-x64-gpu-1.11.1/lib/ -lonnxruntime_providers_cuda
#LIBS += -LC:/onnxruntime-win-x64-gpu-1.11.1/lib/ -lonnxruntime_providers_shared
#LIBS += -LC:/onnxruntime-win-x64-gpu-1.11.1/lib/ -lonnxruntime_providers_tensorrt

#INCLUDEPATH += C:/onnxruntime-win-x64-1.11.1/include
#LIBS += -LC:/onnxruntime-win-x64-1.11.1/lib/ -lonnxruntime

INCLUDEPATH +=  C:/opencv4.2.0_build/install/include
                C:/opencv4.2.0_build/install/include/opencv2

LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_core420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_highgui420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_imgproc420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_calib3d420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_imgcodecs420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_objdetect420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_shape420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_dnn_objdetect420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_dnn420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_video420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_videoio420
LIBS += -LC:/opencv4.2.0_build/install/x86/vc14/lib/ -lopencv_videostab420

INCLUDEPATH += C:/onnxruntime-win-x86-1.11.1/include
LIBS += -LC:/onnxruntime-win-x86-1.11.1/lib/ -lonnxruntime

2、mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QMainWindow>
#include <QTimer>
#include <iostream>
#include <QFileDialog>
#include <QMessageBox>
#include <QImage>


#include "opencv2/opencv.hpp"
#include "common/my_exception.hpp"
#include "algorithm/process_common.hpp"
#include "algorithm/qt_convert.hpp"
#include "algorithm/video.hpp"
#include "qt_extension/display.hpp"
#include "yolo/yolov5/yolov5.hpp"
#include "yolo/yolov6/yolov6.hpp"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_class_names_choice_clicked();
    void on_yolov5_6_choice_clicked();
    void on_open_picture_clicked();
    void on_open_camera_clicked();
    void on_close_camera_clicked();
    void read_frame();
    void on_test_button_clicked();
    void on_save_picture_clicked();
    void on_video_choice_clicked();
    void on_play_video_clicked();
    void on_pause_video_clicked();
    void on_end_video_clicked();

private:
    Ui::MainWindow *ui;
    //
    QImage  img;
    QTimer  *capture_timer = nullptr;
    Video   my_video;
    YOLO    my_yolo;

    clock_t start_name = 0;
    clock_t end_time = 0;

    bool    initialization();  // TODO 待完善
};

#endif // MAINWINDOW_H

3、mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_open_picture_clicked()
{
    QString curDir = QDir::currentPath(); // 获取程序的当前路径
    QString filename = QFileDialog::getOpenFileName(this, "select Image", curDir,
                                                    "Images (*.png *.bmp *.jpg *.tif *.GIF )");
    if (filename.isEmpty())
    {
        QMessageBox::warning(this, u8"警告", u8"未选中图像。");
        return;
    }

    //TODO 暂未判空
    if (!(img.load(filename))) //加载图像
    {
        QMessageBox::warning(this, u8"警告", u8"打开图像失败");
    }

    qDebug() << u8"打开图像" + filename;
    img = img.convertToFormat(QImage::Format_RGB32);

    if (ui->yolov5_6->isChecked())
    {
        std::string model_path = ui->yolov5_6_path->text().toStdString();
        bool is_gpu = ui->ort_gpu->isChecked();

        auto size = cv::Size(640, 640);
        if (ui->size_320->isChecked())
        {
            size = cv::Size(320, 320);
        }

        my_yolo = YOLO(model_path, is_gpu, size);

        float confThreshold = ui->conf->value();
        float iouThreshold = ui->iou->value();
        std::vector<std::string> class_names = yolo_utils::loadNames(ui->class_names_path->text().toStdString());
        auto src_img = QImage_2_cvMat(img);

        auto result = my_yolo.detect(src_img, confThreshold, iouThreshold);  //TODO 待完善结果显示
        yolo_utils::visualizeDetection(src_img, result, class_names,
                                       ui->Box_dispaly->isChecked(), ui->class_dispaly->isChecked());
        img = cvMat_2_QImage(src_img);
    }
    align_center(img, ui->display);
}

void MainWindow::on_open_camera_clicked()
{
    try
    {
        my_video = Video();
        my_video.open_video();

        capture_timer = new QTimer(this);
        connect(capture_timer, SIGNAL(timeout()), this, SLOT(read_frame())); // 时间到,读取当前摄像头信息

        if (ui->yolov5_6->isChecked())
        {
            std::string model_path = ui->yolov5_6_path->text().toStdString();
            bool is_gpu = ui->ort_gpu->isChecked();

            auto size = cv::Size(640, 640);
            if (ui->size_320->isChecked())
            {
                size = cv::Size(320, 320);
            }

            my_yolo = YOLO(model_path, is_gpu, size);
        }

        capture_timer->start(1); // 开始计时,超时则发出timeout()信号
    }
    catch (MyException ex)
    {
        QMessageBox::warning(this, u8"警告", ex.what());
    }
}

void MainWindow::read_frame()
{
    start_name = clock();

    auto src_img = my_video.read_frame(); // 结果图转换为QImage

    if (ui->yolov5_6->isChecked())
    {
        float confThreshold = ui->conf->value(); //TODO 需要明确含义
        float iouThreshold = ui->iou->value();
        std::vector<std::string> class_names = yolo_utils::loadNames(ui->class_names_path->text().toStdString());
        auto result = my_yolo.detect(src_img, confThreshold, iouThreshold);
        yolo_utils::visualizeDetection(src_img, result, class_names,
            ui->Box_dispaly->isChecked(), ui->class_dispaly->isChecked());
    }

    end_time = clock();

    if (ui->FPS_dispaly->isChecked())
    {
        auto text = "FPS: " + std::to_string(1 / ((double)(end_time - start_name) / CLOCKS_PER_SEC));
        qDebug() << "Frame time: " << (double)(end_time - start_name) / CLOCKS_PER_SEC;
        cv::putText(src_img, text, cv::Point(3, 25), cv::FONT_ITALIC, 0.8, cv::Scalar(0, 0, 255), 2);
    }

    align_center(cvMat_2_QImage(src_img), ui->display);
}

void MainWindow::on_close_camera_clicked()
{
    capture_timer->stop();
    delete capture_timer;
    my_video.close_video();
    ui->display->clear();
}

void MainWindow::on_test_button_clicked()
{
    // debug_printf("print.\n");
    // qDebug() << "QDEBUG";
    // QMessageBox::warning(this, "警告", "除数不能为0");

    // //线程释放
    // quit();        // 告诉线程的事件循环以return 0(成功)退出
    // wait();        // 阻塞线程,直到线程已经完成执行
    // deletelater(); // 等于delete
}

void MainWindow::on_yolov5_6_choice_clicked()
{
    QString curDir = QDir::currentPath(); // 获取程序的当前路径
    QString filename = QFileDialog::getOpenFileName(this, "select model", curDir,
                                                    "model (*.onnx )");
    if (filename.isEmpty())
    {
        QMessageBox::warning(this, u8"警告", u8"未选中 yolov5 model 。");
        return;
    }
    qDebug()<< QFileInfo(filename).filePath();  // 获取文件路径
    ui->yolov5_6_path->setText(QFileInfo(filename).filePath());
}

void MainWindow::on_class_names_choice_clicked()
{
    QString curDir = QDir::currentPath();
    QString filename = QFileDialog::getOpenFileName(this, "select class names", curDir,
                                                    "");
    if (filename.isEmpty())
    {
        QMessageBox::warning(this, u8"警告", u8"未选中类别名称。");
        return;
    }
    ui->class_names_path->setText(QFileInfo(filename).filePath());
}

void MainWindow::on_save_picture_clicked()
{
    cv::imwrite("./save_picture.png", QImage_2_cvMat(img));
    //QMessageBox::information(this, "提示", "图像保存成功。");
}

void MainWindow::on_video_choice_clicked()
{
    QString curDir = QDir::currentPath();
    QString filename = QFileDialog::getOpenFileName(this, "select class names", curDir,
        "video (*.mp4 *.avi )");
    if (filename.isEmpty())
    {
        QMessageBox::warning(this, u8"警告", u8"未选中视频。");
        return;
    }
    ui->video_path->setText(QFileInfo(filename).filePath());
}

void MainWindow::on_play_video_clicked()
{
    my_video = Video();
    my_video.open_video(ui->video_path->text().toStdString());

    if (ui->yolov5_6->isChecked())
    {
        std::string model_path = ui->yolov5_6_path->text().toStdString();
        bool is_gpu = ui->ort_gpu->isChecked();

        auto size = cv::Size(640, 640);
        if (ui->size_320->isChecked())
        {
            size = cv::Size(320, 320);
        }

        my_yolo = YOLO(model_path, is_gpu, size);
    }

    while (1)
    {
        start_name = clock();
        auto frame = my_video.read_frame();

        if (frame.empty() || ui->end_video->isDown())
        {
            my_video.close_video();
            ui->display->clear();
            break;
        }

        if (ui->yolov5_6->isChecked())
        {
            float confThreshold = ui->conf->value();
            float iouThreshold = ui->iou->value();
            std::vector<std::string> class_names = yolo_utils::loadNames(ui->class_names_path->text().toStdString());

            auto result = my_yolo.detect(frame, confThreshold, iouThreshold);
            yolo_utils::visualizeDetection(frame, result, class_names,
                ui->Box_dispaly->isChecked(), ui->class_dispaly->isChecked());
        }

        end_time=clock();//ms

        if (ui->FPS_dispaly->isChecked())
        {
            auto text = "FPS: " + std::to_string(1 / ((double)(end_time - start_name) / CLOCKS_PER_SEC));
            qDebug() << "Frame time(ms): " << (double)(end_time - start_name) /*/ CLOCKS_PER_SEC*/;
            cv::putText(frame, text, cv::Point(3, 25), cv::FONT_ITALIC, 0.8, cv::Scalar(0, 0, 255), 2);
        }

        align_center(cvMat_2_QImage(frame), ui->display);
        cv::waitKey(1);
    }
}

void MainWindow::on_pause_video_clicked()
{

}

void MainWindow::on_end_video_clicked()
{

}

四、YOLO 类封装

1、yolov5.h

/*
 * @Author: sun_liqiang
 * @Date: 2022-07-01 19:32:05
 * @LastEditors: sun_liqiang
 * @LastEditTime: 2022-07-05 00:54:51
 * @Description: 
 */

#pragma once

#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <utility>

#include "../utils.hpp"

class YOLO
{
public:
    YOLO();
    explicit YOLO(std::nullptr_t){};
    YOLO(const std::string &modelPath,
                 const bool &isGPU,
                 const cv::Size &inputSize);

    std::vector<Detection> detect(cv::Mat& image, const float& confThreshold,
        const float& iouThreshold);

private:
    Ort::Env env{nullptr};
    Ort::SessionOptions sessionOptions{nullptr};
    Ort::Session session{nullptr};

    void preprocessing(cv::Mat &image, float *&blob, std::vector<int64_t> &inputTensorShape);
    std::vector<Detection> postprocessing(const cv::Size &resizedImageShape,
                                          const cv::Size &originalImageShape,
                                          std::vector<Ort::Value> &outputTensors,
                                          const float &confThreshold, const float &iouThreshold);

    static void getBestClassInfo(std::vector<float>::iterator it, const int &numClasses,
                                 float &bestConf, int &bestClassId);

    std::vector<const char *> inputNames;
    std::vector<const char *> outputNames;
    bool isDynamicInputShape{};
    cv::Size2f inputImageShape;
};

2、yolov5.cpp

#include "yolov5.hpp"
#include <QDebug>

YOLO::YOLO()
{
    
}

YOLO::YOLO(const std::string &modelPath,
                           const bool &isGPU = true,
                           const cv::Size &inputSize = cv::Size(640, 640))
{
    env = Ort::Env(OrtLoggingLevel::ORT_LOGGING_LEVEL_WARNING, "ONNX_DETECTION");
    sessionOptions = Ort::SessionOptions();

    std::vector<std::string> availableProviders = Ort::GetAvailableProviders();
    qDebug()<<"availableProviders 111111111111111111111111 ="<<availableProviders.size();
    for(int i = 0;i<availableProviders.size();i++)
    {
        //"TensorrtExecutionProvider"
        //"CUDAExecutionProvider"
        //"CPUExecutionProvider"

        qDebug()<<"availableProviders 111111111111111111111111 ="<<QString::fromStdString(availableProviders.at(i));
    }
    //
    auto cudaAvailable = std::find(availableProviders.begin(), availableProviders.end(), "CUDAExecutionProvider");
    OrtCUDAProviderOptions cudaOption;

    if (isGPU && (cudaAvailable == availableProviders.end()))
    {
        std::cout << "GPU is not supported by your ONNXRuntime build. Fallback to CPU." << std::endl;
        std::cout << "Inference device: CPU" << std::endl;
    }
    else if (isGPU && (cudaAvailable != availableProviders.end()))
    {
        std::cout << "Inference device: GPU" << std::endl;
        sessionOptions.AppendExecutionProvider_CUDA(cudaOption);
    }
    else
    {
        std::cout << "Inference device: CPU" << std::endl;
    }

#ifdef __linux
    session = Ort::Session(env, modelPath.c_str(), sessionOptions);
#elif _WIN32
    std::wstring w_modelPath = yolo_utils::charToWstring(modelPath.c_str());
    session = Ort::Session(env, w_modelPath.c_str(), sessionOptions);
#else
    std::cout << "未知的平台类型!" << std::endl;
#endif

    Ort::AllocatorWithDefaultOptions allocator;

    Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
    std::vector<int64_t> inputTensorShape = inputTypeInfo.GetTensorTypeAndShapeInfo().GetShape();
    this->isDynamicInputShape = false;
    // checking if width and height are dynamic
    if (inputTensorShape[2] == -1 && inputTensorShape[3] == -1)
    {
        std::cout << "Dynamic input shape" << std::endl;
        this->isDynamicInputShape = true;
    }

    for (auto shape : inputTensorShape)
        std::cout << "Input shape: " << shape << std::endl;

    inputNames.push_back(session.GetInputName(0, allocator));
    outputNames.push_back(session.GetOutputName(0, allocator));

    std::cout << "Input name: " << inputNames[0] << std::endl;
    std::cout << "Output name: " << outputNames[0] << std::endl;

    this->inputImageShape = cv::Size2f(inputSize);
}

void YOLO::getBestClassInfo(std::vector<float>::iterator it, const int &numClasses,
                                    float &bestConf, int &bestClassId)
{
    // first 5 element are box and obj confidence
    bestClassId = 5;
    bestConf = 0;

    for (int i = 5; i < numClasses + 5; i++)
    {
        if (it[i] > bestConf)
        {
            bestConf = it[i];
            bestClassId = i - 5;
        }
    }
}

void YOLO::preprocessing(cv::Mat &image, float *&blob, std::vector<int64_t> &inputTensorShape)
{
    cv::Mat resizedImage, floatImage;
    cv::cvtColor(image, resizedImage, cv::COLOR_BGR2RGB);
    yolo_utils::letterbox(resizedImage, resizedImage, this->inputImageShape,
                     cv::Scalar(114, 114, 114), this->isDynamicInputShape,
                     false, true, 32);

    inputTensorShape[2] = resizedImage.rows;
    inputTensorShape[3] = resizedImage.cols;

    resizedImage.convertTo(floatImage, CV_32FC3, 1 / 255.0);
    blob = new float[floatImage.cols * floatImage.rows * floatImage.channels()];
    cv::Size floatImageSize{floatImage.cols, floatImage.rows};

    // hwc -> chw
    std::vector<cv::Mat> chw(floatImage.channels());
    for (int i = 0; i < floatImage.channels(); ++i)
    {
        chw[i] = cv::Mat(floatImageSize, CV_32FC1, blob + i * floatImageSize.width * floatImageSize.height);
    }
    cv::split(floatImage, chw);
}

std::vector<Detection> YOLO::postprocessing(const cv::Size &resizedImageShape,
                                                    const cv::Size &originalImageShape,
                                                    std::vector<Ort::Value> &outputTensors,
                                                    const float &confThreshold, const float &iouThreshold)
{
    std::vector<cv::Rect> boxes;
    std::vector<float> confs;
    std::vector<int> classIds;

    auto *rawOutput = outputTensors[0].GetTensorData<float>();
    std::vector<int64_t> outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
    size_t count = outputTensors[0].GetTensorTypeAndShapeInfo().GetElementCount();
    std::vector<float> output(rawOutput, rawOutput + count);

    // for (const int64_t& shape : outputShape)
    //     std::cout << "Output Shape: " << shape << std::endl;

    // first 5 elements are box[4] and obj confidence
    int numClasses = (int)outputShape[2] - 5;
    int elementsInBatch = (int)(outputShape[1] * outputShape[2]);

    // only for batch size = 1
    for (auto it = output.begin(); it != output.begin() + elementsInBatch; it += outputShape[2])
    {
        float clsConf = it[4];

        if (clsConf > confThreshold)
        {
            int centerX = (int)(it[0]);
            int centerY = (int)(it[1]);
            int width = (int)(it[2]);
            int height = (int)(it[3]);
            int left = centerX - width / 2;
            int top = centerY - height / 2;

            float objConf;
            int classId;
            this->getBestClassInfo(it, numClasses, objConf, classId);

            float confidence = clsConf * objConf;

            boxes.emplace_back(left, top, width, height);
            confs.emplace_back(confidence);
            classIds.emplace_back(classId);
        }
    }

    std::vector<int> indices;
    cv::dnn::NMSBoxes(boxes, confs, confThreshold, iouThreshold, indices);
    // std::cout << "amount of NMS indices: " << indices.size() << std::endl;

    std::vector<Detection> detections;

    for (int idx : indices)
    {
        Detection det;
        det.box = cv::Rect(boxes[idx]);
        yolo_utils::scaleCoords(resizedImageShape, det.box, originalImageShape);

        det.conf = confs[idx];
        det.classId = classIds[idx];
        detections.emplace_back(det);
    }

    return detections;
}

std::vector<Detection> YOLO::detect(cv::Mat& image, const float& confThreshold,
    const float& iouThreshold)
{
    float *blob = nullptr;
    std::vector<int64_t> inputTensorShape{1, 3, -1, -1};
    this->preprocessing(image, blob, inputTensorShape);

    size_t inputTensorSize = yolo_utils::vectorProduct(inputTensorShape);

    std::vector<float> inputTensorValues(blob, blob + inputTensorSize);

    std::vector<Ort::Value> inputTensors;

    Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(
        OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);

    inputTensors.push_back(Ort::Value::CreateTensor<float>(
        memoryInfo, inputTensorValues.data(), inputTensorSize,
        inputTensorShape.data(), inputTensorShape.size()));

    std::vector<Ort::Value> outputTensors = this->session.Run(Ort::RunOptions{nullptr},
                                                              inputNames.data(),
                                                              inputTensors.data(),
                                                              1,
                                                              outputNames.data(),
                                                              1);

    cv::Size resizedShape = cv::Size((int)inputTensorShape[3], (int)inputTensorShape[2]);
    std::vector<Detection> result = this->postprocessing(resizedShape,
                                                         image.size(),
                                                         outputTensors,
                                                         confThreshold, iouThreshold);

    delete[] blob;
    return result;
}

3、class.names

官方yolov5s.pt模型,能够识别80种物体,classes.names文件记录的就是这80种物体类别名,如下:

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

五、效果

  以本地视频文件1.mp4为例,效果如下:
在这里插入图片描述
  可以看到,在我的机器上,编译32位的程序后,视频卡顿现象非常严重,与opencv-dnn模块的推理部署效果差不多。

  在我尝试编译64位的程序后,效果得到明显改善,FPS约为上面的两倍,效果如下:

在这里插入图片描述

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

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

相关文章

ps2023最新版免费滤镜插件Exposure安装下载教程

滤镜插件是ps的重要功能之一&#xff0c;它主要是用来制作不同的图片特效。那么&#xff0c;ps滤镜插件哪些好用&#xff0c;ps滤镜插件如何获取&#xff0c;下面我们一起来学习这些内容。 ps滤镜插件是比较多的&#xff0c;下面对几款常见的ps滤镜插件进行讲解&#xff0c;看…

TIA博途中的TRACE功能具体使用方法示例

TIA博途中的TRACE功能具体使用方法示例 我们可以利用TRACE曲线来监控程序、排查故障,那么具体怎样使用呢,可以参考以下内容。 如下图所示,打开TIA博途,新建项目后,在左侧项目树中可以看到TRACES, 如下图所示,双击添加新轨迹,然后在右侧窗口中,添加需要监视的信号,…

TOUGH2系列建模方法及在CO2地质封存、水文地球化学、地热、地下水污染等领域中的技术

TOUGH2系列软件是由美国劳伦斯伯克利实验室开发的&#xff0c;旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同&#xff0c;TOUGH2系列软件采用模块化设计和有限积分差网格剖分方法&#xff0c;通过配合不同EOS模块&#xff0c;软件…

【矩阵论】3. 矩阵函数——矩阵函数求导

3.6 矩阵函数求导 3.6.1 积分与求导定义 设 mnm\times nmn 阶矩阵 A(x)(aij(x))mnA(x)\left(a_{ij}(x)\right)_{m\times n}A(x)(aij​(x))mn​ 中的元素都是 x 的可导函数&#xff0c;则 A(x)A(x)A(x) 为关于 xxx 的求导为&#xff1a; A′(A)dA(x)dx(daij(x)dx)mnA(A)\frac{…

正点原子stm32F407学习笔记5——串口通信实验

一、串口通信实验1 上位机给开发板发送数据&#xff0c;开发板将收到的数据发回给上位机 串口设置的一般步骤可以总结为如下几个步骤&#xff1a; 串口时钟使能&#xff0c;GPIO 时钟使能。设置引脚复用器映射&#xff1a;调用 GPIO_PinAFConfig 函数。GPIO 初始化设置&#…

数据库性能翻3倍:Redis on Flash分层存储技术是如何做到的?

Redis on flash简介&#xff1a;Redis on Flash 涉及到的是Redis的分层存储技术&#xff0c;即将数据存放在不同地方。Redis自2016年以来支持Redis on Flash。从2019年开始, Redis企业版&#xff08;Redis Enterprise&#xff09;宣布支持英特尔Optane DC持久性内存&#xff0c…

基于NB-IoT的智能垃圾桶系统设计与实现

本设计是基于物联网的智能垃圾桶&#xff0c;主要实现以下功能&#xff1a; 1&#xff0c;压力传感器模块采集垃圾重量数据&#xff1b; 2&#xff0c;GPS定位模块采集垃圾桶所在的经纬度数据&#xff1b; 3&#xff0c;人体红外模块检测人体并返回是否有人通过的数据&#xf…

会议管理系统SSM记录(二)

目录&#xff1a; &#xff08;1&#xff09;整合Freemarker &#xff08;2&#xff09;用户登录 &#xff08;3&#xff09;提取头部 &#xff08;4&#xff09;提取菜单抽取 &#xff08;1&#xff09;整合Freemarker 在pom.xml中加入Freemark依赖&#xff1a; 创建free…

HTML+CSS大作业:基于HMTL校园学校网页设计题材【我的学校网站】

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

专精特新企业三个层级

专精特新企业也是分层级的。工信部2022年6月印发《优质中小企业梯度培育管理暂行办法》里面明确提出中小企业培育的3个梯度&#xff0c;分别是创新型中小企业、专精特新中小企业和专精特新小巨人企业&#xff0c;刚好构成中小企业发展层级金字塔。这就意味着企业想要发展崛起&a…

19 04-读取DTC快照信息

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;19服务作为UDS中子功能最多的服务&#xff0c;一共有28种子功能&#xff0c;本文将介绍常用的19 04服务&#xff1a;读取快照信息。 关联文章&#xff1a; 19服务List 19 01-通过状态掩码读取DTC数目 $19服务:DTC…

1.2 C++编译器对指针的解释方式(深度理解c++指针)

1.2 指针 1.2.1 指针解释方式 从内存的角度&#xff0c;一个指向类对象的指针与一个指向整数类型的指针或一个指向数组的指针&#xff0c;三者之间是没有任何区别的&#xff0c;它们内部都只存储了一个机器地址值(word)。不同类型指针的区别仅在于其寻址出来的object类型的不…

div+css布局实现个人网页设计(HTML期末作业)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

Spring源码深度解析:七、bean的加载① - doGetBean概述

我们先通过getBean()流程图&#xff0c;来了解Spring的getBean()方法的工作流程&#xff0c;接着根据这个工作流程一步一步的阅读源码 一、前言 文章目录&#xff1a;Spring源码分析&#xff1a;文章目录 getBean()方法是spring ioc的核心&#xff0c;阅读getBean()方法的源…

【Spring Boot+Vue.js+JPA+Mysql】实现前后端分离的名片系统(附源码 超详细必看 可作为大作业使用)

需要项目源码请点赞关注收藏后评论区留言并且私信~~~ 一、项目简介 前后端分离的核心思想时前端页面通过掉用后端的RESTfulApI进行数据交互。本次项目使用Spring BootSpring Data JPA实现后端系统&#xff0c;使用Vue.js实现前端系统&#xff0c;数据库采用mysql&#xff0c;集…

磨金石教育摄影技能干货分享||如何将平凡的窗户拍出美感

窗户有着天然的构图优势&#xff0c;一直是摄影爱好者们喜欢拍的场景。都说眼睛是心灵的窗户&#xff0c;窗户其实就是房间窥探世界的眼睛。 特别是在中国文化中&#xff0c;对窗户的艺术雕刻&#xff0c;总是那么侧重。一间房子好不好看&#xff0c;窗户的设计往往是较为重要…

Java里的异常机制

一、什么是异常 软件程序在运行过程中&#xff0c;遇到用户输入不符合要求、文件路径不存在、文件格式错误、非法参数等的异常问题&#xff0c;叫做异常&#xff08;Exception&#xff09;。 二、简单分类 1.检查性异常 最具代表的检查性异常就是用户错误或问题引起的异常&…

计算机网络:网络层

网络层 网络层主要是解决寻址连接问题&#xff0c;例如两个主机在网络上通过IP进行连接通信 1.网络层概述 网络层的主要任务是实现网络互联&#xff0c;进而实现数据包在各网络之间的传输 需要解决的主要问题&#xff1a; 因特网 使用TCP/IP协议栈通过学习TCP/IP协议栈的网…

Nginx:handler 模块的实现

文章目录1、模块的分类2、模块的基本结构2.1、模块配置结构2.2、模块配置命令2.3、模块上下文结构2.4、模块的定义3、http 请求处理3.1、请求处理阶段3.2、获取用户请求3.3、发送响应4、例&#xff1a;流量限制模块4.1、操作共享内存4.1.1、红黑树4.1.2、双向链表4.2、编写模块…

APS排程软件在压铸行业的应用

压铸是一种金属铸造工艺&#xff0c;其特点是利用模具内腔对融化的金属施加高压。模具通常是用强度更高的合金加工而成的&#xff0c;这个过程有些类似注塑成型。大多数压铸铸件都是不含铁的&#xff0c;例如锌、铜、铝、镁、铅、锡以及铅锡合金以及它们的合金。根据压铸类型的…