(3) Qt multi-threading realizes Haikang industrial camera image acquisition + algorithm detection + OpenGLWidget real-time display

Series Article Directory

提示:这里是该系列文章的所有文章的目录
Chapter 1: (1) Qt+OpenCV calls Haikang industrial camera SDK sample development
Chapter 2: (2) Qt multi-threading realizes real-time image acquisition of Haikang industrial cameras
Chapter 3: (3) Qt multi-threading realizes Hikvision industrial Camera image acquisition + algorithm detection + OpenGLWidget real-time display



foreword

This article optimizes part of the code and functions of the example in the previous article. After the camera captures the image, an additional algorithm processing of the corresponding image is added, and the OpenGLWidget is used to display the image content on the main interface. The relevant content is displayed here for Everyone learns, if there are mistakes, everyone is welcome to criticize and correct.

Project effect
Please add a picture description


提示:以下是本篇文章正文内容,下面案例可供参考

1. Project structure

The following is the source code file directory of my example. Compared with the previous article, the MyGLWidget image display class is added under the project main folder, and the GlobalVar global variable class and ProcessingThread image processing thread class are added under the HikSdk subfolder.
Please add a picture description
According to the content of the reference blog, the following flow chart is made to represent the process of camera image acquisition to display:
Please add a picture description

2. Improvement of MyGLWidget

Please add a picture description

Here is the implementation code of the custom MyGLWidget class, which mainly rewrites the paintEvent:
1.myglwidget.h

#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QPainter>

class MyGLWidget : public QOpenGLWidget
{
    
    
    Q_OBJECT

public:
    MyGLWidget(QWidget *parent);
    ~MyGLWidget();

    void setPixmap(QPixmap pixmap,QString text);

protected:
    void paintEvent(QPaintEvent *);   //重写paintEvent

private:
    QPixmap myPixmap;
    QString myText;

};
#endif // MYGLWIDGET_H

2.myglwidget.cpp

#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    
    
    myText = "";
}

MyGLWidget::~MyGLWidget()
{
    
    

}

void MyGLWidget::setPixmap(QPixmap pixmap,QString text)
{
    
    
    myPixmap = pixmap;
    myText = text;
    this->update();
}

void MyGLWidget::paintEvent(QPaintEvent *)
{
    
    
    QPainter painter;
    painter.begin(this);
    painter.drawPixmap(0,0,400,400,myPixmap);   //如果使用drawImage,无法实现显示文字功能(文字会被覆盖)
    QPen pen;
    pen.setColor(Qt::red);
    painter.setPen(pen);
    painter.drawText(10,20,myText);   //界面上显示文字
    painter.end();
}

3. Define the global container

The image storage container is declared and defined in globalvar.h and globalvar.cpp, where the extern keyword is used:
1.globalvar.h

#ifndef GLOBALVAR_H
#define GLOBALVAR_H

#include <QVector>
#include "cmvcamera.h"

extern QVector<cv::Mat> m_imageVector_1;   //帧保存容器
extern QVector<cv::Mat> m_imageVector_2;

#endif // GLOBALVAR_H

2.globalvar.cpp

#include "globalvar.h"

QVector<cv::Mat> m_imageVector_1;
QVector<cv::Mat> m_imageVector_2;

4. Detection algorithm thread

A new ProcessingThread algorithm processing thread is created, image processing is carried out in this thread, and after the processing is completed, this thread sends a signal to the main interface for display, and here uses the setThreadId function to correspond to the image acquisition thread: 1.processingthread
. h

#ifndef PROCESSINGTHREAD_H
#define PROCESSINGTHREAD_H

#include <QObject>
#include <QThread>
#include <QImage>
#include <QPixmap>
#include <QMutex>
#include "globalvar.h"

class ProcessingThread : public QThread
{
    
    
    Q_OBJECT

public:
    explicit ProcessingThread(QObject *parent = nullptr);
     ~ProcessingThread();

    void initThread();
    void setThreadId(int id);
    void setSwitchFlag(bool switchFlag);

    void run();

    QImage cvMatToImage(const cv::Mat cvMat);
    QPixmap cvMatToPixmap(const cv::Mat cvMat);

signals:
    void signal_newPixmap(QPixmap newPixmap,int id);

private:
    //QMutex testMutex;

