系列文章目录
提示:这里是该系列文章的所有文章的目录
第一章:(一)Qt下实现多个海康工业相机内触发采集回调取流显示
第二章:(二)Qt下多线程实现多个海康工业相机内触发采集回调取流显示
文章目录
- 系列文章目录
- 前言
- 一、创建线程类
- 二、关联线程对象
- 三、示例完整代码
- 四、下载链接
- 总结
前言
在本系列的上一篇文章中,我们讲述了实现海康工业相机的连接,采用内触发采集模式,并使用回调取流的方式来获取图像并实现界面上两个相机的显示,但是发现在多个相机下要写多个回调函数是不太合适的,所以在这里增加了一个线程类,使用多线程的方式对前文的demo进行优化,这里对该示例进行讲解,以便大家学习,如有错误之处,欢迎大家批评指正。
项目效果
提示:以下是本篇文章正文内容,下面案例可供参考
一、创建线程类
在HikSdk文件夹下添加GrabImgThread类,在类的构造函数中赋值相应的相机号,以区分多个相机,在线程类中设置相机指针和相机回调函数,以及添加一个signal_imageReady信号
//回调函数
void __stdcall GrabImgThread::ImageCallback(unsigned char * pData,MV_FRAME_OUT_INFO_EX* pFrameInfo,void* pUser)
{
LOGDEBUG<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz ")<<"回调函数执行了";
GrabImgThread* pThread = static_cast<GrabImgThread *>(pUser);
//创建QImage对象
QImage showImage = QImage(pData,pFrameInfo->nWidth,pFrameInfo->nHeight,QImage::Format_RGB888);
//发送信号,通知主界面更新图像
emit pThread->signal_imageReady(showImage,pThread->m_cameraId);
}
修改后的项目结构如下:
二、关联线程对象
图像显示流程与之前还是一样的,只不过在这里将相机对象与线程对象进行了关联,在初始化的按钮槽函数中注册了线程对象中的回调函数,开启采集后线程对象通过信号与槽的方式将回调函数中的图像数据发送到主界面进行显示
for(int i=0;i<2;i++)
{
//相机对象
m_myCamera[i] = new CMvCamera;
//线程对象
m_grabThread[i] = new GrabImgThread(i);
connect(m_grabThread[i],SIGNAL(signal_imageReady(QImage,int)),this,SLOT(slot_imageReady(QImage,int)),Qt::BlockingQueuedConnection);
···
//设置线程对象中的回调函数
m_grabThread[i]->setCameraPtr(m_myCamera[i]);
}
三、示例完整代码
1.grabimgthread.h
#ifndef GRABIMGTHREAD_H
#define GRABIMGTHREAD_H
#include <QThread>
#include <QImage>
#include <QDateTime>
#include "HikSdk/cmvcamera.h"
class GrabImgThread : public QThread
{
Q_OBJECT
public:
explicit GrabImgThread(int cameraId);
~GrabImgThread();
void setCameraPtr(CMvCamera *camera);
void run();
signals:
void signal_imageReady(const QImage &image,int cameraId);
private:
static void __stdcall ImageCallback(unsigned char * pData,MV_FRAME_OUT_INFO_EX* pFrameInfo,void* pUser);
private:
int m_cameraId;
CMvCamera *m_cameraPtr;
};
#endif // GRABIMGTHREAD_H
2.grabimgthread.cpp
#include "grabimgthread.h"
GrabImgThread::GrabImgThread(int cameraId)
: m_cameraId(cameraId)
{
}
GrabImgThread::~GrabImgThread()
{
}
//设置相机指针
void GrabImgThread::setCameraPtr(CMvCamera *camera)
{
m_cameraPtr = camera;
//注册回调函数
//nRet = m_myCamera[i]->RegisterImageCallBack(ImageCallback,this); //单色相机
int nRet = m_cameraPtr->RegisterImageCallBackRGB(ImageCallback,this); //彩色相机
if(MV_OK != nRet)
{
LOGDEBUG<<"m_cameraId:"<<m_cameraId<<"注册回调函数失败!";
return;
}
}
//线程运行
void GrabImgThread::run()
{
}
//回调函数
void __stdcall GrabImgThread::ImageCallback(unsigned char * pData,MV_FRAME_OUT_INFO_EX* pFrameInfo,void* pUser)
{
LOGDEBUG<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz ")<<"回调函数执行了";
GrabImgThread* pThread = static_cast<GrabImgThread *>(pUser);
//创建QImage对象
QImage showImage = QImage(pData,pFrameInfo->nWidth,pFrameInfo->nHeight,QImage::Format_RGB888);
//发送信号,通知主界面更新图像
emit pThread->signal_imageReady(showImage,pThread->m_cameraId);
}
3.mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDateTime>
#include "HikSdk/grabimgthread.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 showImage(QImage showImage,int index);
private slots:
void slot_imageReady(const QImage &image,int cameraId);
void on_pb_init_clicked();
void on_pb_start_clicked();
void on_pb_stop_clicked();
private:
Ui::MainWindow *ui;
CMvCamera *m_myCamera[2]; //相机对象
GrabImgThread *m_grabThread[2]; //捕获图像线程
MV_CC_DEVICE_INFO *m_deviceInfo[2]; //设备信息
MV_CC_DEVICE_INFO_LIST m_stDevList; //设备信息列表
};
#endif // MAINWINDOW_H
4.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()
{
delete ui;
for(int i=0;i<2;i++)
{
if(m_myCamera[i])
{
m_myCamera[i]->Close();
delete m_myCamera[i];
m_myCamera[i] = NULL;
}
}
}
void MainWindow::initWidget()
{
for(int i=0;i<2;i++)
{
//相机对象
m_myCamera[i] = new CMvCamera;
//线程对象
m_grabThread[i] = new GrabImgThread(i);
connect(m_grabThread[i],SIGNAL(signal_imageReady(QImage,int)),this,SLOT(slot_imageReady(QImage,int)),Qt::BlockingQueuedConnection);
}
}
//显示图像
void MainWindow::slot_imageReady(const QImage &image,int cameraId)
{
QPixmap showPixmap = QPixmap::fromImage(image).scaled(QSize(250,200),Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
if(cameraId == 0)
{
ui->lb_time_1->setText("相机1:" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
ui->lb_image_1->setPixmap(showPixmap);
}
else
{
ui->lb_time_2->setText("相机2:" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
ui->lb_image_2->setPixmap(showPixmap);
}
}
//初始化
void MainWindow::on_pb_init_clicked()
{
//枚举子网内所有设备
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)
{
LOGDEBUG<<"枚举相机设备失败!";
return;
}
int deviceNum = m_stDevList.nDeviceNum;
LOGDEBUG<<"相机连接数量:"<<deviceNum;
if(deviceNum > 2)
{
//我这里只定义了两个相机对象,所以限制为2,实际情况需要根据相机数量定义头文件中的对象数
deviceNum = 2;
}
for(int i=0;i<deviceNum;i++)
{
MV_CC_DEVICE_INFO *pDeviceInfo = m_stDevList.pDeviceInfo[i];
QString strSerialNumber = "";
if(pDeviceInfo->nTLayerType == MV_GIGE_DEVICE)
{
strSerialNumber = (char*)pDeviceInfo->SpecialInfo.stGigEInfo.chSerialNumber;
}
else if(pDeviceInfo->nTLayerType == MV_USB_DEVICE)
{
strSerialNumber = (char*)pDeviceInfo->SpecialInfo.stUsb3VInfo.chSerialNumber;
}
else
{
LOGDEBUG<<"警告,未知设备枚举!";
return;
}
LOGDEBUG<<"i:"<<i<<" strSerialNumber:"<<strSerialNumber;
if(i == 0)
{
ui->lb_name_1->setText(strSerialNumber);
}
else if(i == 1)
{
ui->lb_name_2->setText(strSerialNumber);
}
//根据相机序列号指定相机对象,就可以指定某个窗口进行显示
//if(strSerialNumber == "DA0333897")
//{
// m_deviceInfo[0] = pDeviceInfo;
//}
//else if(strSerialNumber == "DA0424312")
//{
// m_deviceInfo[1] = pDeviceInfo;
//}
//不指定
m_deviceInfo[i] = pDeviceInfo;
//打开相机
int nRet = m_myCamera[i]->Open(m_deviceInfo[i]);
if(MV_OK != nRet)
{
LOGDEBUG<<"i:"<<i<<"打开相机失败!";
return;
}
else
{
//关闭触发模式
nRet = m_myCamera[i]->SetEnumValue("TriggerMode",0);
if(MV_OK != nRet)
{
LOGDEBUG<<"i:"<<i<<"关闭触发模式失败!";
return;
}
//设置线程对象中的回调函数
m_grabThread[i]->setCameraPtr(m_myCamera[i]);
}
}
}
//开始取图
void MainWindow::on_pb_start_clicked()
{
for(int i=0;i<2;i++)
{
int nRet = m_myCamera[i]->StartGrabbing();
if (MV_OK != nRet)
{
LOGDEBUG<<"i:"<<i<<"开始取图失败!";
return;
}
}
}
//停止取图
void MainWindow::on_pb_stop_clicked()
{
for(int i=0;i<2;i++)
{
int nRet = m_myCamera[i]->StopGrabbing();
if (MV_OK != nRet)
{
LOGDEBUG<<"i:"<<i<<"停止取图失败!";
return;
}
}
}
5.mainwindow.ui
四、下载链接
我的示例百度网盘链接:https://pan.baidu.com/s/1SpWcRc0WnnaBAjS1lfoyfA
提取码:xxcj
总结
通过多线程的方式,就不用写多个回调函数了,也是实现了多个海康工业相机内触发采集回调取流,这样做是比较简便的,同时也防止了整个项目代码过于冗余。文章线程类中注册回调函数中有提到单色和彩色两种相机,这个示例的是彩色相机的取图,如果是单色的,需要注意在回调函数中进行QImage类型转换的时候,要将其中的QImage::Format_RGB888修改为QImage::Format_Indexed8
常用的还可能需要转换为cv::Mat类型的图像,这里也简单介绍一下
//创建cv::Mat对象
cv::Mat showMat(pFrameInfo->nHeight,pFrameInfo->nWidth,CV_8UC1,pData); //单色
cv::Mat showMat(pFrameInfo->nHeight,pFrameInfo->nWidth,CV_8UC3,pData); //彩色
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。