基于树莓派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开发一个简单的界面,根据自己的需求,将所有模块融合进去,最后就可以推理成功啦。话不多说,作者的简单界面是这样的。

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

一、数据集准备

        由于作者做的是工业相关的一个小项目,基于甲方提供的设备,数据集采集是使用海康威视的工业相机,后续实时推理也是连接了海康工业相机,具体如何在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整合为一个输出,方便后续推理的时候读取。

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

 四、树莓派部署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图形化界面,需要更改的地方如下图所示

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

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

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

sudo make -j4

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

        编译完成之后,执行命令,安装opencv配置文件到默认路径

sudo make install
        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)头文件

头文件有如上五个,其中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;
}

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

要用 

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

那么你就要用

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

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

qt源码文件

  • 20
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
基于Yolov5OpenCV的课堂学习状态识别检测源码可以通过以下步骤实现: 1. 安装Yolov5OpenCV库:首先,需要在计算机上安装Yolov5OpenCV库。可以通过pip命令来安装这些库,如'pip install yolov5'和'pip install opencv-python'。 2. 下载Yolov5模型权重:从Yolov5的GitHub页面上可以下载预训练的Yolov5模型权重文件,如'yolov5s.pt'。 3. 设置输入源:可以使用OpenCV来设置输入源,如摄像头、视频文件或图像。例如,可以使用以下代码来设置使用摄像头作为输入源: ```python import cv2 cap = cv2.VideoCapture(0) ``` 4. 加载Yolov5模型:使用Yolov5的load_model()方法来加载预训练的Yolov5模型权重文件。 ```python from models.experimental import attempt_load model = attempt_load('yolov5s.pt') ``` 5. 进行目标检测:使用Yolov5模型对输入源中的图像进行目标检测。可以使用以下代码来实现: ```python ret, frame = cap.read() results = model(frame) ``` 6. 进行状态识别:根据目标检测的结果,使用OpenCV的图像处理和分析方法来进行状态识别。例如,可以检测人脸表情、姿势、动作等。 7. 输出结果:根据状态识别的结果,使用OpenCV的图像绘制函数来标注人脸或其他识别到的物体,并显示在屏幕上。 以上就是基于Yolov5OpenCV的课堂学习状态识别检测源码的基本步骤。可以根据实际需求进行相应的代码实现和优化。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值