    bool startFlag;
    int m_threadId;
};
#endif // PROCESSINGTHREAD_H

2.processingthread.cpp

#include "processingthread.h"

ProcessingThread::ProcessingThread(QObject *parent)
    : QThread{
    
    parent}
{
    
    
    this->initThread();
}

ProcessingThread::~ProcessingThread()
{
    
    

}

void ProcessingThread::initThread()   //初始化图像处理线程
{
    
    
    startFlag = false;
    m_threadId = 0;
}

void ProcessingThread::setThreadId(int id)
{
    
    
    m_threadId = id;
}

void ProcessingThread::setSwitchFlag(bool switchFlag)
{
    
    
    startFlag = switchFlag;
}

void ProcessingThread::run()
{
    
    
    while(startFlag)
    {
    
    
        if(m_threadId == 0)
        {
    
    
            //qDebug()<<"m_imageVector_1:"<<m_imageVector_1.size();
            if(m_imageVector_1.size() == 1)
            {
    
    
                //testMutex.lock();   //根据实际情况判断是否加锁,注意互斥量
                //此处添加图像算法处理代码,这里以下列函数为例
                QPixmap newPixmap_1 = cvMatToPixmap(m_imageVector_1.at(0));
                //testMutex.unlock();

                //将处理好的图像发现到主界面
                emit signal_newPixmap(newPixmap_1,0);

                //使用完后清空容器
                m_imageVector_1.clear();
            }
            //else   //size超过指定数量时添加异常处理,应根据实际情况修改
            //{
    
    
            //    m_imageVector_1.clear();
            //}
        }
        else if(m_threadId == 1)
        {
    
    
            //qDebug()<<"m_imageVector_2:"<<m_imageVector_1.size();
            if(m_imageVector_2.size() == 1)
            {
    
    
                QPixmap newPixmap_2 = cvMatToPixmap(m_imageVector_2.at(0));
                emit signal_newPixmap(newPixmap_2,1);
                m_imageVector_2.clear();
            }
        }
        msleep(20);   //此处缓冲可减少cpu运行率,注意不要慢于相机线程的缓冲
    }
}

//Mat->QImage
QImage ProcessingThread::cvMatToImage(const cv::Mat cvMat)
{
    
    
    if(cvMat.channels() > 1)
    {
    
    
        return QImage((const unsigned char*)(cvMat.data),cvMat.cols,cvMat.rows,QImage::Format_RGB888);
    }
    else
    {
    
    
        return QImage((const unsigned char*)(cvMat.data),cvMat.cols,cvMat.rows,QImage::Format_Indexed8);
    }
}

//可以将此处修改为算法处理函数
//Mat->QPixmap
QPixmap ProcessingThread::cvMatToPixmap(const cv::Mat cvMat)
{
    
    
    QImage myImage;
    //qDebug()<<"channels:"<<cvMat.channels();
    if(cvMat.channels() > 1)
    {
    
    
        myImage = QImage((const unsigned char*)(cvMat.data),cvMat.cols,cvMat.rows,QImage::Format_RGB888);   //彩色图
    }
    else
    {
    
    
        myImage = QImage((const unsigned char*)(cvMat.data),cvMat.cols,cvMat.rows,QImage::Format_Indexed8);   //灰度图
    }

    //QSize表示图像在界面上的显示尺寸
    return QPixmap::fromImage(myImage).scaled(QSize(400,400),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
}

5. Sample code modification

The above new classes are added by clicking Add New... on the Qt project tree, and the corresponding codes will be automatically generated in .pro and .pri. Here are some remaining code files for display: 1.cmvcamera.h
( Optimized for reading and saving of camera images)

/************************************************************************/
/* 以C++接口为基础,对常用函数进行二次封装,方便用户使用                */
/************************************************************************/

#ifndef _MV_CAMERA_H_
#define _MV_CAMERA_H_

#include <string.h>
#include "MvCameraControl.h"
#include "opencv2/opencv.hpp"
#include <QDebug>

//会跟系统函数定义冲突
//using namespace cv;

#ifndef MV_NULL
#define MV_NULL    0
#endif

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: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 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);
    int GetEnumEntrySymbolic(IN const char* strKey, IN MVCC_ENUMENTRY* pstEnumEntry);

    // 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);

    // ch:绘制圆形辅助线 | en:Draw circle auxiliary line
    int DrawCircle(MVCC_CIRCLE_INFO* pCircleInfo);

    // ch:绘制线形辅助线 | en:Draw lines auxiliary line
    int DrawLines(MVCC_LINES_INFO* pLinesInfo);

    //读取相机中的图像
    int ReadBuffer(cv::Mat &image);

    //保存相机中的图像
    int SaveBuffer(QByteArray imageName);

