基于树莓派Qt+opencv+yolov5-Lite+C++部署深度学习推理

这篇具有很好参考价值的文章主要介绍了基于树莓派Qt+opencv+yolov5-Lite+C++部署深度学习推理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:

        本文是基于qt和opencv的dnn深度学习推理模块,在树莓派上部署YOLO系列推理,适用于yolov5-6.1以及yolov5-Lite,相比直接用python的onnxruntime,用基于opencv的dnn模块,利用训练生成的onnx模型,即可快速部署,不需要在树莓派上额外安装深度学习的一系列环境,因为我们知道部署环境是比较玄学的,也是比较麻烦的。

        刚开始使用的是YOLOv5-6.1版本,部署在电脑虚拟机上能达到7fps,但是如果在树莓派上运行,速度极慢,基本上fps在0.3左右,即推理一次所需的时间在3s以上,实际上根本达不到实时的要求。因此,作者换了更轻量化的网络yoloV5-Lite,部署完成后fps大概在1.54左。之前看到过有作者直接用onnxruntime推理fps能达到5左右,可能是因为qt占树莓派运行内存较大,故很慢。

        因此本文首先是使用海康工业相机采集到需要的数据集,然后在PC端利用YOLOV5-Lite网络进行训练,训练完成之后,将生成的.pt(pytorch格式)权重文件转换为.onnx格式,方便后续利用opencv的dnn模块直接推理。在得到了onnx权重文件后,在树莓派上部署推理所需的环境,即安装qt和opencv。配置好环境后,在qt开发一个简单的界面,根据自己的需求,将所有模块融合进去,最后就可以推理成功啦。话不多说,作者的简单界面是这样的。

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

下面就开始逐步跟大家讲一下实现过程。

一、数据集准备

        由于作者做的是工业相关的一个小项目,基于甲方提供的设备,数据集采集是使用海康威视的工业相机,后续实时推理也是连接了海康工业相机,具体如何在Linux系统下,使用qt成功控制海康工业相机,链接里面有详细说明。有些同学可能使用的是公开的数据集,那这节可以略过。至于给数据集打标签以及配置深度学习环境等一系列问题,作者参考的是良心up炮哥,链接放下面目标检测---教你利用yolov5训练自己的目标检测模型,此外他还有很多博客,可以从0环境到实现搭建深度学习YOLOv5所需的所有环境,因为YOLOV5-Lite和YOLOV5所需的环境是一样的,所以可以直接拿来用。

二、训练模型

        训练模型过程中,都是常用的参数更改,根据自己的数据集路径,更改yaml文件等操作,这里不再赘述,上节链接中作者的博客中都有,触类旁通,yolov5-Lite也是一样的。训练完成后可以发现在train文件夹下exp...某个文件夹中生成了best.pt和last.pt,使用best.pt就可以转换为onnx格式啦。

三、.pt转onnx

        这一步是至关重要的,直接关乎到是否能推理成功。至于如何转onnx,yolov5作者都有写,运行export.py就可以转换成功。

        作者的代码适用于推理YOLOv5-6.1或者onnx输出后整合为单一通道的格式,如下图,其中output就是将三个尺度,也就是80,40,20整合为一个输出,方便后续推理的时候读取。

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

 当然,也可以使用下图中的格式,只是需要在推理的时候改一下代码,后续会讲到

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

 四、树莓派部署qt和opencv环境

(1)安装qt5

        对于树莓派安装qt,可以参考以下博客,还是比较简单的,具体可以搜一下教程,基本都可以成功,但是要注意,qt在5.14版本以后变成了online安装,也就是线上安装,因此树莓派必须成功联网,不然安装不上,作者安装的qt版本是5.14.2。

(2)安装opencv

        安装opencv可以说是很玄学的,你是天选之子,那一次就可以成功,但作者并非是,在树莓派配置opencv环境过程中,足足花费了一下午时间,一直在处理报错的路上,作者也整理了一下如何在Linux下配置opencv,下面就细细讲解一下。

        1)下载opencv4.7.0压缩包

        https://opencv.org/releases.html,找到对应的版本安装,但注意,opencv4.5以上版本才支持dnn推理,也就是深度学习模型的读取,不能安装低于4.5的版本。

        2)安装前准备

        在下载好了压缩包后,右键提取到当前目录下,然后进入opencv4.7.0

目录中,并在opencv-4.7.0下创建键build和install两个文件夹,后续编译会用到。

        下面就是安装CmakeCmake-gui以及所依赖的库(若出错可以思考一下是否执行了sudo apt updatesudo apt upgrade),执行如下代码

sudo apt-get install cmake-qt-gui
sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
        3)开始安装 

            进入build文件夹下编译cmake ,执行命令

cmake-gui

打开了cmake-gui图形化界面,需要更改的地方如下图所示

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

首先,1是我们解压好的opencv安装包的路径,2处选择build文件夹的路径 ,记得一定要勾选3处Advanced。然后就可以点击configure开始配置了。在configure done之后,我们不需要改路径,直接保持默认路径/usr/local,不然会出现各种报错。如下图

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

 然后继续configure和generate之后,没有红色提示说明配置成功。如果出现了红色问题,那就搜一搜报错,应该都能搜到解决方法。

        下面就是进入到build文件夹目录下,开始执行命令

sudo make -j4
sudo make install

        开始了漫长的编译之路,希望每位读者都能一次到100%,如果没到,那就开始和作者一样的痛苦改错日记吧。

        4)安装后配置环境变量

        在完成了编译之后,需要配置opencv的环境变量,进入目录

cd /etc/ld.so.conf.d/
sudo vim opencv.conf

创建文件后插入

/usr/local

保存并退出,执行:

sudo ldconfig

下面就是配置bash,

sudo vim /etc/bash.bashrc

在末尾添加:

PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/pkgconfig

export PKG_CONFIG_PATH

保存退出:执行

source /etc/bash.bashrc

最后就是安装mlocate

sudo apt-get update
sudo apt-get install mlocate

执行更新资料库:

sudo updatedb

最后就可以找一个opencv的qt程序测试一下,能不能打开一张图片之类的。opencv的部署就至此结束啦

五、开发qt界面实现推理

   这部分没有什么需要细讲的,直接上代码:

 (1)main.cpp
#include "widget.h"
#include "mainwidget.h"
#include "yolov5.h"
#include <QApplication>
#include <math.h>
using namespace std;
using namespace cv;
using namespace dnn;



int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
    QApplication a(argc, argv);
    MainWidget mw;
    mw.show();
    return a.exec();
}

此处是将mainWeight为主窗体运行,其他没有任何额外的功能

(2)头文件

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

头文件有如上五个,其中mainwidget是主窗体的头文件,MVCamera.h是海康相机相关的,mythread.h是创建一个独立的线程,以此来控制相机传过来的图像间隔,如果单纯检测视频的话,可以不用这个。widget.h也是海康工业相机的相关头文件,yolov5.h是有关推理的,着重讲一下这个,其他的我相信大家都能看懂,我直接附代码

   1)mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#define MAX_MAINDEVICE_NUM 255
#include "ui_widget.h"
#include "ui_mainwidget.h"
#include "widget.h"
#include <QFileDialog>
#include <QFile>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <QMainWindow>
#include <QTimer>
#include <QImage>
#include <QPixmap>
#include <QDateTime>
#include <QMutex>
#include <QMutexLocker>
#include <QMimeDatabase>
#include <iostream>
#include "MvCamera.h"
#include <chrono>
#include<math.h>
#include <opencv2/highgui/highgui.hpp>
#include "mythread.h"
#include "yolov5.h"
#include <QLineEdit>
#include <QTextCursor>

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

QPixmap MatImage(cv::Mat src);

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWidget;
}

class Widget;              //前向声明,在qt中要用另一个类时
class CMvCamera;
class YoloV5;
class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = nullptr);
    //存储相机配置界面的指针
    Widget *wid=NULL;

    CMvCamera *m_pcMyMainCamera[MAX_MAINDEVICE_NUM]; // 相机指针对象

    cv::Mat *myImage_Main = new cv::Mat(); //保存相机图像的图像指针对象

    MyThread *myThread_Camera_Mainshow = NULL;      //相机画面实时显示线程对象

    std::vector<std::string> className1 = { /*"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" */"OK","NG"};

    Ui::MainWidget *ui;
    ~MainWidget();
public:
    // 状态
    bool m_bOpenDevice;                  // 是否打开设备
    bool m_bStartGrabbing;               // 是否开始抓图
    int m_nTriggerMode;                  // 触发模式
    int m_bContinueStarted;
    int num;
    // 开启过连续采集图像
    MV_SAVE_IAMGE_TYPE m_nSaveImageType; // 保存图像格式

    YoloV5 yolov5;

    Net net;
private slots:
    void on_pbn_start_grab_clicked();

    void closeW();               //关闭子窗体的槽函数

    void readFrame();

    void on_openfile_clicked();

    void on_pbn_load_model_clicked();

    void on_startdetect_clicked();

    void on_stopdetect_clicked();

    void on_cbx_modelName_activated(const QString &arg1);

    void display_myImage_Main(const Mat *imagePrt);

    void getPtr();

    void yoloDetect( Mat *imagePrt);
    //void showCamera();
    void on_checkBox_stateChanged(int arg1);

private:

    QTimer *timer;
    cv::VideoCapture *capture;


    std::vector<cv::Rect> bboxes;
    int IsDetect_ok =0;

};

#endif // MAINWIDGET_H
2)MvCamera.h
#ifndef MVCAMERA_H
#define MVCAMERA_H

#include "MvCameraControl.h"
#include <string.h>
#include <QDebug>
#include <stdio.h>

#ifndef MV_NULL
#define MV_NULL 0
#endif

#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/types_c.h"

class CMvCamera
{
public:
    CMvCamera();
    ~CMvCamera();

    // ch:获取SDK版本号 | en:Get SDK Version
    static int GetSDKVersion();

    // ch:枚举设备 | en:Enumerate Device
    static int EnumDevices(unsigned int nTLayerType,
                           MV_CC_DEVICE_INFO_LIST *pstDevList);

    // ch:判断设备是否可达 | en:Is the device accessible
    static bool IsDeviceAccessible(MV_CC_DEVICE_INFO *pstDevInfo,
                                   unsigned int nAccessMode);

    // ch:打开设备 | en:Open Device
    int Open(MV_CC_DEVICE_INFO *pstDeviceInfo);

    // ch:关闭设备 | en:Close Device
    int Close();

    // ch:判断相机是否处于连接状态 | en:Is The Device Connected
    bool IsDeviceConnected();

    // ch:注册图像数据回调 | en:Register Image Data CallBack
    int RegisterImageCallBack(
        void(__stdcall *cbOutput)(unsigned char *pData,
                                  MV_FRAME_OUT_INFO_EX *pFrameInfo,
                                  void *pUser),
        void *pUser);

    // ch:开启抓图 | en:Start Grabbing
    int StartGrabbing();

    // ch:停止抓图 | en:Stop Grabbing
    int StopGrabbing();

