提示:这里是该系列文章的所有文章的目录
第一章: (一)Qt+OpenCV调用海康工业相机SDK示例开发
第二章: (二)Qt多线程实现海康工业相机图像实时采集
第三章: (三)Qt多线程实现海康工业相机图像采集+算法检测+OpenGLWidget实时显示
本文对上一篇文章的示例进行了部分代码及功能的优化,在相机采集图像后额外添加了对应图像的算法处理,并在主界面上使用OpenGLWidget来显示图像内容,这里将相关内容展示,以便大家学习,如有错误之处,欢迎大家批评指正。
提示:以下是本篇文章正文内容,下面案例可供参考
下面是我的示例的源代码文件目录,其中相较于上篇文章在项目主文件夹下添加了MyGLWidget图像显示类,HikSdk子文件夹下添加了GlobalVar全局变量类、ProcessingThread图像处理线程类。
根据参考博客的内容,做出了下列流程图来表示相机图像采集到显示的过程:
这里是自定义的MyGLWidget类的实现代码,主要对其中的paintEvent进行了重写:
1.myglwidget.h
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include
#include
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();
}
在globalvar.h和globalvar.cpp中对图像保存容器进行了声明和定义,这里使用到了extern关键字:
1.globalvar.h
#ifndef GLOBALVAR_H
#define GLOBALVAR_H
#include
#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;
新建了ProcessingThread算法处理线程,图像处理在此线程中进行,并在处理完毕后由此线程发送信号至主界面上进行显示,这里通过setThreadId函数来将其与图像采集线程进行对应:
1.processingthread.h
#ifndef PROCESSINGTHREAD_H
#define PROCESSINGTHREAD_H
#include
#include
#include
#include
#include
#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:"<
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:"<
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:"<
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);
}
以上的新增类是通过点击Qt项目树上的Add New…来增加的,会在.pro和.pri中自动生成对应代码,这里将剩下的一些代码文件进行展示:
1.cmvcamera.h(对相机图像的读取和保存进行了优化)
/************************************************************************/
/* 以C++接口为基础,对常用函数进行二次封装,方便用户使用 */
/************************************************************************/
#ifndef _MV_CAMERA_H_
#define _MV_CAMERA_H_
#include
#include "MvCameraControl.h"
#include "opencv2/opencv.hpp"
#include
//会跟系统函数定义冲突
//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:"<
nRet = SaveImageToFile(&stSaveFileParam); //保存图片为文件
if(MV_OK != nRet)
{
return nRet;
}
free(pDate);
return MV_OK;
}
3.camerathread.h(图像采集线程)
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
#include
#include
#include
#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:"<
//添加到容器
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(主界面)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#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);
}
我的示例百度网盘链接:https://pan.baidu.com/s/1MxIqbIzoZSbM00n33Tf79w
提取码:xxcj
CSDN上资源下载:https://download.csdn.net/download/XCJandLL/87546525
本文使用了两个工业相机进行图像采集,同样对应着两个图像采集线程和两个算法处理线程,主界面上使用了OpenGLWidget来显示图像,这些都是对本系列前一篇文章示例进行的优化。另外在这两个线程类中的run函数while循环下,可以看到都使用了msleep进行线程的休眠,这里我把它当作缓冲,在不影响视觉查看图像的情况下,这样可以减轻cpu的利用率,需要注意的是处理线程中设定的数值不要大于图像采集线程的,不然会出现异常。(文中有提示,跟图像处理逻辑有关,不然你试试>_<
,哈哈哈~)
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考博客:qt多线程读取海康摄像机图像,处理并显示