private:

    void *m_hDevHandle;
};

#endif//_MV_CAMERA_H_

2.cmvcamera.cpp

#include "cmvcamera.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: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 MVCC_INTVALUE_EX *pIntValue)
{
    
    
    return MV_CC_GetIntValueEx(m_hDevHandle, strKey, pIntValue);
}

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

int CMvCamera::GetEnumEntrySymbolic(IN const char* strKey, IN MVCC_ENUMENTRY* pstEnumEntry)
{
    
    
    return MV_CC_GetEnumEntrySymbolic(m_hDevHandle, strKey, pstEnumEntry);
}

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

// ch:绘制圆形辅助线 | en:Draw circle auxiliary line
int CMvCamera::DrawCircle(MVCC_CIRCLE_INFO* pCircleInfo)
{
    
    
    return MV_CC_DrawCircle(m_hDevHandle, pCircleInfo);
}

// ch:绘制线形辅助线 | en:Draw lines auxiliary line
int CMvCamera::DrawLines(MVCC_LINES_INFO* pLinesInfo)
{
    
    
    return MV_CC_DrawLines(m_hDevHandle, pLinesInfo);
}

//读取相机中的图像
int CMvCamera::ReadBuffer(cv::Mat &image)
{
    
    
    unsigned int nRecvBufSize = 0;
    MVCC_INTVALUE_EX stParam;
    memset(&stParam,0,sizeof(MVCC_INTVALUE_EX));
    int nRet = GetIntValue("PayloadSize",&stParam);
    if(MV_OK != nRet)
    {
    
    
        return nRet;
    }
    nRecvBufSize = stParam.nCurValue;
    unsigned char* pDate;
    pDate = (unsigned char *)malloc(nRecvBufSize);
    MV_FRAME_OUT_INFO_EX stImageInfo;
    memset(&stImageInfo,0,sizeof(MV_FRAME_OUT_INFO));
    nRet = MV_CC_GetOneFrameTimeout(m_hDevHandle,pDate,nRecvBufSize,&stImageInfo,500);
    if(MV_OK != nRet)
    {
    
    
        return nRet;
    }
    cv::Mat getImage = cv::Mat(stImageInfo.nHeight,stImageInfo.nWidth,CV_8UC1,pDate);
    getImage.copyTo(image);
    getImage.release();
    free(pDate);
    return MV_OK;
}

//读取保存相机中的图像
int CMvCamera::SaveBuffer(QByteArray imageName)
{
    
    
    unsigned int nRecvBufSize = 0;
    MVCC_INTVALUE_EX stParam;
    memset(&stParam,0,sizeof(MVCC_INTVALUE_EX));
    int nRet = GetIntValue("PayloadSize",&stParam);   //获取Integer属性值
    if(MV_OK != nRet)
    {
    
    
        return nRet;
    }
    nRecvBufSize = stParam.nCurValue;
    unsigned char* pDate;
    pDate = (unsigned char *)malloc(nRecvBufSize);
    MV_FRAME_OUT_INFO_EX stImageInfo;
    memset(&stImageInfo,0,sizeof(MV_FRAME_OUT_INFO));
    nRet = MV_CC_GetOneFrameTimeout(m_hDevHandle,pDate,nRecvBufSize,&stImageInfo,500);   //采用超时机制获取一帧图片
    if(MV_OK != nRet)
    {
    
    
        return nRet;
    }
    MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam;
    memset(&stSaveFileParam,0,sizeof(MV_SAVE_IMG_TO_FILE_PARAM));
    stSaveFileParam.enImageType = MV_Image_Bmp;
    stSaveFileParam.enPixelType = stImageInfo.enPixelType;
    stSaveFileParam.nWidth      = stImageInfo.nWidth;
    stSaveFileParam.nHeight     = stImageInfo.nHeight;
    stSaveFileParam.nDataLen    = stImageInfo.nFrameLen;
    stSaveFileParam.pData       = pDate;
    stSaveFileParam.iMethodValue = 0;
    sprintf_s(stSaveFileParam.pImagePath,256,imageName.data());   //文件名
    //qDebug()<<"pImagePath:"<<stSaveFileParam.pImagePath;
    nRet = SaveImageToFile(&stSaveFileParam);   //保存图片为文件
    if(MV_OK != nRet)
    {
    
    
        return nRet;
    }
    free(pDate);
    return MV_OK;
}

