(2) Qt multi-threading realizes real-time image acquisition of Hikvision industrial cameras

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 mainly describes the use of Qt multi-threading to realize the image collection of Haikang industrial cameras, and displays the image information collected by the two cameras on the interface at the same time. On the basis of the first article in this series of articles, the saved Here is an example of the function of the picture, so that everyone can learn. If there are any mistakes, you are 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. In order to make the project structure look more tidy, the pri sub-module is used here, and the
Please add a picture description
following content is added to MyCamera2.pro

#包含子模块
include (./HikSdk/HikSdk.pri)   #海康SDK

(This is a small record: If you convert Qt's MinGW project to MSVC project, you need to set the character)

#设置字符
QMAKE_CXXFLAGS += -execution-charset:utf-8
QMAKE_CXXFLAGS += -source-charset:utf-8
QMAKE_CXXFLAGS_WARN_ON += -wd4819

2. Initialize the camera

My example is that the camera is initialized in the constructor, and the opening of the camera is completed, which can avoid jamming when the acquisition starts (it takes time to open the camera, and it may not be realized when a small number of cameras are opened. If the number of cameras increases , there will be an obvious waiting time), and the two camera models used in this example are the same, so they cannot be distinguished by this, but the camera number is set during initialization, and the code for obtaining the camera model can be seen in the first Code in an article.

//初始化相机
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]->setImagePtr(m_myImage[i]);
    m_cameraThread[i]->setCameraIndex(i);
}

3. Image storage

In the first article, the image is saved using the QPixmap method in qt, but if you need to save the image in real time, using this method will cause the interface to freeze, so the method of saving the image is improved in this article , using the save function in the Hikvision sdk, and implementing it in a thread, see the following for the complete code:

//这里进行了保存图像名的组装,并readBuffer中将保存标志置为true
//format: .bmp .tif .png .jpg
QString saveNameOne = m_cameraOnePath + "Grab_" + curTime + "." + format;
m_pcMyCamera[0]->CommandExecute("TriggerSoftware");
int readFlag = m_pcMyCamera[0]->ReadBuffer(image,true,saveNameOne.toUtf8());

//读取相机中的图像
//将saveFlag置为true,并提供保存图像名,即可实现图像的保存
int CMvCamera::ReadBuffer(cv::Mat &image,bool saveFlag,QByteArray imageName)
{
    
    
    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;
    memset(&stImageInfo,0,sizeof(MV_FRAME_OUT_INFO));
    tempValue= MV_CC_GetOneFrameTimeout(m_hDevHandle, pDate, nRecvBufSize, &stImageInfo, 700);
    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;
    }

    cv::Mat getImage;
    if(isMono)
    {
    
    
        getImage = cv::Mat(stImageInfo.nHeight,stImageInfo.nWidth,CV_8UC1,pDate);
        if(saveFlag)
        {
    
    
            MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam;
            memset(&stSaveFileParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM));
            stSaveFileParam.enImageType = MV_Image_Bmp; // ch:需要保存的图像类型 | en:Image format to save
            stSaveFileParam.enPixelType = stImageInfo.enPixelType;  // ch:相机对应的像素格式 | en:Camera pixel type
            stSaveFileParam.nWidth      = stImageInfo.nWidth;         // ch:相机对应的宽 | en:Width
            stSaveFileParam.nHeight     = stImageInfo.nHeight;          // ch:相机对应的高 | en:Height
            stSaveFileParam.nDataLen    = stImageInfo.nFrameLen;
            stSaveFileParam.pData       = pDate;
            stSaveFileParam.iMethodValue = 0;

            sprintf_s(stSaveFileParam.pImagePath,256,imageName.data());
            //qDebug()<<"pImagePath:"<<stSaveFileParam.pImagePath;
            int nRet = SaveImageToFile(&stSaveFileParam);
            if(MV_OK != nRet)
            {
    
    
                //qDebug()<<"nRet:"<<nRet;
            }
        }
    }

    getImage.copyTo(image);
    getImage.release();
    free(pDate);
    free(m_pBufForSaveImage);
    return 0;
}