    // ch:主动获取一帧图像数据 | en:Get one frame initiatively
    int GetImageBuffer(MV_FRAME_OUT *pFrame, int nMsec);

    // ch:释放图像缓存 | en:Free image buffer
    int FreeImageBuffer(MV_FRAME_OUT *pFrame);

    // ch:主动获取一帧图像数据 | en:Get one frame initiatively
    int GetOneFrameTimeout(unsigned char *pData, unsigned int *pnDataLen,
                           unsigned int nDataSize,
                           MV_FRAME_OUT_INFO_EX *pFrameInfo, int nMsec);

    // ch:显示一帧图像 | en:Display one frame image
    int DisplayOneFrame(MV_DISPLAY_FRAME_INFO *pDisplayInfo);

    // ch:设置SDK内部图像缓存节点个数 | en:Set the number of the internal image
    // cache nodes in SDK
    int SetImageNodeNum(unsigned int nNum);

    // ch:获取设备信息 | en:Get device information
    int GetDeviceInfo(MV_CC_DEVICE_INFO *pstDevInfo);

    // ch:获取GEV相机的统计信息 | en:Get detect info of GEV camera
    int GetGevAllMatchInfo(MV_MATCH_INFO_NET_DETECT *pMatchInfoNetDetect);

    // ch:获取U3V相机的统计信息 | en:Get detect info of U3V camera
    int GetU3VAllMatchInfo(MV_MATCH_INFO_USB_DETECT *pMatchInfoUSBDetect);

    // ch:获取和设置Int型参数,如 Width和Height,详细内容参考SDK安装目录下的
    // MvCameraNode.xlsx 文件 en:Get Int type parameters, such as Width and
    // Height, for details please refer to MvCameraNode.xlsx file under SDK
    // installation directory
    // int GetIntValue(IN const char* strKey, OUT MVCC_INTVALUE_EX* pIntValue);
    int GetIntValue(IN const char *strKey, OUT unsigned int *pnValue);
    int SetIntValue(IN const char *strKey, IN int64_t nValue);

    // ch:获取和设置Enum型参数,如 PixelFormat,详细内容参考SDK安装目录下的
    // MvCameraNode.xlsx 文件 en:Get Enum type parameters, such as PixelFormat,
    // for details please refer to MvCameraNode.xlsx file under SDK installation
    // directory
    int GetEnumValue(IN const char *strKey, OUT MVCC_ENUMVALUE *pEnumValue);
    int SetEnumValue(IN const char *strKey, IN unsigned int nValue);
    int SetEnumValueByString(IN const char *strKey, IN const char *sValue);

    // ch:获取和设置Float型参数,如
    // ExposureTime和Gain,详细内容参考SDK安装目录下的 MvCameraNode.xlsx 文件
    // en:Get Float type parameters, such as ExposureTime and Gain, for details
    // please refer to MvCameraNode.xlsx file under SDK installation directory
    int GetFloatValue(IN const char *strKey, OUT MVCC_FLOATVALUE *pFloatValue);
    int SetFloatValue(IN const char *strKey, IN float fValue);

    // ch:获取和设置Bool型参数,如 ReverseX,详细内容参考SDK安装目录下的
    // MvCameraNode.xlsx 文件 en:Get Bool type parameters, such as ReverseX, for
    // details please refer to MvCameraNode.xlsx file under SDK installation
    // directory
    int GetBoolValue(IN const char *strKey, OUT bool *pbValue);
    int SetBoolValue(IN const char *strKey, IN bool bValue);

    // ch:获取和设置String型参数,如 DeviceUserID,详细内容参考SDK安装目录下的
    // MvCameraNode.xlsx 文件UserSetSave en:Get String type parameters, such as
    // DeviceUserID, for details please refer to MvCameraNode.xlsx file under
    // SDK installation directory
    int GetStringValue(IN const char *strKey, MVCC_STRINGVALUE *pStringValue);
    int SetStringValue(IN const char *strKey, IN const char *strValue);

    // ch:执行一次Command型命令,如 UserSetSave,详细内容参考SDK安装目录下的
    // MvCameraNode.xlsx 文件 en:Execute Command once, such as UserSetSave, for
    // details please refer to MvCameraNode.xlsx file under SDK installation
    // directory
    int CommandExecute(IN const char *strKey);

    // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal
    // package size(It only works for the GigE camera)
    int GetOptimalPacketSize(unsigned int *pOptimalPacketSize);

    // ch:注册消息异常回调 | en:Register Message Exception CallBack
    int RegisterExceptionCallBack(
        void(__stdcall *cbException)(unsigned int nMsgType, void *pUser),
        void *pUser);

    // ch:注册单个事件回调 | en:Register Event CallBack
    int RegisterEventCallBack(
        const char *pEventName,
        void(__stdcall *cbEvent)(MV_EVENT_OUT_INFO *pEventInfo, void *pUser),
        void *pUser);

    // ch:强制IP | en:Force IP
    int ForceIp(unsigned int nIP, unsigned int nSubNetMask,
                unsigned int nDefaultGateWay);

    // ch:配置IP方式 | en:IP configuration method
    int SetIpConfig(unsigned int nType);

    // ch:设置网络传输模式 | en:Set Net Transfer Mode
    int SetNetTransMode(unsigned int nType);

    // ch:像素格式转换 | en:Pixel format conversion
    int ConvertPixelType(MV_CC_PIXEL_CONVERT_PARAM *pstCvtParam);

    // ch:保存图片 | en:save image
    int SaveImage(MV_SAVE_IMAGE_PARAM_EX *pstParam);

    // ch:保存图片为文件 | en:Save the image as a file
    //int SaveImageToFile(MV_SAVE_IMG_TO_FILE_PARAM *pstParam);

    //设置是否为触发模式
    int setTriggerMode(unsigned int TriggerModeNum);

    //设置触发源
    int setTriggerSource(unsigned int TriggerSourceNum);

    //软触发
    int softTrigger();

    //读取buffer
    int ReadBuffer(cv::Mat &image);

    //读取buffer
    int ReadBuffer2(cv::Mat &image,bool saveFlag,QByteArray imageName);

    //设置曝光时间
    int setExposureTime(float ExposureTimeNum);

public:
    void *m_hDevHandle;
    unsigned int m_nTLayerType;

public:
    unsigned char *m_pBufForSaveImage; // 用于保存图像的缓存
    unsigned int m_nBufSizeForSaveImage;

    unsigned char *m_pBufForDriver; // 用于从驱动获取图像的缓存
    unsigned int m_nBufSizeForDriver;
};

#endif // MVCAMERA_H
3)mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include "QThread"
#include "MvCamera.h"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <QTimer>
using namespace std;
using namespace cv;

class MyThread :public QThread
{
        Q_OBJECT

public:
        MyThread();
        ~MyThread();

        void run();
        void getCameraPtr(CMvCamera* camera);
        void getImagePtr(Mat* image);
        void getCameraIndex(int index);

signals:
        void mess();
        void Display(const Mat* image);

        //发送获取图像处理的信号
        void GetPtr(Mat* image2);

private:
        CMvCamera* cameraPtr = NULL;
        cv::Mat* imagePtr = NULL;
        //cv::Mat* imagePtr2 = NULL;
        int cameraIndex ;
        int TriggerMode;

        QTimer *time;

};

#endif // MYTHREAD_H
4)widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QMessageBox>
#include <QCloseEvent>
#include <QSettings>
#include <QDate>
#include <QDir>

#include "MvCamera.h"
#include "mythread.h"
#include "mainwidget.h"

#define MAX_DEVICE_NUM     2
#define TRIGGER_SOURCE     7
#define EXPOSURE_TIME      40000
#define FRAME              30
#define TRIGGER_ON         1
#define TRIGGER_OFF        0
#define START_GRABBING_ON  1
#define START_GRABBING_OFF 0
#define IMAGE_NAME_LEN     64


QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class MainWidget;
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
signals:
    void closedWid();
    void back();

public:
    CMvCamera *m_pcMyCamera[MAX_DEVICE_NUM]; // 相机指针对象
    MV_CC_DEVICE_INFO_LIST m_stDevList;      // 存储设备列表
    cv::Mat *myImage_L = new cv::Mat(); //保存左相机图像的图像指针对象
    cv::Mat *myImage_R = new cv::Mat(); //保存右相机有图像的图像指针对象
    int devices_num;                    // 设备数量

    MainWidget *mainWid = NULL;           //创建一个存储主界面窗体的指针
public:
    MyThread *myThread_Camera_show = NULL; //相机实时显示线程对象

private slots:
    void on_pbn_enum_camera_clicked();
    void on_pbn_open_camera_clicked();
    void on_rdo_continue_mode_clicked();
    void on_rdo_softigger_mode_clicked();
    void on_pbn_start_grabbing_clicked();
    void on_pbn_stop_grabbing_clicked();
    void on_pbn_software_once_clicked();
    void display_myImage_L(const Mat *imagePrt);
    void display_myImage_Main(const Mat *imagePrt);
    void on_pbn_close_camera_clicked();
    void on_pbn_save_BMP_clicked();
    void on_pbn_save_JPG_clicked();
    void on_le_set_exposure_textChanged(const QString &arg1);
    void on_le_set_gain_textChanged(const QString &arg1);



    void on_pbn_return_main_clicked();

public:
    // 状态
    bool m_bOpenDevice;                  // 是否打开设备
    bool m_bStartGrabbing;               // 是否开始抓图
    int m_nTriggerMode;                  // 触发模式
    int m_bContinueStarted;              // 开启过连续采集图像
    MV_SAVE_IAMGE_TYPE m_nSaveImageType; // 保存图像格式

private:
    QString PrintDeviceInfo(MV_CC_DEVICE_INFO *pstMVDevInfo, int num_index);
    QString m_SaveImagePath;
    void OpenDevices();
    void CloseDevices();
    void SaveImage();
    void saveImage(QString format,int index);

private:

    Ui::Widget *ui;

protected:
    void closeEvent(QCloseEvent *event) override;     //重写关闭事件处理函数
};
#endif // WIDGET_H

5)yolov5.h
#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
#include <QString>
#define YOLO_P6 false //是否使用P6模型
#include<math.h>

struct Output {
    int id;             //结果类别id
    float confidence;   //结果置信度
    cv::Rect box;       //矩形框
};

class YoloV5 {
public:
    YoloV5() {
    }
    ~YoloV5() {}
    bool readModel(cv::dnn::Net& net, std::string& netPath, bool isCuda);
    bool Detect(cv::Mat& SrcImg, cv::dnn::Net& net, std::vector<Output>& output);
    void drawPred(cv::Mat& img, std::vector<Output> result, std::vector<cv::Scalar> color);
    void getTime(QString msg);
private:
    float Sigmoid(float x) {
            return static_cast<float>(1.f / (1.f + exp(-x)));
        }