3.camerathread.h (image acquisition thread)

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include <QImage>
#include <QDebug>
#include "globalvar.h"

class CameraThread : public QThread
{
    
    
    Q_OBJECT

public:
    explicit CameraThread(QObject *parent = nullptr);
     ~CameraThread();

    void initThread();
    void setCameraPtr(CMvCamera *camera);
    void setCameraIndex(int index);
    void setSwitchFlag(bool switchFlag);

    void run();

signals:
    void signal_messImage(QImage myImage,int index);

private:
    bool startFlag;
    int m_cameraIndex;

    CMvCamera *cameraPtr = NULL;
};

#endif // MYTHREAD_H

4.camerathread.cpp

#include "camerathread.h"

CameraThread::CameraThread(QObject *parent)
    : QThread{
    
    parent}
{
    
    
    this->initThread();
}

CameraThread::~CameraThread()
{
    
    
    if(cameraPtr == NULL)
    {
    
    
        delete cameraPtr;
    }
}

void CameraThread::initThread()
{
    
    
    startFlag = false;
    m_cameraIndex = 0;
}

void CameraThread::setCameraPtr(CMvCamera *camera)
{
    
    
    cameraPtr = camera;
}

void CameraThread::setCameraIndex(int index)
{
    
    
    m_cameraIndex = index;
}

void CameraThread::setSwitchFlag(bool switchFlag)
{
    
    
    startFlag = switchFlag;
}

void CameraThread::run()
{
    
    
    if(cameraPtr == NULL)
    {
    
    
        return;
    }
    while(startFlag)
    {
    
    
        //将图片添加到处理线程
        cv::Mat imagePtr;
        cameraPtr->CommandExecute("TriggerSoftware");
        cameraPtr->ReadBuffer(imagePtr);

        //qDebug()<<"m_cameraIndex:"<<m_cameraIndex;
        //添加到容器
        if(m_cameraIndex == 0)
        {
    
    
            m_imageVector_1.push_back(imagePtr);
        }
        else if(m_cameraIndex == 1)
        {
    
    
            m_imageVector_2.push_back(imagePtr);
        }
        msleep(30);   //适当缓冲,减少cpu运行率
    }
}

5.mainwindow.h (main interface)

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDate>
#include <QDir>
#include <QMessageBox>
#include "HikSdk/cmvcamera.h"
#include "HikSdk/camerathread.h"
#include "HikSdk/processingthread.h"

QT_BEGIN_NAMESPACE
namespace Ui {
    
     class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    
    
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void initWidget();
    void saveImage(QString format,int index);

private slots:
    void slot_displayPixmap(QPixmap newPixmap,int index);

    void on_pb_start_clicked();
    void on_pb_stop_clicked();
    void on_pb_saveOne_clicked();
    void on_pb_saveTwo_clicked();

private:
    Ui::MainWindow *ui;

    int m_deviceNum;
    bool m_bOpenDevice;
    QString m_savePath;
    QString m_cameraOnePath;
    QString m_cameraTwoPath;

    MV_CC_DEVICE_INFO_LIST m_stDevList;        //设备信息列表结构体变量,用来存储设备列表
    MV_CC_DEVICE_INFO *m_DeviceInfo[2];
    CMvCamera *m_pcMyCamera[2];                //相机指针对象
    CameraThread *m_cameraThread[2];           //相机线程对象
    ProcessingThread *m_processingThread[2];   //处理线程对象

};
#endif // MAINWINDOW_H

6.mainwindow.cpp

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

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

MainWindow::~MainWindow()
{
    
    
    for(int i=0;i<2;i++)
    {
    
    
        if(m_pcMyCamera[i])
        {
    
    
            m_pcMyCamera[i]->Close();
            delete m_pcMyCamera[i];
            m_pcMyCamera[i] = NULL;
        }

        //关闭线程,防止关闭窗口出错
        if(m_cameraThread[i]->isRunning())
        {
    
    
            m_cameraThread[i]->setSwitchFlag(false);
        }
        if(m_processingThread[i]->isRunning())
        {
    
    
            m_processingThread[i]->setSwitchFlag(false);
        }
    }
}