4. Example complete code

1.MyCamera2.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

#设置字符
QMAKE_CXXFLAGS += -execution-charset:utf-8
QMAKE_CXXFLAGS += -source-charset:utf-8
QMAKE_CXXFLAGS_WARN_ON += -wd4819

#包含子模块
include (./HikSdk/HikSdk.pri)   #海康SDK

SOURCES += \
    main.cpp \
    mainwindow.cpp \

HEADERS += \
    mainwindow.h \

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${
    
    TARGET}/bin
else: unix:!android: target.path = /opt/$${
    
    TARGET}/bin
!isEmpty(target.path): INSTALLS += target

2.HikSdk.at

HEADERS += \
    $$PWD/camerathread.h \
    $$PWD/cmvcamera.h

SOURCES += \
    $$PWD/camerathread.cpp \
    $$PWD/cmvcamera.cpp

msvc:
{
    
    
    #OpenCV
    LIBS += -L$$PWD/OpenCV/OpenCV_Msvc/lib/ -lopencv_world451d
    INCLUDEPATH += $$PWD/OpenCV/OpenCV_Msvc/Includes
    DEPENDPATH += $$PWD/OpenCV/OpenCV_Msvc/Includes

    #SDK
    LIBS += -L$$PWD/SDK/Lib/ -lMvCameraControl
    INCLUDEPATH += $$PWD/SDK/Includes
    DEPENDPATH += $$PWD/SDK/Includes
}

#mingw:
#{
    
    
#    #OpenCV
#    LIBS += -L $$PWD/OpenCV/OpenCV_MinGw/Lib/libopencv_*.a
#    INCLUDEPATH += $$PWD/OpenCV/OpenCV_MinGw/Includes
#
#    #SDK
#    LIBS += -L$$PWD/SDK/Lib/MvCameraControl.lib
#    INCLUDEPATH += $$PWD/SDK/Includes
#    DEPENDPATH += $$PWD/SDK/Includes
#}

3. cmvcamera.h (copied directly from the SDK provided by Haikang, modified here)

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

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

private:

    void *m_hDevHandle;

    //用于保存图像的缓存
    unsigned int m_nBufSizeForSaveImage;

};

#endif//_MV_CAMERA_H_

4.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,bool saveFlag,QByteArray imageName)
{
    
    
    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;
    memset(&stImageInfo,0,sizeof(MV_FRAME_OUT_INFO));
    tempValue= MV_CC_GetOneFrameTimeout(m_hDevHandle, pDate, nRecvBufSize, &stImageInfo, 700);
    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;
    }

    cv::Mat getImage;
    if(isMono)
    {
    
    
        getImage = cv::Mat(stImageInfo.nHeight,stImageInfo.nWidth,CV_8UC1,pDate);
        if(saveFlag)
        {
    
    
            MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam;
            memset(&stSaveFileParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM));
            stSaveFileParam.enImageType = MV_Image_Bmp; // ch:需要保存的图像类型 | en:Image format to save
            stSaveFileParam.enPixelType = stImageInfo.enPixelType;  // ch:相机对应的像素格式 | en:Camera pixel type
            stSaveFileParam.nWidth      = stImageInfo.nWidth;         // ch:相机对应的宽 | en:Width
            stSaveFileParam.nHeight     = stImageInfo.nHeight;          // ch:相机对应的高 | en:Height
            stSaveFileParam.nDataLen    = stImageInfo.nFrameLen;
            stSaveFileParam.pData       = pDate;
            stSaveFileParam.iMethodValue = 0;

            sprintf_s(stSaveFileParam.pImagePath,256,imageName.data());
            //qDebug()<<"pImagePath:"<<stSaveFileParam.pImagePath;
            int nRet = SaveImageToFile(&stSaveFileParam);
            if(MV_OK != nRet)
            {
    
    
                //qDebug()<<"nRet:"<<nRet;
            }
        }
    }

    getImage.copyTo(image);
    getImage.release();
    free(pDate);
    free(m_pBufForSaveImage);
    return 0;
}