    const float netAnchors[3][6] = { { 10.0, 13.0, 16.0, 30.0, 33.0, 23.0 },{ 30.0, 61.0, 62.0, 45.0, 59.0, 119.0 },{ 116.0, 90.0, 156.0, 198.0, 373.0, 326.0 } };
        //stride
        const float netStride[3] = { 8.0, 16.0, 32.0 };
        const int netWidth = 320; //网络模型输入大小
        const int netHeight = 320;
        float nmsThreshold = 0.45;
        float boxThreshold = 0.35;
        float classThreshold = 0.35;
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" */"OK","NG"};
};

这里需要讲一下,对于vector数组className,需要设置自己的类别数,这里需要改一下,因为作者的类别数只有“ok”和“NG”,所有我将原有的数据集coco的80个类别给注释掉了,自己根据需求更改相关参数,都在头文件里。

(3)cpp文件 
1)mainwidget.cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include "widget.h"
#include <QPushButton>

MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWidget)
{
    ui->setupUi(this);
    setWindowTitle(QStringLiteral("4079Lab"));
    ui->lbl_res_pic->setScaledContents(true);
    ui->txt_message->setLineWrapMode(QTextEdit::WidgetWidth);
    timer =new QTimer(this);
    timer->setInterval(30);                     //设置定时器触发的间隔

    num=1;
    //将定时器与读取数据流的函数连接起来,每当定时器触发,执行readFrame
    connect(timer,&QTimer::timeout,this,&MainWidget::readFrame);

    //将定时器与读取相机帧率以及检测的函数连接起来,每当定时器触发,执行yoloDetect

    //ui->startdetect->setEnabled(false);

    this->wid=new Widget();
    //点击相机配置按钮,显示相机配置界面,隐藏主界面
    connect(ui->pbn_start_grab,&QPushButton::clicked,wid,[=]()
    {
        this->hide();
        wid->show();
    });

    //关闭相机配置界面后,显示主界面
    connect(this->wid, &Widget::closedWid, this,[=]()
    {
        this->show();
    });

    //主界面接受信号后,会显示主界面,隐藏相机界面
    connect(this->wid,&Widget::back,this,[=]()
    {
        this->wid->hide();
        this->show();

    });

    this->myThread_Camera_Mainshow=new MyThread;
    connect(wid->myThread_Camera_show, SIGNAL(GetPtr(Mat *)), this,
            SLOT(yoloDetect(Mat *)));

    //设置打开图片和摄像头button为disable,只有当加载模型后才会Enable
    ui->openfile->setEnabled(false);
    ui->pbn_start_grab->setEnabled(false);
}
MainWidget::~MainWidget()
{
    capture->release();
    delete capture;
    delete ui;
    delete wid;


}


//读取流函数
void MainWidget::readFrame()
{
    cv::Mat frame;
    capture->read(frame);
    if (frame.empty())
        return;

    auto start = std::chrono::steady_clock::now();
   //yolov5->detect(frame);
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double, std::milli> elapsed = end - start;
    ui->txt_message->append(QString("cost_time: %1 ms").arg(elapsed.count()));
    cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
    QImage rawImage = QImage((uchar*)(frame.data),frame.cols,frame.rows,frame.step,QImage::Format_RGB888);
    ui->lbl_res_pic->setPixmap(QPixmap::fromImage(rawImage));
}


//根据定时器触发实时检测
void MainWidget::yoloDetect(Mat *myImage_Main)
{

    //读取相机流
    //m_pcMyMainCamera[0]->ReadBuffer(*myImage_Main);
    if(myImage_Main!=NULL)
    {
        cv::Mat temp;
        if(myImage_Main->channels()==4)
            cv::cvtColor(*myImage_Main,temp,cv::COLOR_BGRA2RGB);
        else if (myImage_Main->channels()==3)
            cv::cvtColor(*myImage_Main,temp,cv::COLOR_BGR2RGB);
        else
            cv::cvtColor(*myImage_Main,temp,cv::COLOR_GRAY2RGB);
        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));
            }
        vector<Output> result;


        auto start = std::chrono::steady_clock::now();
        yolov5.Detect(temp,net,result);
        auto end = std::chrono::steady_clock::now();
        std::chrono::duration<double, std::milli> elapsed = end - start;

        ui->time_tet->setOverwriteMode(true);
        ui->time_tet->setText(QString("%1 ms").arg(elapsed.count()));

        ui->txt_message->append(QString("%1  result:%2   const_time: %3 ms").arg(num).arg((className1[result[0].id].data())).arg(elapsed.count()));
        yolov5.drawPred(temp, result, color);
        QImage img = QImage((uchar*)(temp.data),temp.cols,temp.rows,temp.step,QImage::Format_RGB888);
        ui->lbl_res_pic->setPixmap(QPixmap::fromImage(img));
        ui->lbl_res_pic->resize(ui->lbl_res_pic->pixmap()->size());
        if(ui->checkBox->isChecked())
        {
            QString savePath="/home/joe/program/VersionDetect/LiteTest/res_img/num.png";
            img.save(savePath);
        }
        result.pop_back();
        num++;
    }


}

//加载相机界面
void MainWidget::on_pbn_start_grab_clicked()
{
//    QString onnxFile = "/home/joe/program/VersionDetect/test2/yolov5s.onnx";

//    if (!yolov5->loadModel(onnxFile.toLatin1().data())){
//        ui->txt_message->append(QStringLiteral("加载模型失败!"));
//        return;
//    }
//    ui->txt_message->append(QString::fromUtf8("Open onnxFile: %1 succesfully!").arg(onnxFile));
}

//关闭子窗体  显示父窗体
void MainWidget::closeW()
{
    this->show();
}




//打开文件按钮的槽函数
void MainWidget::on_openfile_clicked()
{

    //过滤除了"*.mp4 *.avi;;*.png *.jpg *.jpeg *.bmp"以外的文件
    QString filename = QFileDialog::getOpenFileName(this,QStringLiteral("打开文件"),"/home/joe/onnx",".*.png *.jpg *.jpeg *.bmp;;*.mp4 *.avi");
    if(!QFile::exists(filename)){
        return;
    }

    QMimeDatabase db;
    QMimeType mime = db.mimeTypeForFile(filename);
    if (mime.name().startsWith("image/")) {
        cv::Mat src = cv::imread(filename.toLatin1().data());
        if(src.empty()){
            return;
        }
        cv::Mat temp;
        if(src.channels()==4)
            cv::cvtColor(src,temp,cv::COLOR_BGRA2RGB);
        else if (src.channels()==3)
            cv::cvtColor(src,temp,cv::COLOR_BGR2RGB);
        else
            cv::cvtColor(src,temp,cv::COLOR_GRAY2RGB);
        cv::Mat Rgb;
        QImage Img;
        if (src.channels() == 3)//RGB Img
        {
            cv::cvtColor(temp, Rgb, CV_BGR2RGB);//颜色空间转换
            Img = QImage((const uchar*)(Rgb.data), Rgb.cols, Rgb.rows, Rgb.cols * Rgb.channels(), QImage::Format_RGB888);
        }
        else//Gray Img
        {
            Img = QImage((const uchar*)(temp.data), temp.cols, temp.rows, temp.cols*temp.channels(), QImage::Format_Indexed8);
        }
        ui->lbl_res_pic->setPixmap(QPixmap::fromImage(Img));
        ui->lbl_res_pic->resize(ui->lbl_res_pic->pixmap()->size());

        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));
            }
        vector<Output> result;

        auto start = std::chrono::steady_clock::now();
        yolov5.Detect(temp,net,result);
        auto end = std::chrono::steady_clock::now();
        std::chrono::duration<double, std::milli> elapsed = end - start;
        ui->txt_message->append(QString("%1  result:%2   const_time: %3 ms").arg(num).arg((className1[result[0].id].data())).arg(elapsed.count()));
        //ui->txt_message->append(QString("cost_time: %1 ms").arg(elapsed.count()));

        ui->time_tet->setOverwriteMode(true);
        ui->time_tet->setText(QString("%1 ms").arg(elapsed.count()));

        yolov5.drawPred(temp, result, color);
        QImage img = QImage((uchar*)(temp.data),temp.cols,temp.rows,temp.step,QImage::Format_RGB888);
        ui->lbl_res_pic->setPixmap(QPixmap::fromImage(img));
        ui->lbl_res_pic->resize(ui->lbl_res_pic->pixmap()->size());
        if(ui->checkBox->isChecked())
        {
            QString savePath="/home/joe/program/VersionDetect/LiteTest/res_img/num.png";
            img.save(savePath);
        }
        num++;
        filename.clear();
    }else if (mime.name().startsWith("video/")) {
        capture->open(filename.toLatin1().data());
        if (!capture->isOpened()){
            ui->txt_message->append("fail to open MP4!");
            return;
        }
        IsDetect_ok +=1;
//        if (IsDetect_ok ==2)
//            ui->startdetect->setEnabled(true);
        ui->txt_message->append(QString::fromUtf8("Open video: %1 succesfully!").arg(filename));

        //获取整个帧数QStringLiteral
        long totalFrame = capture->get(cv::CAP_PROP_FRAME_COUNT);
        int width = capture->get(cv::CAP_PROP_FRAME_WIDTH);
        int height = capture->get(cv::CAP_PROP_FRAME_HEIGHT);
        ui->txt_message->append(QStringLiteral("整个视频共 %1 帧, 宽=%2 高=%3 ").arg(totalFrame).arg(width).arg(height));
        ui->lbl_res_pic->resize(QSize(width, height));

        //设置开始帧()
        long frameToStart = 0;
        capture->set(cv::CAP_PROP_POS_FRAMES, frameToStart);
        ui->txt_message->append(QStringLiteral("从第 %1 帧开始读").arg(frameToStart));

        //获取帧率
        double rate = capture->get(cv::CAP_PROP_FPS);
        ui->txt_message->append(QStringLiteral("帧率为: %1 ").arg(rate));
    }
}

void MainWidget::on_pbn_load_model_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(this,QStringLiteral("打开文件"),"/home/joe/onnx","*.onnx");
    if(!QFile::exists(filePath)){
        return;
    }
    string model_path=filePath.toStdString();

    if (yolov5.readModel(net, model_path, true)) {
            ui->txt_message->append("模型加载成功");
            //提取文件名并显示在TextEdit上
            QFileInfo fileInfo(filePath);
            QString fileName=fileInfo.fileName();
            ui->fileName_tet->setText(fileName);
            ui->fileName_tet->show();
            ui->openfile->setEnabled(true);
            ui->pbn_start_grab->setEnabled(true);

        }
    else
    {
        ui->txt_message->append("模型加载失败");
    }

}

