(二)Qt下多线程实现多个海康工业相机内触发采集回调取流显示

news2024/11/26 19:29:17

系列文章目录

提示:这里是该系列文章的所有文章的目录
第一章:(一)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:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/760078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单例模式之常见模式详解

单例模式之常见模式详解 单例模式的定义单例模式的分类饿汉模式懒汉模式 单例模式的主要特点单例模式的应用场景总结 单例模式的定义 单例模式是一种设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。 在单例模式中&#xff0c;类…

2023需求最高的编程语言:Python、JavaScript和Java

科技招聘平台 CodinGame 和 CoderPad 最新发布了一份 2023 年科技行业招聘报告&#xff0c;基于对 14000 名专业人士的调查&#xff0c;对 2023 年科技行业招聘人员和求职者可能面临的情况进行了深入分析。其中一些关键亮点包括&#xff1a; 59% 的开发人员没有计算机科学方面的…

吴恩达机器学习笔记-week2

第2周 目录 第2周 四、多变量线性回归(Linear Regression with Multiple Variables) 4.1 多维特征4.2 多变量梯度下降4.3 梯度下降法实践1-特征缩放4.4 梯度下降法实践2-学习率4.5 特征和多项式回归4.6 正规方程4.7 正规方程及不可逆性(可选) 四、多变量线性回归(Linear Regres…

docker的安装以及常用命令详解

目录 一、docker简介 二、docker安装 三、常用命令 1、显示 Docker 版本信息 2、显示 Docker 系统信息&#xff0c;包括镜像和容器数 3、帮助 四、镜像管理 1、列出镜像 2、获取一个新的镜像 3、查找镜像 4、删除镜像 5、镜像导入与导出 五、容器生命周期 1、运行…

Bring Your Data!Self- supervised Evolution of Large Language Models

Bring Your Data&#xff01;Self- supervised Evolution of Large Language Models IntroductionMethod参考 Introduction 这篇论文提出了一种自监督的评估方式来衡量大型语言模型的能力和局限性。常规的基于数据集的评估方式存在一些缺点: 需要不断新建数据集。存在数据集和…

外包软件定制开发中时间和进度管理

引言 时间和进度管理是外包软件定制开发项目中至关重要的方面。有效地管理时间和进度可以确保项目按计划顺利进行&#xff0c;减少延误和风险&#xff0c;从而提高交付的可靠性和质量。然而&#xff0c;由于外包团队和客户位于不同的地理位置和时区&#xff0c;时间和进度管理…

[MySQL]MySQL索引

[MySQL]MySQL索引 文章目录 [MySQL]MySQL索引1. 索引的概念2. 认识磁盘磁盘的内部结构磁盘中的一个盘片结构定位扇区磁盘随机访问与连续访问 3. MySQL与磁盘交互的基本单位4. 建立共识5. 索引的理解创建测试表进行测试理解单个page理解多个page页目录其他的数据结构的问题聚簇索…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud角色与权限

目录 组织和项目 角色和权限 访问权限等级 文档推荐 Zilliz Cloud 提供访问控制。您可以灵活管理用户,为用户分配不同角色,授予用户不同的集群资源访问和操作权限。无权限的用户无法访问特定资源。 组织和项目 Zilliz Cloud 通过组织和项目实现访问控制和资源分离。 …

PostgreSQL数据库的部署与优化

文章目录 一.PostgreSQL数据库1.PostgreSQL数据库的概念1.1 PostgreSQL 的核心概念 2.PostgreSQL特点3.PostgreSQL的作用4.PostgreSQL的应用场景5.PostgreSQL、mysql、oracle的对比 二.Linux系统安装PostgresSQL&#xff08;Centos7&#xff09;1.更新yun源2.安装PostgreSQL2.1…

【消息队列设计总结】RabbitMQ、Kafka、RocketMQ对比之运行架构

目录 1 RabbitMQ 1.1 工作原理 1.2 名词解释 1.3 交换机的几种类型 2 Kafka 2.1 工作原理 2.2 基本概念 3 RocketMQ 3.1 工作原理 3.2 基本概念 4 RabbitMQ & Kafka & RocketMQ的差异 5 参考文档 1 RabbitMQ 1.1 工作原理 1.【消息生产】生产者&#xff0…

润本股份提交注册:拟募资约9亿元,赵贵钦、鲍松娟夫妇为实控人

撰稿|汤汤 来源|贝多财经 近日&#xff0c;润本生物技术股份有限公司&#xff08;下称“润本股份”&#xff09;在上海证券交易所递交招股书&#xff08;注册稿&#xff09;。据贝多财经了解&#xff0c;润本股份于2023年3月递交上市申请材料&#xff0c;准备在上海证券交易所…

MySQL-DDL-表的结构-查询修改删除

DDL&#xff08;表操作&#xff09; 查询 查询当前数据库所有表&#xff1a;show tables 查询表结构&#xff1a;desc 表名 查询建表语句&#xff1a;show create table 表名 修改&#xff08;主要还是通过图形化界面进行操作&#xff09; 添加字段&#xff1a;alter table …

王道考研数据结构第六章知识点总结

6.1.1图的基本概念 顶点集一定是非空集&#xff0c;边集可以是空集 非连通图&#xff1a;让任意n-1个顶点之间两两相连&#xff0c;另一个顶点与任意一个都不存在路径&#xff0c;则为非连通图的最大边数。 注意长三角地区铁路网不属于连通分量&#xff0c;因为它没有包含尽可能…

简单认识MySQL数据库索引

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、索引的概念1、简介2、作用3、索引的副作用&#xff1a;4、创建索引的原则依据5、索引的分类 二、索引的增删改查1.创建索引&#xff08;1&#xff09;创建普通索…

ios 通过xib自定义控件

通过xib自定义控件 xib和stroyboayd对比 共同点&#xff1a; 都是用来描述软件界面 都是用interface Builder工具来编辑 本质都是转换成代码去创建控件 不同点&#xff1a; xib是轻量级的&#xff0c;用来描述局部ui界面 创建模型文件 XMGCar 自定义控件 xib 图形设计 …

zookeeper和kafka与EFLFK集群

zookeeper和kafka 一&#xff1a;Zookeeper 概述1.Zookeeper 定义2.Zookeeper 工作机制3.Zookeeper 特点4.Zookeeper 数据结构5.Zookeeper 应用场景6.Zookeeper 选举机制6.1.第一次启动选举机制6.2.非第一次启动选举机制&#xff08;1&#xff09;当ZooKeeper 集群中的一台服务…

支持向量机(SVM)的超参数调整 C 和 Gamma 参数

作者:CSDN @ _养乐多_ 支持向量机(Support Vector Machine,SVM)是一种广泛应用的监督式机器学习算法。它主要用于分类任务,但也适用于回归任务。在本文中,我们将深入探讨支持向量机的两个重要参数:C和gamma。在阅读本文前,我假设您对该算法有基本的了解,并专注于这些…

【MySQL】MySQL在Centos7环境下安装

目录 一、卸载不要的环境 1.1、查看是否有安装mysql 1.2、关闭运行的程序 1.3、卸载安装 二、配置yum 源 2.1、下载yum 源 2.2 安装yum源 2.3 查看是否已经生效 三、安装mysql服务 四、启动服务 五、登录方法 方法一&#xff08;不行就下一个&#xff09; 方法二&#xff08;不…

前端必看的书籍

十本学习前端必看书籍 第一本&#xff0c;入门 《Head first HTML&CSS》 最好的入门书。看两遍就对HTML & CSS 有个大概印象了。 此时把w3cschool作为备查手册收藏起来 第二本《CSS权威指南(第三版)》 最权威的CSS书籍&#xff0c;除了阅读W3C的文档外的不二选择(就是翻…

HDFS体系结构及基本概念

1、hdfs体系结构及基本概念 1.1 namenode是什么,作用是什么 Namenode维护了很多得信息,那么这么多信息存放在哪里呢?在hadoop中有个core-site.xml的文件 路径是 [root@cdp3 hadoop]# pwd /home/hadoop/hadoop-2.2.0/etc/hadoop 1.2 datanode是什么,作用是什么 data…