5.camerathread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

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

class CameraThread : public QThread
{
    
    
    Q_OBJECT

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

    void initThread();
    void setCameraPtr(CMvCamera *camera);
    void setImagePtr(cv::Mat *image);
    void setCameraIndex(int index);
    void setSwitchFlag(bool switchFlag);

    void run();

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

private:
    bool startFlag;
    int m_cameraIndex = 0;

    CMvCamera *cameraPtr = NULL;
    cv::Mat *imagePtr = NULL;
    QImage *myImage = NULL;

};

#endif // MYTHREAD_H

6.camerathread.cpp

#include "camerathread.h"

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

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

void CameraThread::initThread()
{
    
    
    startFlag = false;
    myImage = new QImage();
}

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

void CameraThread::setImagePtr(cv::Mat *image)
{
    
    
    imagePtr = image;
}

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

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

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

    while(startFlag)
    {
    
    
        cameraPtr->CommandExecute("TriggerSoftware");
        cameraPtr->ReadBuffer(*imagePtr,false,"");

        //先处理好再发送
        if(imagePtr->channels()>1)
        {
    
    
            *myImage = QImage((const unsigned char*)(imagePtr->data),imagePtr->cols,imagePtr->rows,QImage::Format_RGB888);
        }
        else
        {
    
    
            *myImage = QImage((const unsigned char*)(imagePtr->data),imagePtr->cols,imagePtr->rows,QImage::Format_Indexed8);
        }
        emit signal_messImage(*myImage,m_cameraIndex);
    }
}

7.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDate>
#include <QDir>
#include <QMessageBox>
#include "HikSdk/cmvcamera.h"
#include "HikSdk/camerathread.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_displayImage(QImage myImage,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];           //相机指针对象
    cv::Mat *m_myImage[2];                //用于保存相机图像的图像指针对象
    CameraThread *m_cameraThread[2];      //相机线程对象

};
#endif // MAINWINDOW_H

8.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++)
    {
    
    
        delete m_myImage[i];
        if(m_pcMyCamera[i])
        {
    
    
            m_pcMyCamera[i]->Close();
            delete m_pcMyCamera[i];
            m_pcMyCamera[i] = NULL;
        }
    }
}

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_myImage[i] = new cv::Mat();

        //线程对象实例化
        m_cameraThread[i] = new CameraThread();
        connect(m_cameraThread[i],SIGNAL(signal_messImage(QImage,int)),this,SLOT(slot_displayImage(QImage,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]->setImagePtr(m_myImage[i]);
        m_cameraThread[i]->setCameraIndex(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]->ReadBuffer(image,true,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]->ReadBuffer(image,true,saveNameTwo.toUtf8());
        if(MV_OK == readFlag)
        {
    
    
            qDebug()<<"相机二图像保存成功!";
        }
        else
        {
    
    
            qDebug()<<"相机二图像保存失败!";
        }
    }
}

void MainWindow::slot_displayImage(QImage myImage,int index)
{
    
    
    if(myImage.isNull())
    {
    
    
        //qDebug()<<"Image is a null image!";
        return;
    }
    //显示图像
    QPixmap myPixmap = QPixmap::fromImage(myImage).scaled(QSize(400,400),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
    if(index == 0)
    {
    
    
        ui->lb_imageOne->setPixmap(myPixmap);
    }
    if(index == 1)
    {
    
    
        ui->lb_imageTwo->setPixmap(myPixmap);
    }
}

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

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

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

9.mainwindow.ui
Please add a picture description

5. Download link

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


Summarize

This article uses the run function of rewriting QThread to realize multi-threading, and controls the start and stop of image acquisition by setting flag bits, which is also a relatively simple way. In the example, the image acquisition of two cameras is realized. There are no problems such as freezing when using this method, but in the case of more cameras (I have connected 12 cameras at the same time), there will be interface freezing. My solution The method is to add a 10ms delay in the while loop in the thread run function (QThread::msleep(10);), which can be regarded as a temporary solution to the problem, but if there is a better way, I hope to get everyone's feedback answer~


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

Guess you like

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