//获取图像信息
void MainWidget::getPtr()
{
    // 触发模式标记一下,切换触发模式时先执行停止采集图像函数
    m_bContinueStarted = 1;

    if (m_nTriggerMode == TRIGGER_ON) {
        // 开始采集之后才创建workthread线程

            //开启相机采集
            m_pcMyMainCamera[0]->StartGrabbing();
            int camera_Index=0;
            if (camera_Index == 0) {
                myThread_Camera_Mainshow->getCameraPtr(
                    m_pcMyMainCamera[0]); //线程获取左相机指针
                myThread_Camera_Mainshow->getImagePtr(
                    myImage_Main); //线程获取图像指针
                myThread_Camera_Mainshow->getCameraIndex(0); //相机 Index==0

                if (!myThread_Camera_Mainshow->isRunning()) {
                    myThread_Camera_Mainshow->start();
                    m_pcMyMainCamera[0]->softTrigger();
                    m_pcMyMainCamera[0]->ReadBuffer(*myImage_Main); //读取Mat格式的图像
                }
            }
        }
}
//在主界面显示
void MainWidget::display_myImage_Main(const Mat *imagePrt)
{


    cv::Mat rgb;
    cv::cvtColor(*imagePrt, rgb, CV_BGR2RGB);

    QImage QmyImage_L;
    QmyImage_L = QImage((const unsigned char *)(rgb.data), rgb.cols,
                            rgb.rows, QImage::Format_RGB888);

    QmyImage_L = (QmyImage_L)
                     .scaled(ui->lbl_res_pic->size(), Qt::IgnoreAspectRatio,
                             Qt::SmoothTransformation); //饱满填充
    //显示图像
    //ui->lbl_camera_L->setPixmap(QPixmap::fromImage(QmyImage_L));
    ui->lbl_res_pic->setPixmap(QPixmap::fromImage(QmyImage_L));
}

void MainWidget::on_startdetect_clicked()
{
    timer->start();
//    ui->startdetect->setEnabled(false);
//    ui->stopdetect->setEnabled(true);
    ui->openfile->setEnabled(false);
    ui->pbn_load_model->setEnabled(false);
//    ui->cbx_modelName->setEnabled(false);
    ui->txt_message->append(QStringLiteral("=======================\n"
                                           "        开始检测\n"
                                           "=======================\n"));
}

void MainWidget::on_stopdetect_clicked()
{
//    ui->startdetect->setEnabled(true);
//    ui->stopdetect->setEnabled(false);
    ui->openfile->setEnabled(true);
    ui->pbn_load_model->setEnabled(true);
//    ui->cbx_modelName->setEnabled(true);
    timer->stop();
    ui->txt_message->append(QStringLiteral("======================\n"
                                           "        停止检测\n"
                                           "======================\n"));
}


void MainWidget::on_cbx_modelName_activated(const QString &arg1)
{

}



void MainWidget::on_checkBox_stateChanged(int arg1)
{
    if(ui->checkBox->isChecked())
    {
        ui->txt_message->append("您选择了保存结果,保存路径为/home/joe/program/VersionDetect/LiteTest/res_img");
    }
}
2)MvCamera.cpp
#include "MvCamera.h"
#include <stdio.h>

CMvCamera::CMvCamera()
{
    m_hDevHandle = MV_NULL;
}

CMvCamera::~CMvCamera()
{
    if (m_hDevHandle) {
        MV_CC_DestroyHandle(m_hDevHandle);
        m_hDevHandle = MV_NULL;
    }
}

// ch:获取SDK版本号 | en:Get SDK Version
int CMvCamera::GetSDKVersion()
{
    return MV_CC_GetSDKVersion();
}

// ch:枚举设备 | en:Enumerate Device
int CMvCamera::EnumDevices(unsigned int nTLayerType,
                           MV_CC_DEVICE_INFO_LIST *pstDevList)
{
    return MV_CC_EnumDevices(nTLayerType, pstDevList);
}

// ch:判断设备是否可达 | en:Is the device accessible
bool CMvCamera::IsDeviceAccessible(MV_CC_DEVICE_INFO *pstDevInfo,
                                   unsigned int nAccessMode)
{
    return MV_CC_IsDeviceAccessible(pstDevInfo, nAccessMode);
}

// ch:打开设备 | en:Open Device
int CMvCamera::Open(MV_CC_DEVICE_INFO *pstDeviceInfo)
{
    if (MV_NULL == pstDeviceInfo) {
        return MV_E_PARAMETER;
    }

    if (m_hDevHandle) {
        return MV_E_CALLORDER;
    }

    int nRet = MV_CC_CreateHandle(&m_hDevHandle, pstDeviceInfo);
    if (MV_OK != nRet) {
        return nRet;
    }

    nRet = MV_CC_OpenDevice(m_hDevHandle);
    if (MV_OK != nRet) {
        MV_CC_DestroyHandle(m_hDevHandle);
        m_hDevHandle = MV_NULL;
    }

    return nRet;
}

// ch:关闭设备 | en:Close Device
int CMvCamera::Close()
{
    if (MV_NULL == m_hDevHandle) {
        return MV_E_HANDLE;
    }

    MV_CC_CloseDevice(m_hDevHandle);

    int nRet = MV_CC_DestroyHandle(m_hDevHandle);
    m_hDevHandle = MV_NULL;

    return nRet;
}

// ch:判断相机是否处于连接状态 | en:Is The Device Connected
bool CMvCamera::IsDeviceConnected()
{
    return MV_CC_IsDeviceConnected(m_hDevHandle);
}

// ch:注册图像数据回调 | en:Register Image Data CallBack
int CMvCamera::RegisterImageCallBack(
    void(__stdcall *cbOutput)(unsigned char *pData,
                              MV_FRAME_OUT_INFO_EX *pFrameInfo, void *pUser),
    void *pUser)
{
    return MV_CC_RegisterImageCallBackEx(m_hDevHandle, cbOutput, pUser);
}

// ch:开启抓图 | en:Start Grabbing
int CMvCamera::StartGrabbing()
{
    return MV_CC_StartGrabbing(m_hDevHandle);
}

// ch:停止抓图 | en:Stop Grabbing
int CMvCamera::StopGrabbing()
{
    return MV_CC_StopGrabbing(m_hDevHandle);
}

// ch:主动获取一帧图像数据 | en:Get one frame initiatively
int CMvCamera::GetImageBuffer(MV_FRAME_OUT *pFrame, int nMsec)
{
    return MV_CC_GetImageBuffer(m_hDevHandle, pFrame, nMsec);
}

// ch:释放图像缓存 | en:Free image buffer
int CMvCamera::FreeImageBuffer(MV_FRAME_OUT *pFrame)
{
    return MV_CC_FreeImageBuffer(m_hDevHandle, pFrame);
}

// ch:主动获取一帧图像数据 | en:Get one frame initiatively
int CMvCamera::GetOneFrameTimeout(unsigned char *pData, unsigned int *pnDataLen,
                                  unsigned int nDataSize,
                                  MV_FRAME_OUT_INFO_EX *pFrameInfo, int nMsec)
{
    if (NULL == pnDataLen) {
        return MV_E_PARAMETER;
    }

    int nRet = MV_OK;

    *pnDataLen = 0;

    nRet = MV_CC_GetOneFrameTimeout(m_hDevHandle, pData, nDataSize, pFrameInfo,
                                    nMsec);
    if (MV_OK != nRet) {
        return nRet;
    }

    *pnDataLen = pFrameInfo->nFrameLen;

    return nRet;
}

// ch:设置显示窗口句柄 | en:Set Display Window Handle
int CMvCamera::DisplayOneFrame(MV_DISPLAY_FRAME_INFO *pDisplayInfo)
{
    return MV_CC_DisplayOneFrame(m_hDevHandle, pDisplayInfo);
}

// ch:设置SDK内部图像缓存节点个数 | en:Set the number of the internal image
// cache nodes in SDK
int CMvCamera::SetImageNodeNum(unsigned int nNum)
{
    return MV_CC_SetImageNodeNum(m_hDevHandle, nNum);
}

// ch:获取设备信息 | en:Get device information
int CMvCamera::GetDeviceInfo(MV_CC_DEVICE_INFO *pstDevInfo)
{
    return MV_CC_GetDeviceInfo(m_hDevHandle, pstDevInfo);
}

// ch:获取GEV相机的统计信息 | en:Get detect info of GEV camera
int CMvCamera::GetGevAllMatchInfo(MV_MATCH_INFO_NET_DETECT *pMatchInfoNetDetect)
{
    if (MV_NULL == pMatchInfoNetDetect) {
        return MV_E_PARAMETER;
    }

    MV_CC_DEVICE_INFO stDevInfo = { 0 };
    GetDeviceInfo(&stDevInfo);
    if (stDevInfo.nTLayerType != MV_GIGE_DEVICE) {
        return MV_E_SUPPORT;
    }

    MV_ALL_MATCH_INFO struMatchInfo = { 0 };

    struMatchInfo.nType = MV_MATCH_TYPE_NET_DETECT;
    struMatchInfo.pInfo = pMatchInfoNetDetect;
    struMatchInfo.nInfoSize = sizeof(MV_MATCH_INFO_NET_DETECT);
    memset(struMatchInfo.pInfo, 0, sizeof(MV_MATCH_INFO_NET_DETECT));

    return MV_CC_GetAllMatchInfo(m_hDevHandle, &struMatchInfo);
}

// ch:获取U3V相机的统计信息 | en:Get detect info of U3V camera
int CMvCamera::GetU3VAllMatchInfo(MV_MATCH_INFO_USB_DETECT *pMatchInfoUSBDetect)
{
    if (MV_NULL == pMatchInfoUSBDetect) {
        return MV_E_PARAMETER;
    }

    MV_CC_DEVICE_INFO stDevInfo = { 0 };
    GetDeviceInfo(&stDevInfo);
    if (stDevInfo.nTLayerType != MV_USB_DEVICE) {
        return MV_E_SUPPORT;
    }

    MV_ALL_MATCH_INFO struMatchInfo = { 0 };

    struMatchInfo.nType = MV_MATCH_TYPE_USB_DETECT;
    struMatchInfo.pInfo = pMatchInfoUSBDetect;
    struMatchInfo.nInfoSize = sizeof(MV_MATCH_INFO_USB_DETECT);
    memset(struMatchInfo.pInfo, 0, sizeof(MV_MATCH_INFO_USB_DETECT));

    return MV_CC_GetAllMatchInfo(m_hDevHandle, &struMatchInfo);
}

// ch:获取和设置Int型参数,如 Width和Height,详细内容参考SDK安装目录下的
// MvCameraNode.xlsx 文件 en:Get Int type parameters, such as Width and Height,
// for details please refer to MvCameraNode.xlsx file under SDK installation
// directory
int CMvCamera::GetIntValue(IN const char *strKey, OUT unsigned int *pnValue)
{
    if (NULL == strKey || NULL == pnValue) {
        return MV_E_PARAMETER;
    }

    MVCC_INTVALUE stParam;
    memset(&stParam, 0, sizeof(MVCC_INTVALUE));
    int nRet = MV_CC_GetIntValue(m_hDevHandle, strKey, &stParam);
    if (MV_OK != nRet) {
        return nRet;
    }

    *pnValue = stParam.nCurValue;

    return MV_OK;
}

int CMvCamera::SetIntValue(IN const char *strKey, IN int64_t nValue)
{
    return MV_CC_SetIntValueEx(m_hDevHandle, strKey, nValue);
}