void MainWindow::initWidget()
{
    
    
    //初始化变量
    m_deviceNum = 0;
    m_bOpenDevice = false;
    m_savePath = "";
    m_cameraOnePath = "";
    m_cameraTwoPath = "";

    //生成保存图片的文件夹
    QString curDate = QDate::currentDate().toString("yyyy-MM-dd");
    m_savePath = QDir::currentPath() + "/SaveImages/" + curDate + "/";
    QDir saveDir(m_savePath);
    if(!saveDir.exists())
    {
    
    
        if(!saveDir.mkpath(m_savePath))
        {
    
    
            qDebug()<<"创建文件夹失败!";
            //return;
        }
    }

    for(int i=0;i<2;i++)
    {
    
    
        //相机对象
        m_pcMyCamera[i] = new CMvCamera;

        //相机线程对象实例化
        m_cameraThread[i] = new CameraThread();

        //处理线程对象实例化
        m_processingThread[i] = new ProcessingThread();
        connect(m_processingThread[i],SIGNAL(signal_newPixmap(QPixmap,int)),this,SLOT(slot_displayPixmap(QPixmap,int)),Qt::BlockingQueuedConnection);
    }

    //初始化相机
    memset(&m_stDevList,0,sizeof(MV_CC_DEVICE_INFO_LIST));
    //枚举子网内所有设备
    int nRet = CMvCamera::EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE,&m_stDevList);
    if(MV_OK != nRet)
    {
    
    
        return;
    }
    m_deviceNum = m_stDevList.nDeviceNum;
    if(m_deviceNum != 2)
    {
    
    
        QMessageBox::warning(this,"警告","请检查相机是否正常连接!");
        return;
    }
    for(int i=0;i<m_deviceNum;i++)
    {
    
    
        MV_CC_DEVICE_INFO *pDeviceInfo = m_stDevList.pDeviceInfo[i];
        int nRet = m_pcMyCamera[i]->Open(pDeviceInfo);   //打开相机
        if(MV_OK != nRet)
        {
    
    
            delete m_pcMyCamera[i];
            m_pcMyCamera[i] = NULL;
            QMessageBox::warning(this,"警告","打开设备失败!");
            return;
        }
        m_pcMyCamera[i]->SetEnumValue("TriggerMode",1);        //设置为触发模式
        m_pcMyCamera[i]->SetEnumValue("TriggerSource",7);      //设置触发源为软触发
        m_pcMyCamera[i]->SetFloatValue("ExposureTime",5000);   //设置曝光时间
        m_cameraThread[i]->setCameraPtr(m_pcMyCamera[i]);
        m_cameraThread[i]->setCameraIndex(i);
        m_processingThread[i]->setThreadId(i);
    }
}

void MainWindow::saveImage(QString format,int index)
{
    
    
    cv::Mat image;
    //当前时间
    QString curTime = QTime::currentTime().toString("hhmmss");
    if(index == 1)
    {
    
    
        //format: .bmp .tif .png .jpg
        QString saveNameOne = m_cameraOnePath + "Grab_" + curTime + "." + format;
        m_pcMyCamera[0]->CommandExecute("TriggerSoftware");
        int readFlag = m_pcMyCamera[0]->SaveBuffer(saveNameOne.toUtf8());
        if(MV_OK == readFlag)
        {
    
    
            qDebug()<<"相机一图像保存成功!";
        }
        else
        {
    
    
            qDebug()<<"相机一图像保存失败!";
        }
    }
    else if(index == 2)
    {
    
    
        QString saveNameTwo = m_cameraTwoPath + "Grab_" + curTime + "." + format;
        m_pcMyCamera[1]->CommandExecute("TriggerSoftware");
        int readFlag = m_pcMyCamera[1]->SaveBuffer(saveNameTwo.toUtf8());
        if(MV_OK == readFlag)
        {
    
    
            qDebug()<<"相机二图像保存成功!";
        }
        else
        {
    
    
            qDebug()<<"相机二图像保存失败!";
        }
    }
}

