シリーズ記事の目次
提示:这里是该系列文章的所有文章的目录
第 1 章: (1) Qt での複数の Hikvision 産業用カメラでのトリガー取得コールバックとストリーム表示の実装
第 2 章: (2) 複数の Hikvision 産業用カメラでのトリガー取得コールバックとストリーム表示を実装するための Qt でのマルチスレッド
序文
このシリーズの前回の記事では、内部トリガー取得モードを使用し、コールバック ストリーム取得を使用して画像を取得し、インターフェイス上に 2 台のカメラを表示する Hikvision 産業用カメラの接続について説明しましたが、これは適切ではないことがわかりました。複数のカメラに対して複数のコールバック関数を作成するため、マルチスレッドを使用して前のデモを最適化するために、スレッド クラスがここに追加されます。誰もが学習できるように、例はここで説明されています。エラーがある場合は、大歓迎です。それらを批判し、修正します。
事業効果
提示:以下是本篇文章正文内容,下面案例可供参考
1. スレッドクラスを作成する
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);
}
変更されたプロジェクト構造は次のとおりです。
2. 関連するスレッドオブジェクト
画像表示処理は以前と同じですが、ここではカメラオブジェクトがスレッドオブジェクトに関連付けられていること、スレッドオブジェクト内のコールバック関数が初期化されたボタンスロット関数に登録されていること、スレッドオブジェクトがシグナルとスロットを渡すことを除いて、取得が開始され、コールバック関数内の画像データをメインインターフェイスに送信して表示します
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]);
}
3. 完全なコードのサンプル
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.メインウィンドウ.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.メインウィンドウ.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.メインウィンドウ.ui
4. ダウンロードリンク
私の Baidu ネットワーク ディスク リンクの例: https://pan.baidu.com/s/1SpWcRc0WnnaBAjS1lfoyfA
抽出コード: xxcj
要約する
マルチスレッドにより、複数のコールバック関数を記述する必要がなく、複数の Hikvision 産業用カメラが取得コールバックをトリガーしてストリームを取得できます。これは比較的単純であり、プロジェクト コード全体が冗長になりすぎることも防ぎます。記事のスレッドクラスに登録されているコールバック関数には、モノクロカメラとカラーカメラの2種類のカメラが記載されていますが、この例はカラーカメラから写真を撮る場合です。モノクロの場合、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); //カラー
こんにちは:
一緒に学び、一緒に進歩しましょう。関連する質問がまだある場合は、ディスカッションのためにコメント領域にメッセージを残すことができます。