// ch:获取和设置Enum型参数,如 PixelFormat,详细内容参考SDK安装目录下的
// MvCameraNode.xlsx 文件 en:Get Enum type parameters, such as PixelFormat, for
// details please refer to MvCameraNode.xlsx file under SDK installation
// directory
int CMvCamera::GetEnumValue(IN const char *strKey,
                            OUT MVCC_ENUMVALUE *pEnumValue)
{
    return MV_CC_GetEnumValue(m_hDevHandle, strKey, pEnumValue);
}

int CMvCamera::SetEnumValue(IN const char *strKey, IN unsigned int nValue)
{
    return MV_CC_SetEnumValue(m_hDevHandle, strKey, nValue);
}

int CMvCamera::SetEnumValueByString(IN const char *strKey,
                                    IN const char *sValue)
{
    return MV_CC_SetEnumValueByString(m_hDevHandle, strKey, sValue);
}

// ch:获取和设置Float型参数,如 ExposureTime和Gain,详细内容参考SDK安装目录下的
// MvCameraNode.xlsx 文件 en:Get Float type parameters, such as ExposureTime and
// Gain, for details please refer to MvCameraNode.xlsx file under SDK
// installation directory
int CMvCamera::GetFloatValue(IN const char *strKey,
                             OUT MVCC_FLOATVALUE *pFloatValue)
{
    return MV_CC_GetFloatValue(m_hDevHandle, strKey, pFloatValue);
}

int CMvCamera::SetFloatValue(IN const char *strKey, IN float fValue)
{
    return MV_CC_SetFloatValue(m_hDevHandle, strKey, fValue);
}

// ch:获取和设置Bool型参数,如 ReverseX,详细内容参考SDK安装目录下的
// MvCameraNode.xlsx 文件 en:Get Bool type parameters, such as ReverseX, for
// details please refer to MvCameraNode.xlsx file under SDK installation
// directory
int CMvCamera::GetBoolValue(IN const char *strKey, OUT bool *pbValue)
{
    return MV_CC_GetBoolValue(m_hDevHandle, strKey, pbValue);
}

int CMvCamera::SetBoolValue(IN const char *strKey, IN bool bValue)
{
    return MV_CC_SetBoolValue(m_hDevHandle, strKey, bValue);
}

// ch:获取和设置String型参数,如 DeviceUserID,详细内容参考SDK安装目录下的
// MvCameraNode.xlsx 文件UserSetSave en:Get String type parameters, such as
// DeviceUserID, for details please refer to MvCameraNode.xlsx file under SDK
// installation directory
int CMvCamera::GetStringValue(IN const char *strKey,
                              MVCC_STRINGVALUE *pStringValue)
{
    return MV_CC_GetStringValue(m_hDevHandle, strKey, pStringValue);
}

int CMvCamera::SetStringValue(IN const char *strKey, IN const char *strValue)
{
    return MV_CC_SetStringValue(m_hDevHandle, strKey, strValue);
}

// ch:执行一次Command型命令,如 UserSetSave,详细内容参考SDK安装目录下的
// MvCameraNode.xlsx 文件 en:Execute Command once, such as UserSetSave, for
// details please refer to MvCameraNode.xlsx file under SDK installation
// directory
int CMvCamera::CommandExecute(IN const char *strKey)
{
    return MV_CC_SetCommandValue(m_hDevHandle, strKey);
}

// ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal
// package size(It only works for the GigE camera)
int CMvCamera::GetOptimalPacketSize(unsigned int *pOptimalPacketSize)
{
    if (MV_NULL == pOptimalPacketSize) {
        return MV_E_PARAMETER;
    }

    int nRet = MV_CC_GetOptimalPacketSize(m_hDevHandle);
    if (nRet < MV_OK) {
        return nRet;
    }

    *pOptimalPacketSize = (unsigned int)nRet;

    return MV_OK;
}

// ch:注册消息异常回调 | en:Register Message Exception CallBack
int CMvCamera::RegisterExceptionCallBack(
    void(__stdcall *cbException)(unsigned int nMsgType, void *pUser),
    void *pUser)
{
    return MV_CC_RegisterExceptionCallBack(m_hDevHandle, cbException, pUser);
}

// ch:注册单个事件回调 | en:Register Event CallBack
int CMvCamera::RegisterEventCallBack(
    const char *pEventName,
    void(__stdcall *cbEvent)(MV_EVENT_OUT_INFO *pEventInfo, void *pUser),
    void *pUser)
{
    return MV_CC_RegisterEventCallBackEx(m_hDevHandle, pEventName, cbEvent,
                                         pUser);
}

// ch:强制IP | en:Force IP
int CMvCamera::ForceIp(unsigned int nIP, unsigned int nSubNetMask,
                       unsigned int nDefaultGateWay)
{
    return MV_GIGE_ForceIpEx(m_hDevHandle, nIP, nSubNetMask, nDefaultGateWay);
}

// ch:配置IP方式 | en:IP configuration method
int CMvCamera::SetIpConfig(unsigned int nType)
{
    return MV_GIGE_SetIpConfig(m_hDevHandle, nType);
}

// ch:设置网络传输模式 | en:Set Net Transfer Mode
int CMvCamera::SetNetTransMode(unsigned int nType)
{
    return MV_GIGE_SetNetTransMode(m_hDevHandle, nType);
}

// ch:像素格式转换 | en:Pixel format conversion
int CMvCamera::ConvertPixelType(MV_CC_PIXEL_CONVERT_PARAM *pstCvtParam)
{
    return MV_CC_ConvertPixelType(m_hDevHandle, pstCvtParam);
}

// ch:保存图片 | en:save image
int CMvCamera::SaveImage(MV_SAVE_IMAGE_PARAM_EX *pstParam)
{
    return MV_CC_SaveImageEx2(m_hDevHandle, pstParam);
}

 //ch:保存图片为文件 | en:Save the image as a file
// int CMvCamera::SaveImageToFile(MV_SAVE_IMG_TO_FILE_PARAM *pstSaveFileParam)
//{
//    return MV_CC_SaveImageToFile(m_hDevHandle, pstSaveFileParam);
//}

//设置是否为触发模式
int CMvCamera::setTriggerMode(unsigned int TriggerModeNum)
{
    // 0:Off  1:On
    int tempValue =
        MV_CC_SetEnumValue(m_hDevHandle, "TriggerMode", TriggerModeNum);
    if (tempValue != 0) {
        return -1;
    } else {
        return 0;
    }
}

//设置触发源
int CMvCamera::setTriggerSource(unsigned int TriggerSourceNum)
{
    // 0:Line0  1:Line1  7:Software
    int tempValue =
        MV_CC_SetEnumValue(m_hDevHandle, "TriggerSource", TriggerSourceNum);
    if (tempValue != 0) {
        return -1;
    } else {
        return 0;
    }
}

//发送软触发
int CMvCamera::softTrigger()
{
    int tempValue = MV_CC_SetCommandValue(m_hDevHandle, "TriggerSoftware");
    if (tempValue != 0) {
        return -1;
    } else {
        return 0;
    }
}

//读取相机中的图像
// int ReadBuffer(cv::Mat &image);
int CMvCamera::ReadBuffer(cv::Mat &image)
{
    cv::Mat *getImage = new cv::Mat();
    unsigned int nRecvBufSize = 0;
    MVCC_INTVALUE stParam;
    memset(&stParam, 0, sizeof(MVCC_INTVALUE));
    int tempValue = MV_CC_GetIntValue(m_hDevHandle, "PayloadSize", &stParam);
    if (tempValue != 0) {
        return -1;
    }
    nRecvBufSize = stParam.nCurValue;
    unsigned char *pDate;
    pDate = (unsigned char *)malloc(nRecvBufSize);

    MV_FRAME_OUT_INFO_EX stImageInfo = { 0 };
    tempValue = MV_CC_GetOneFrameTimeout(m_hDevHandle, pDate, nRecvBufSize,
                                         &stImageInfo, 500);
    if (tempValue != 0) {
        return -1;
    }
    m_nBufSizeForSaveImage =
        stImageInfo.nWidth * stImageInfo.nHeight * 3 + 2048;
    unsigned char *m_pBufForSaveImage;
    m_pBufForSaveImage = (unsigned char *)malloc(m_nBufSizeForSaveImage);

    bool isMono;
    switch (stImageInfo.enPixelType) {
    case PixelType_Gvsp_Mono8:
    case PixelType_Gvsp_Mono10:
    case PixelType_Gvsp_Mono10_Packed:
    case PixelType_Gvsp_Mono12:
    case PixelType_Gvsp_Mono12_Packed:
        isMono = true;
        break;
    default:
        isMono = false;
        break;
    }
    if (isMono) {
        *getImage =
            cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC1, pDate);
        // imwrite("d:\\测试opencv_Mono.tif", image);
    } else {
        //转换图像格式为BGR8
        MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
        memset(&stConvertParam, 0, sizeof(MV_CC_PIXEL_CONVERT_PARAM));
        stConvertParam.nWidth = stImageInfo.nWidth; // ch:图像宽 | en:image
                                                    // width
        stConvertParam.nHeight =
            stImageInfo.nHeight; // ch:图像高 | en:image height
        // stConvertParam.pSrcData = m_pBufForDriver; //ch:输入数据缓存 |
        // en:input data buffer
        stConvertParam.pSrcData =
            pDate; // ch:输入数据缓存 | en:input data buffer
        stConvertParam.nSrcDataLen =
            stImageInfo.nFrameLen; // ch:输入数据大小 | en:input data size
        stConvertParam.enSrcPixelType =
            stImageInfo.enPixelType; // ch:输入像素格式 | en:input pixel format
        stConvertParam.enDstPixelType =
            PixelType_Gvsp_BGR8_Packed; // ch:输出像素格式 | en:output pixel
                                        // format  适用于OPENCV的图像格式
        // stConvertParam.enDstPixelType = PixelType_Gvsp_RGB8_Packed;
        //输出像素格式 | en:output pixel format
        stConvertParam.pDstBuffer =
            m_pBufForSaveImage; // ch:输出数据缓存 | en:output data buffer
        stConvertParam.nDstBufferSize =
            m_nBufSizeForSaveImage; // ch:输出缓存大小 | en:output buffer size
        MV_CC_ConvertPixelType(m_hDevHandle, &stConvertParam);

        *getImage = cv::Mat(stImageInfo.nHeight, stImageInfo.nWidth, CV_8UC3,
                            m_pBufForSaveImage);
    }
    (*getImage).copyTo(image);
    (*getImage).release();
    free(pDate);
    free(m_pBufForSaveImage);
    return 0;
}


//设置曝光时间
int CMvCamera::setExposureTime(float ExposureTimeNum)
{
    int tempValue =
        MV_CC_SetFloatValue(m_hDevHandle, "ExposureTime", ExposureTimeNum);
    if (tempValue != 0) {
        return -1;
    } else {
        return 0;
    }
}
3)mythread.cpp
#include "mythread.h"