void MainWindow::slot_displayPixmap(QPixmap newPixmap,int index)
{
    
    
    if(newPixmap.isNull())
    {
    
    
        qDebug()<<"newPixmap is a null image!"<<"   index:"<<index;
        return;
    }
    if(index == 0)
    {
    
    
        //ui->lb_imageOne->setPixmap(newPixmap);
        ui->gl_imageOne->setPixmap(newPixmap,"图像一");   //使用OpenGLWidget显示
    }
    if(index == 1)
    {
    
    
        //ui->lb_imageTwo->setPixmap(newPixmap);
        ui->gl_imageTwo->setPixmap(newPixmap,"图像二");
    }
}

void MainWindow::on_pb_start_clicked()
{
    
    
    if(!m_bOpenDevice)
    {
    
    
        m_bOpenDevice = true;
    }
    for(int i=0;i<2;i++)
    {
    
    
        if(m_pcMyCamera[i]->IsDeviceConnected())
        {
    
    
            m_pcMyCamera[i]->StartGrabbing();   //开启相机采集
            if(!m_cameraThread[i]->isRunning())
            {
    
    
                m_cameraThread[i]->setSwitchFlag(true);
                m_cameraThread[i]->start();  
            }
            if(!m_processingThread[i]->isRunning())
            {
    
    
                m_processingThread[i]->setSwitchFlag(true);
                m_processingThread[i]->start();
            }
        }
    }
}

void MainWindow::on_pb_stop_clicked()
{
    
    
    if(m_bOpenDevice)
    {
    
    
        m_bOpenDevice = false;
    }
    for(int i=0;i<2;i++)
    {
    
    
        m_pcMyCamera[i]->StopGrabbing();   //关闭相机采集
        if(m_cameraThread[i]->isRunning())
        {
    
    
            m_cameraThread[i]->setSwitchFlag(false);
        }
        if(m_processingThread[i]->isRunning())
        {
    
    
            m_processingThread[i]->setSwitchFlag(false);
        }
    }
}

void MainWindow::on_pb_saveOne_clicked()
{
    
    
    if(!m_bOpenDevice)
    {
    
    
        QMessageBox::warning(this,"警告","请开启相机采集!");
    }
    m_cameraOnePath = m_savePath + "Camera1/";
    QDir dstDir(m_cameraOnePath);
    if(!dstDir.exists())
    {
    
    
        if(!dstDir.mkpath(m_cameraOnePath))
        {
    
    
            qDebug()<<"创建Camera1文件夹失败!";
            //return;
        }
    }
    saveImage("bmp",1);
}

void MainWindow::on_pb_saveTwo_clicked()
{
    
    
    if(!m_bOpenDevice)
    {
    
    
        QMessageBox::warning(this,"警告","请开启相机采集!");
    }
    m_cameraTwoPath = m_savePath + "Camera2/";
    QDir dstDir(m_cameraTwoPath);
    if(!dstDir.exists())
    {
    
    
        if(!dstDir.mkpath(m_cameraTwoPath))
        {
    
    
            qDebug()<<"创建Camera2文件夹失败!";
            //return;
        }
    }
    saveImage("bmp",2);
}

6.mainwindow.ui
Please add a picture description

6. Download link

My example Baidu network disk link: https://pan.baidu.com/s/1MxIqbIzoZSbM00n33Tf79w
Extraction code: xxcj

Resource download on CSDN: https://download.csdn.net/download/XCJandLL/87546525


Summarize

This article uses two industrial cameras for image acquisition, which also correspond to two image acquisition threads and two algorithm processing threads. OpenGLWidget is used to display images on the main interface. These are the optimizations of the previous article examples in this series. In addition, under the while loop of the run function in these two thread classes, you can see that msleep is used to sleep the thread. Here I use it as a buffer, which can reduce the CPU load without affecting the visual viewing of the image. Utilization, it should be noted that the value set in the processing thread should not be greater than that of the image acquisition thread, otherwise an exception will occur. (There are hints in the text, which are related to the logic of image processing, otherwise you can try it >_<, hahaha~)


hello:
Learn together and make progress together. If you still have related questions, you can leave a message in the comment area for discussion.

Reference blog: qt multithreading reads Haikang camera images, processes and displays them

Guess you like

Origin blog.csdn.net/XCJandLL/article/details/129287191