MyThread::MyThread()
{
}

MyThread::~MyThread()
{
    terminate();
    if (cameraPtr != NULL) {
        delete cameraPtr;
    }
    if (imagePtr != NULL) {
        delete imagePtr;
    }
}

void MyThread::getCameraPtr(CMvCamera *camera)
{
    cameraPtr = camera;
}

void MyThread::getImagePtr(Mat *image)
{
    imagePtr = image;
}

void MyThread::getCameraIndex(int index)
{
    cameraIndex = index;
}

void MyThread::run()
{
    if (cameraPtr == NULL) {
        return;
    }

    if (imagePtr == NULL) {
        return;
    }


    while (!isInterruptionRequested()) {
        std::cout << "Thread_Trigger:" << cameraPtr->softTrigger() << std::endl;
        std::cout << "Thread_Readbuffer:" << cameraPtr->ReadBuffer(*imagePtr)
                  << std::endl;
        //cameraPtr->ReadBuffer(*imagePtr2);
        emit mess();
        emit Display(imagePtr); //发送信号lbl_camera_L接收并显示

        emit GetPtr(imagePtr); //发送信号yolo进行检测并显示


        msleep(30);
    }
}

4)widget.cpp
#include "widget.h"
#include "./ui_widget.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <QDebug>

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->lbl_camera_L->setPixmap(QPixmap(":/icon/MVS.png"));
    ui->lbl_camera_L->setScaledContents(true);

    //ui->lbl_camera_R->setPixmap(QPixmap("back_img.jpg"));
    //ui->lbl_camera_R->setScaledContents(true);

    // 相机初始化控件
    ui->pbn_enum_camera->setEnabled(true);
    ui->pbn_open_camera->setEnabled(false);
    ui->pbn_close_camera->setEnabled(false);
    ui->cmb_camera_index->setEnabled(false);

    // 图像采集控件
    ui->rdo_continue_mode->setEnabled(false);
    ui->rdo_softigger_mode->setEnabled(false);
    ui->pbn_start_grabbing->setEnabled(false);
    ui->pbn_stop_grabbing->setEnabled(false);
    ui->pbn_software_once->setEnabled(false);

    // 参数控件
    ui->le_set_exposure->setEnabled(false);
    ui->le_set_gain->setEnabled(false);

    // 保存图像控件
    ui->pbn_save_BMP->setEnabled(false);
    ui->pbn_save_JPG->setEnabled(false);

    // 线程对象实例化
    myThread_Camera_show = new MyThread; //相机线程对象


    //发送信号实现页面切换
    connect(ui->pbn_return_main,SIGNAL(clicked()),this,SIGNAL(back()));

    connect(myThread_Camera_show, SIGNAL(Display(const Mat *)), this,
            SLOT(display_myImage_L(const Mat *)));


    // 曝光和增益的输入控件限制值
    QRegExp int_rx("100000|([0-9]{0,5})");
    ui->le_set_exposure->setValidator(new QRegExpValidator(int_rx, this));
    QRegExp double_rx("15|([0-9]{0,1}[0-5]{1,2}[\.][0-9]{0,1})");
    ui->le_set_gain->setValidator(new QRegExpValidator(double_rx, this));


}

Widget::~Widget()
{
    delete ui;
    delete mainWid;
    delete myThread_Camera_show;
    delete myImage_L;
}

//创建关闭子窗体事件,显示主窗体
void Widget::closeEvent(QCloseEvent *event)
{
    emit closedWid();     //发射closed信号
    event->accept();

}


void Widget::on_pbn_enum_camera_clicked()
{
    memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
    int nRet = MV_OK;
    nRet = CMvCamera::EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_stDevList);

    devices_num = m_stDevList.nDeviceNum;
    if (devices_num == 0) {
        QString cameraInfo;
        cameraInfo =
            QString::fromLocal8Bit("暂无设备可连接,请检查设备是否连接正确!");
        ui->lbl_camera_messagee->setText(cameraInfo);
    }
    if (devices_num > 0) {
        QString cameraInfo;
        for (int i = 0; i < devices_num; i++) {
            MV_CC_DEVICE_INFO *pDeviceInfo = m_stDevList.pDeviceInfo[i];
            QString cameraInfo_i = PrintDeviceInfo(pDeviceInfo, i);
            cameraInfo.append(cameraInfo_i);
            cameraInfo.append("\n");
            ui->cmb_camera_index->addItem(QString::number(i+1));
        }
        ui->lbl_camera_messagee->setText(cameraInfo);
        ui->pbn_open_camera->setEnabled(true);
        ui->cmb_camera_index->setEnabled(true);
    }
}

//打印相机的型号、ip等信息
QString Widget::PrintDeviceInfo(MV_CC_DEVICE_INFO *pstMVDevInfo, int num_index)
{
    QString cameraInfo_index;
    cameraInfo_index = QString::fromLocal8Bit("相机序号:");
    cameraInfo_index.append(QString::number(num_index+1));
    cameraInfo_index.append("\t\t");
    // 海康GIGE协议的相机
    if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE) {
        int nIp1 =
            ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >>
             24);
        int nIp2 =
            ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >>
             16);
        int nIp3 =
            ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >>
             8);
        int nIp4 =
            (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);

        cameraInfo_index.append(QString::fromLocal8Bit("相机型号:"));
        std::string str_name =
            (char *)pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName;
        cameraInfo_index.append(QString::fromStdString(str_name));
        cameraInfo_index.append("\n");
        cameraInfo_index.append(QString::fromLocal8Bit("当前相机IP地址:"));
        cameraInfo_index.append(QString::number(nIp1));
        cameraInfo_index.append(".");
        cameraInfo_index.append(QString::number(nIp2));
        cameraInfo_index.append(".");
        cameraInfo_index.append(QString::number(nIp3));
        cameraInfo_index.append(".");
        cameraInfo_index.append(QString::number(nIp4));
        cameraInfo_index.append("\t");
    } else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE) {
        cameraInfo_index.append(QString::fromLocal8Bit("相机型号:"));
        std::string str_name =
            (char *)pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName;
        cameraInfo_index.append(QString::fromStdString(str_name));
        cameraInfo_index.append("\n");
    } else {
        cameraInfo_index.append(QString::fromLocal8Bit("相机型号:未知"));
    }
    cameraInfo_index.append(QString::fromLocal8Bit("相机品牌:海康威视"));
    return cameraInfo_index;
}

void Widget::on_pbn_open_camera_clicked()
{
    ui->pbn_open_camera->setEnabled(false);
    ui->pbn_close_camera->setEnabled(true);
    ui->rdo_continue_mode->setEnabled(true);
    ui->rdo_softigger_mode->setEnabled(true);

    ui->rdo_continue_mode->setCheckable(true);
    // 参数据控件
    ui->le_set_exposure->setEnabled(true);
    ui->le_set_gain->setEnabled(true);


    OpenDevices();
}

void Widget::OpenDevices()
{
    int nRet = MV_OK;
    // 创建相机指针对象
    for (unsigned int i = 0, j = 0; j < m_stDevList.nDeviceNum; j++, i++) {
        m_pcMyCamera[i] = new CMvCamera;
        // 相机对象初始化
        m_pcMyCamera[i]->m_pBufForDriver = NULL;
        m_pcMyCamera[i]->m_pBufForSaveImage = NULL;
        m_pcMyCamera[i]->m_nBufSizeForDriver = 0;
        m_pcMyCamera[i]->m_nBufSizeForSaveImage = 0;
        m_pcMyCamera[i]->m_nTLayerType =
            m_stDevList.pDeviceInfo[j]->nTLayerType;

        nRet = m_pcMyCamera[i]->Open(m_stDevList.pDeviceInfo[j]); //打开相机
        //设置触发模式
        m_pcMyCamera[i]->setTriggerMode(TRIGGER_ON);
        //设置触发源为软触发
        m_pcMyCamera[i]->setTriggerSource(TRIGGER_SOURCE);
        //设置曝光时间,初始为40000,并关闭自动曝光模式
        m_pcMyCamera[i]->setExposureTime(EXPOSURE_TIME);
        m_pcMyCamera[i]->SetEnumValue("ExposureAuto",
                                      MV_EXPOSURE_AUTO_MODE_OFF);
    }
}

void Widget::on_rdo_continue_mode_clicked()
{
    ui->pbn_start_grabbing->setEnabled(true);
    m_nTriggerMode = TRIGGER_ON;
}

void Widget::on_rdo_softigger_mode_clicked()
{
    // 如果开始选择的连续模式,切换到触发模式之前,需要先停止采集
    if (m_bContinueStarted == 1) {
        on_pbn_stop_grabbing_clicked(); //先执行停止采集
    }

    ui->pbn_start_grabbing->setEnabled(false);
    ui->pbn_software_once->setEnabled(true);

    m_nTriggerMode = TRIGGER_OFF;
    for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) {
        m_pcMyCamera[i]->setTriggerMode(m_nTriggerMode);
    }
}

void Widget::on_pbn_start_grabbing_clicked()
{
    // 触发模式标记一下,切换触发模式时先执行停止采集图像函数
    m_bContinueStarted = 1;

    ui->pbn_start_grabbing->setEnabled(false);
    ui->pbn_stop_grabbing->setEnabled(true);
    // 保存图像控件
    ui->pbn_save_BMP->setEnabled(true);
    // 图像采集控件
    ui->pbn_save_JPG->setEnabled(true);

    int camera_Index = 0;

    // 先判断什么模式,再判断是否正在采集
    if (m_nTriggerMode == TRIGGER_ON) {
        // 开始采集之后才创建workthread线程
        for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) {
            //开启相机采集
            m_pcMyCamera[i]->StartGrabbing();
            camera_Index = i;
            if (camera_Index == 0) {
                myThread_Camera_show->getCameraPtr(
                    m_pcMyCamera[0]); //线程获取左相机指针
                myThread_Camera_show->getImagePtr(
                    myImage_L); //线程获取图像指针
                myThread_Camera_show->getCameraIndex(0); //相机 Index==0

                if (!myThread_Camera_show->isRunning()) {
                    myThread_Camera_show->start();
                    m_pcMyCamera[0]->softTrigger();
                    m_pcMyCamera[0]->ReadBuffer(*myImage_L); //读取Mat格式的图像
                }
            }
        }
    }
}

void Widget::on_pbn_stop_grabbing_clicked()
{
    ui->pbn_start_grabbing->setEnabled(true);
    ui->pbn_stop_grabbing->setEnabled(false);

    for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) {
        //关闭相机
        if (myThread_Camera_show->isRunning()) {
            m_pcMyCamera[0]->StopGrabbing();
            myThread_Camera_show->requestInterruption();
            myThread_Camera_show->wait();
        }
    }
}

void Widget::on_pbn_software_once_clicked()
{
    // 保存图像控件
    ui->pbn_save_BMP->setEnabled(true);
    ui->pbn_save_JPG->setEnabled(true);

    if (m_nTriggerMode == TRIGGER_OFF) {
        int nRet = MV_OK;
        for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) {
            //开启相机采集
            m_pcMyCamera[i]->StartGrabbing();

            if (i == 0) {
                nRet = m_pcMyCamera[i]->CommandExecute("TriggerSoftware");
                m_pcMyCamera[i]->ReadBuffer(*myImage_L);
                display_myImage_L(myImage_L);       //左相机图像
                display_myImage_Main(myImage_L);    //主界面显示
            }
        }
    }
}

//在相机配置界面显示
void Widget::display_myImage_L(const Mat *imagePrt)
{

    cv::Mat rgb;

    //判断是黑白、彩色图像
    QImage QmyImage_L;
    if (myImage_L->channels() > 1) {
        cv::cvtColor(*imagePrt, rgb, CV_BGR2RGB);
        QmyImage_L = QImage((const unsigned char *)(rgb.data), rgb.cols,
                            rgb.rows, QImage::Format_RGB888);
    } else {
        cv::cvtColor(*imagePrt, rgb, CV_GRAY2RGB);
        QmyImage_L = QImage((const unsigned char *)(rgb.data), rgb.cols,
                            rgb.rows, QImage::Format_RGB888);
    }


    QmyImage_L = (QmyImage_L)
            .scaled(ui->lbl_camera_L->size(), Qt::IgnoreAspectRatio,
                    Qt::SmoothTransformation); //饱满填充

    ui->lbl_camera_L->setPixmap(QPixmap::fromImage(QmyImage_L));

}

//在主界面显示
void Widget::display_myImage_Main(const Mat *imagePrt)
{
    cv::Mat rgb;
    cv::cvtColor(*imagePrt, rgb, CV_BGR2RGB);

    QImage QmyImage_L;
    QmyImage_L = QImage((const unsigned char *)(rgb.data), rgb.cols,
                            rgb.rows, QImage::Format_RGB888);

    QmyImage_L = (QmyImage_L)
                     .scaled(ui->lbl_camera_L->size(), Qt::IgnoreAspectRatio,
                             Qt::SmoothTransformation); //饱满填充
    QDateTime curdatetime;
    if(!QmyImage_L.isNull()){
            curdatetime=QDateTime::currentDateTime();
            QString str=curdatetime.toString("yyyyMMddhhmmss");
            QString filename="/home/joe/picture/"+str+".png";
            qDebug()<<"正在保存"<<filename<<"图片,请稍候...";
            /* save(arg1,arg2,arg3)重载函数,arg1 代表路径文件名,
             * arg2 保存的类型,arg3 代表保存的质量等级 */
            QmyImage_L.save(filename,"PNG",-1);
            qDebug()<<"保存完成!";
        }

    //显示图像
    this->mainWid=new MainWidget();
    //ui->lbl_camera_L->setPixmap(QPixmap::fromImage(QmyImage_L));
    mainWid->ui->lbl_res_pic->setPixmap(QPixmap::fromImage(QmyImage_L));
}

void Widget::CloseDevices()
{
    for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++) {
        // 关闭线程、相机
        if (myThread_Camera_show->isRunning()) {
            myThread_Camera_show->requestInterruption();
            myThread_Camera_show->wait();
            m_pcMyCamera[0]->StopGrabbing();
        }

        m_pcMyCamera[i]->Close();
    }

    // 关闭之后再枚举一遍
    memset(&m_stDevList, 0,
           sizeof(MV_CC_DEVICE_INFO_LIST)); // 初始化设备信息列表
    int devices_num = MV_OK;
    devices_num =
        CMvCamera::EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE,
                               &m_stDevList); // 枚举子网内所有设备,相机设备数量
}

void Widget::on_pbn_close_camera_clicked()
{
    ui->pbn_open_camera->setEnabled(true);
    ui->pbn_close_camera->setEnabled(false);
    // 图像采集控件
    ui->rdo_continue_mode->setEnabled(false);
    ui->rdo_softigger_mode->setEnabled(false);
    ui->pbn_start_grabbing->setEnabled(false);
    ui->pbn_stop_grabbing->setEnabled(false);
    ui->pbn_software_once->setEnabled(false);
    // 参数控件
    ui->le_set_exposure->setEnabled(false);
    ui->le_set_gain->setEnabled(false);
    // 保存图像控件
    ui->pbn_save_BMP->setEnabled(false);
    ui->pbn_save_JPG->setEnabled(false);

    // 关闭设备,销毁线程
    CloseDevices();
    ui->lbl_camera_messagee->clear();
    ui->lbl_camera_L->clear();
    ui->lbl_camera_L->setPixmap(QPixmap(":/icon/MVS.png"));
    //ui->lbl_camera_R->clear();
}

void Widget::on_pbn_save_BMP_clicked()
{
    m_nSaveImageType = MV_Image_Bmp;
    SaveImage();

}

void Widget::on_pbn_save_JPG_clicked()
{
    m_nSaveImageType = MV_Image_Jpeg;
    SaveImage();
}

void Widget::SaveImage()
{
    // 获取1张图
    MV_FRAME_OUT_INFO_EX stImageInfo = { 0 };
    memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
    unsigned int nDataLen = 0;
    int nRet = MV_OK;

    //保存图片的路径
    QString saveDirPath="/home/joe/program/VersionDetect/LiteTest/image/";
    for (int i = 0; i < devices_num; i++) {

        //保存图像的缓存类指针
        const char *imageName_c_str=NULL;
        // 仅在第一次保存图像时申请缓存,在CloseDevice时释放
        if (NULL == m_pcMyCamera[i]->m_pBufForDriver) {
            unsigned int nRecvBufSize = 0;

            m_pcMyCamera[i]->GetIntValue("PayloadSize", &nRecvBufSize);

            m_pcMyCamera[i]->m_nBufSizeForDriver = nRecvBufSize; // 一帧数据大小

            m_pcMyCamera[i]->m_pBufForDriver =
                (unsigned char *)malloc(m_pcMyCamera[i]->m_nBufSizeForDriver);
        }

        nRet = m_pcMyCamera[i]->GetOneFrameTimeout(
            m_pcMyCamera[i]->m_pBufForDriver, &nDataLen,
            m_pcMyCamera[i]->m_nBufSizeForDriver, &stImageInfo, 1000);
        if (MV_OK == nRet) {
            // 仅在第一次保存图像时申请缓存,在 CloseDevice 时释放
            if (NULL == m_pcMyCamera[i]->m_pBufForSaveImage) {
                // BMP图片大小:width * height * 3 + 2048(预留BMP头大小)
                m_pcMyCamera[i]->m_nBufSizeForSaveImage =
                    stImageInfo.nWidth * stImageInfo.nHeight * 3 + 2048;

                m_pcMyCamera[i]->m_pBufForSaveImage = (unsigned char *)malloc(
                    m_pcMyCamera[i]->m_nBufSizeForSaveImage);
            }
            // 设置对应的相机参数
            MV_SAVE_IMAGE_PARAM_EX stParam = { 0 };
            stParam.enImageType = m_nSaveImageType; // 需要保存的图像类型
            stParam.enPixelType = stImageInfo.enPixelType; // 相机对应的像素格式
            stParam.nBufferSize =
                m_pcMyCamera[i]->m_nBufSizeForSaveImage; // 存储节点的大小
            stParam.nWidth = stImageInfo.nWidth;         // 相机对应的宽
            stParam.nHeight = stImageInfo.nHeight;       // 相机对应的高
            stParam.nDataLen = stImageInfo.nFrameLen;
            stParam.pData = m_pcMyCamera[i]->m_pBufForDriver;
            stParam.pImageBuffer = m_pcMyCamera[i]->m_pBufForSaveImage;
            stParam.nJpgQuality = 90; // jpg编码,仅在保存Jpg图像时有效

            nRet = m_pcMyCamera[i]->SaveImage(&stParam);


            QString image_name;
            //图像名称
            char chImageName[IMAGE_NAME_LEN] = {0};
            if (MV_Image_Bmp == stParam.enImageType) {
                if (i == 0) {
                    snprintf(chImageName, IMAGE_NAME_LEN,
                             "Image_w%d_h%d_fn%d_L.bmp", stImageInfo.nWidth,
                             stImageInfo.nHeight, stImageInfo.nFrameNum);
                    image_name = "Image_w";
                    image_name.append(QString::number(stImageInfo.nWidth));
                    image_name.append("_h");
                    image_name.append(QString::number(stImageInfo.nHeight));
                    image_name.append("_fn");
                    image_name.append(QString::number(stImageInfo.nFrameNum));
                    image_name.append("_L.bmp");
                }
                if (i == 1) {
                    snprintf(chImageName, IMAGE_NAME_LEN,
                             "Image_w%d_h%d_fn%03d_R.bmp", stImageInfo.nWidth,
                             stImageInfo.nHeight, stImageInfo.nFrameNum);
                }

            } else if (MV_Image_Jpeg == stParam.enImageType) {
                if (i == 0) {
                    snprintf(chImageName, IMAGE_NAME_LEN,
                             "Image_w%d_h%d_fn%d_L.jpg", stImageInfo.nWidth,
                             stImageInfo.nHeight, stImageInfo.nFrameNum);
                    image_name = "Image_w";
                    image_name.append(QString::number(stImageInfo.nWidth));
                    image_name.append("_h");
                    image_name.append(QString::number(stImageInfo.nHeight));
                    image_name.append("_fn");
                    image_name.append(QString::number(stImageInfo.nFrameNum));
                    image_name.append("_L.jpg");
                }
                if (i == 1) {
                    snprintf(chImageName, IMAGE_NAME_LEN,
                             "Image_w%d_h%d_fn%03d_R.jpg", stImageInfo.nWidth,
                             stImageInfo.nHeight, stImageInfo.nFrameNum);
                }

            }

            QString imagePath = saveDirPath + image_name;
            QByteArray ba = imagePath.toLatin1();
            imageName_c_str = ba.data();


            FILE *fp = fopen(imageName_c_str, "wb");

            fwrite(m_pcMyCamera[i]->m_pBufForSaveImage, 1, stParam.nImageLen,
                   fp);
            fclose(fp);


//            ui->lbl_camera_R->setPixmap(QPixmap(image_name));
//            ui->lbl_camera_R->setScaledContents(true);
        }
    }
}


void Widget::on_le_set_exposure_textChanged(const QString &arg1)
{
    //设置曝光时间
    QString str = ui->le_set_exposure->text(); // 读取
    int exposure_Time = str.toInt();

    for (int i = 0; i < devices_num; i++) {
        m_pcMyCamera[i]->SetEnumValue("ExposureAuto",
                                      MV_EXPOSURE_AUTO_MODE_OFF);
        m_pcMyCamera[i]->SetFloatValue("ExposureTime", exposure_Time);
    }
}

void Widget::on_le_set_gain_textChanged(const QString &arg1)
{
    QString str = ui->le_set_gain->text(); // 读取
    float gain = str.toFloat();

    for (int i = 0; i < devices_num; i++) {
        m_pcMyCamera[i]->SetEnumValue("GainAuto", 0);
        m_pcMyCamera[i]->SetFloatValue("Gain", gain);
    }
}


void Widget::on_pbn_return_main_clicked()
{
    //发送信号实现页面切换
    //connect(ui->pbn_return_main,SIGNAL(clicked()),this,SIGNAL(back()));
}

5)yolov5.cpp

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


bool YoloV5::readModel(Net &net, string &netPath,bool isCuda = false) {
    try {
        net = readNetFromONNX(netPath);
    }
    catch (const std::exception&) {
        return false;
    }
    //cuda
    if (isCuda) {
        net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
        net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
    }
    //cpu
    else {
        net.setPreferableBackend(cv::dnn::DNN_BACKEND_DEFAULT);
        net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
    }
    return true;
}
bool YoloV5::Detect(Mat &SrcImg,Net &net,vector<Output> &output) {
    Mat blob;
    int col = SrcImg.cols;
    int row = SrcImg.rows;
    int maxLen = MAX(col, row);
    Mat netInputImg = SrcImg.clone();
    if (maxLen > 1.2*col || maxLen > 1.2*row) {
        Mat resizeImg = Mat::zeros(maxLen, maxLen, CV_8UC3);
        SrcImg.copyTo(resizeImg(Rect(0, 0, col, row)));
        netInputImg = resizeImg;
    }
    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(0, 0,0), 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> netOutputImg;
//    vector<string> outputLayerName{"652","669", "686","output" };
//    net.forward(netOutputImg, outputLayerName[2]); //获取output的输出
    net.forward(netOutputImg, net.getUnconnectedOutLayersNames());

    //接上面
    std::vector<int> classIds;//结果id数组
    std::vector<float> confidences;//结果每个id对应置信度数组
    std::vector<cv::Rect> boxes;//每个id矩形框
    float ratio_h = (float)netInputImg.rows / netHeight;
    float ratio_w = (float)netInputImg.cols / netWidth;
    int net_width = className.size() + 5;  //输出的网络宽度是类别数+5
    float* pdata = (float*)netOutputImg[0].data;
    for (int stride = 0; stride < 3; stride++) {    //stride
        int grid_x = (int)(netWidth / netStride[stride]);
        int grid_y = (int)(netHeight / netStride[stride]);
        for (int anchor = 0; anchor < 3; anchor++) { //anchors
            const float anchor_w = netAnchors[stride][anchor * 2];
            const float anchor_h = netAnchors[stride][anchor * 2 + 1];
            for (int i = 0; i < grid_y; i++) {
                for (int j = 0; j < grid_y; j++) {
                    float box_score = Sigmoid(pdata[4]);//获取每一行的box框中含有某个物体的概率
                    if (box_score > boxThreshold) {
                        //为了使用minMaxLoc(),将85长度数组变成Mat对象
                        cv::Mat scores(1,className.size(), CV_32FC1, pdata+5);
                        Point classIdPoint;
                        double max_class_socre;
                        minMaxLoc(scores, 0, &max_class_socre, 0, &classIdPoint);
                        max_class_socre = Sigmoid((float)max_class_socre);
                        if (max_class_socre > classThreshold) {
                            //rect [x,y,w,h]
                            float x = (Sigmoid(pdata[0]) * 2.f - 0.5f + j) * netStride[stride];  //x
                            float y = (Sigmoid(pdata[1]) * 2.f - 0.5f + i) * netStride[stride];   //y
                            float w = powf(Sigmoid(pdata[2]) * 2.f, 2.f) * anchor_w;   //w
                            float h = powf(Sigmoid(pdata[3]) * 2.f, 2.f) * anchor_h;  //h
                            int left = (x - 0.5*w)*ratio_w;
                            int top = (y - 0.5*h)*ratio_h;
                            classIds.push_back(classIdPoint.x);
                            confidences.push_back(max_class_socre*box_score);
                            boxes.push_back(Rect(left, top, int(w*ratio_w), int(h*ratio_h)));
                        }
                    }
                    pdata += net_width;//指针移到下一行
                }
            }
        }
    }
    vector<int> nms_result;
    NMSBoxes(boxes, confidences, classThreshold, nmsThreshold, nms_result);
    for (int i = 0; i < nms_result.size(); i++) {
        int idx = nms_result[i];
        Output result;
        result.id = classIds[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx];
        output.push_back(result);
    }

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

//这里的color是颜色数组,对没一个id随机分配一种颜色
void YoloV5::drawPred(Mat &img, vector<Output> result, vector<Scalar> color) {
    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);
        rectangle(img, result[i].box, Scalar(255, 0, 0), 2, 8);
        string label = className[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);
        //rectangle(frame, Point(left, top - int(1.5 * labelSize.height)), Point(left + int(1.5 * labelSize.width), top + baseLine), Scalar(0, 255, 0), FILLED);
        //putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 1, color[result[i].id], 2);
        putText(img, label, Point(30, 100), FONT_HERSHEY_SIMPLEX, 2.5, Scalar(255, 0, 0), 2);
    }
}

void getTime(QString msg)
{
    auto start = std::chrono::steady_clock::now();
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double, std::milli> elapsed = end - start;
    QString output = msg.arg(elapsed.count());
    string out = output.toStdString();
    cout<<out<<endl;
}

这里需要讲一下,如果如前面所示你用的单一输出,如下图

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

要用 qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

outputLayerName来获取数组,如作者的onnx模型,名称为output,那就获取整个output的权重,数据,如果导出格式如下图所示:

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

那么你就要用

qt6 深度学习部署,qt,opencv,YOLO,深度学习,c++,linux

以上的forword函数来获取权重文件,其他地方基本无需改动,当然肯定部署会出现各种报错,在你读懂了代码之后,基本上都能改好

写在最后:如果想要qt源码文件,可以进入以下链接,创作不易,谢谢大家支持

qt源码文件文章来源地址https://www.toymoban.com/news/detail-853689.html

到了这里,关于基于树莓派Qt+opencv+yolov5-Lite+C++部署深度学习推理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • C++模型部署:qt+yolov5/6+onnxruntime+opencv

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

    2024年02月05日
    浏览(37)
  • yolov5分割+检测c++ qt 中部署,以opencv方式(详细代码(全)+复制可用)

    1:版本说明: qt 5.12.10 opencv 4.5.3 (yolov5模型部署要求opencv4.5.0) 2:检测的代码 yolo.h yolo.cpp 检测的调用代码测试案例 这段调用的例子,只要把frame 改成你们自己的图片即可 4:分割的主要代码 YoloSeg.h YoloSeg.cpp yolov5_seg_utils.h  yolov5_seg_utils.cpp 分割的调用代码测试案例  分割的

    2024年02月03日
    浏览(29)
  • 树莓派部署YOLOv5模型

    本文章是关于树莓派部署YOLOv5s模型,实际测试效果的FPS仅有0.15,不够满足实际检测需要,各位大佬可以参考参考。 1、在树莓派中安装opencv(默认安装好python3) 2、导出onnx模型 从YOLOv5官网下载源代码和YOLOv5s.pt文件 YOLOv5官网 YOLOv5s.pt下载 按照作者提示安装环境,使用它自带

    2024年02月11日
    浏览(34)
  • 【解惑笔记】树莓派+OpenCV+YOLOv5目标检测(Pytorch框架)

     -【学习资料】 子豪兄的零基础树莓派教程 https://github.com/TommyZihao/ZihaoTutorialOfRaspberryPi/blob/master/%E7%AC%AC2%E8%AE%B2%EF%BC%9A%E6%A0%91%E8%8E%93%E6%B4%BE%E6%96%B0%E6%89%8B%E6%97%A0%E7%97%9B%E5%BC%80%E6%9C%BA%E6%8C%87%E5%8D%97.md#%E7%83%A7%E5%BD%95%E9%95%9C%E5%83%8F 第2讲:树莓派新手无痛开机指南【子豪兄的树莓派

    2024年02月14日
    浏览(28)
  • 【问题记录】树莓派+OpenCV+YOLOv5目标检测(Pytorch框架)

     -【学习资料】 子豪兄的零基础树莓派教程 https://github.com/TommyZihao/ZihaoTutorialOfRaspberryPi/blob/master/%E7%AC%AC2%E8%AE%B2%EF%BC%9A%E6%A0%91%E8%8E%93%E6%B4%BE%E6%96%B0%E6%89%8B%E6%97%A0%E7%97%9B%E5%BC%80%E6%9C%BA%E6%8C%87%E5%8D%97.md#%E7%83%A7%E5%BD%95%E9%95%9C%E5%83%8F 第2讲:树莓派新手无痛开机指南【子豪兄的树莓派

    2024年02月02日
    浏览(42)
  • QT+Opencv+yolov5实现监测

    功能说明:使用QT+Opencv+yolov5实现监测 =============================== 仓库链接:https://gitee.com/wangyoujie11/qt_yolov5.git git本仓库到本地 一、环境配置 1.opencv配置 将OpenCV-MinGW-Build-OpenCV-4.5.2-x64文件夹放在自己的一个目录下,如我的路径: 编辑系统环境变量【配置完成之后,要 确定 】

    2024年04月11日
    浏览(28)
  • QT部署YOLOV5

      这篇文章主要用来记录用pyqt5搭建YOLOV5的检测平台,代码是在yoloV5官方代码上加了个qt模块,目前可以支持 GPU/CPU下pt/onnx推理 。 根据代码中的requirements.txt进行环境搭建,前提是你已有Python环境 这里需要注意的是,pyqt5需要配置下环境,参考连接为:安装pyqt5,步骤如下:

    2024年02月05日
    浏览(27)
  • yolov5 opencv dnn部署自己的模型

    github开源代码地址 yolov5官网还提供的dnn、tensorrt推理链接 本人使用的opencv c++ github代码,代码作者非本人,也是上面作者推荐的链接之一 如果想要尝试直接运行源码中的yolo.cpp文件和yolov5s.pt推理sample.mp4,请参考这个链接的介绍 使用github源码结合自己导出的onnx模型推理自己的

    2024年01月23日
    浏览(36)
  • yolov5 opencv dnn部署 github代码

    源码地址 yolov5官网还提供的dnn、tensorrt推理链接 本人使用的opencv c++ github代码,代码作者非本人,也是上面作者推荐的链接之一 实现推理源码中作者的yolov5s.onnx 推理条件 实现推理code中作者的yolov5s.onnx windows 10 Visual Studio 2019 Nvidia GeForce GTX 1070 opencv 4.5.5、opencv4.7.0 (注意 4.7.0代码

    2024年01月23日
    浏览(39)
  • YOLOv5 5.0版本 + OpenCV 4.5.2 部署

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 版本一定要和我的一致,不然不保证能run起来!!! 该任务我们选择YOLOv5 5.0 版本。在https://github.com/ultralytics/yolov5中,可以选择对应的版本下载。由于GitHub上 v5 5.0 版本的C++代码居多,故选择 v5 5.0。(

    2024年02月10日
    浏览(31)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包