大恒水星相机SDK(实时采集)基于QT与C++

本文详细介绍了如何在VS2019环境中使用QT框架配置大恒相机SDK,包括环境搭建、代码示例和整体流程,重点涉及设备初始化、图像采集和UI界面的控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

资料文档下载

1.环境搭建

本次的开发环境是基于vs2019使用QT的框架对大恒相机的SDK进行实时采集的操作。我们从零开始讲,根据上面的资料文档我们来添加一个新的项目,并且将C++的库文件添加进去。

首先,我们新建的时候使用QT的模板。
在这里插入图片描述

记住我们新建的路径,下面添加库文件的时候用得到
在这里插入图片描述

创建之后会让我们选择QT的模板,直接默认就好了,等待项目创建完成。待项目新建完成,根据资料文档根据以下路径/Samples/C++ SDK 找到两个文件夹,分别是inclib这两个文件都是大恒相机的库文件,我们导入就可以了。

在这里插入图片描述

库文件导入后需要在vs中进行设置,打开vs–右键解决方案–属性
在这里插入图片描述

在这里插入图片描述

到这里,其实我们已经基本搭建完了。不过下面还有一项我们可以选择一下,默认是窗口模式,我们选择控制台。因为窗口模式在我们调试的时候window的控制台不会显示从来,打印的信息就看不到了,所以可以设置一下。
在这里插入图片描述

前面的环境我们已经配置完成了,现在先来验证一下。在 .h的头文件里添加#include <GalaxyIncludes.h>,这是相机的SDK基本都在里面,具体是什么意思可以查看资料文件。添加完成后,运行程序如果正常运行且不报错,环境搭建就算是完成了,我们可以进行下一步了。

2.整体流程

初始化设备
枚举设备
打开设备
设置参数
开始采集
采集图像/图像处理
停止采集
关闭相机

这是整体的流程思路。点击vs里面的资源管理器,有一个UI的文件,打开并建立四个按钮控制设备的打开与关闭,采集和停止,再插入一个标签的控件用来显示图像。所以我们的画面一共有五个控件。在左侧的属性控制器——属性——objectName,可以修改控件对象名字。对于UI的画面,这里就已经完成了,后面就是代码的部分。

在这里插入图片描述

3.代码示例

这里是部分代码块的讲解,后是会有完整代码的。可能会有些不足,但大体思路是这样的,可以根据自己的理解去进行调整。

#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication1.h"
#include <qdebug.h>
#include <qlabel.h>
#include <qthread.h>
#include <qqueue.h>
#include <GalaxyIncludes.h>

#pragma execution_character_set("utf-8")

class QtWidgetsApplication1 : public QMainWindow
{
    Q_OBJECT

public:
    QtWidgetsApplication1(QWidget* parent = Q_NULLPTR);

    Ui::QtWidgetsApplication1Class ui;


    bool m_bisOpen = false;
    bool m_bisSnap = false;                                     //按钮的显示(是否能点击)

    QQueue <QPixmap> ImageQueue;                                            //建立图像缓存队列

    void UpdataUI();                                            //用于更新画面

private slots:
    void open_camera();                                         //打开设备
    void close_camera();                                        //关闭设备
    void start_acquisition();                                   //开始采集
    void close_acquisition();                                   //关闭采集
};

这是QT的一个类,新建项目的时候就会一起新建出来QtWidgetsApplication1是我的项目的名字。根据自己下项目名字的不同,它也不同。这里除了建立一些信号槽和变量外,需要建立一个缓存队列。因为更新画面的时候我们读取图片是在通道里面读取,如果不放入缓存队列里再读取,会造成内存错误,这一点是需要注意的。

class CSampleCaptureEventHandler : public ICaptureEventHandler
{
public:
    CSampleCaptureEventHandler(QtWidgetsApplication1* mainWindow)
        : pMainWindow(mainWindow)
    {
    }
    void DoOnImageCaptured(CImageDataPointer& objImageDataPointer, void* pUserParam);

private:
    QtWidgetsApplication1* pMainWindow;                 //用于更新画面的一个指针
};

这是相机SDK里面的一个回调采集类,我们获取图片的方法有两种。一种是采单帧,另一种是回调采集,这些文档里面是有讲的,具体可以去查看文档。回调采集只是获取的图片,我们还需要将获取到的图片更新到显示窗口,下面就建立了一个指针用来更新画面。到这里头文件已经完成了,下面就是cpp文件的实现。


下面是cpp的全部代码,我会分别讲解每一个块的作用于联系

#include "QtWidgetsApplication1.h"

CGXDevicePointer objDeviceptr;
CGXStreamPointer ObjStreamPtr;
CGXFeatureControlPointer ObjFeatureControlPtr;

QImage blackImage;                                                      //用于黑色背景
ICaptureEventHandler* pCaptureEventHandler=nullptr;                     //建立回调采集变量


QtWidgetsApplication1::QtWidgetsApplication1(QWidget* parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);

    connect(ui.open_camear_button, &QPushButton::clicked, this, &QtWidgetsApplication1::open_camera);
    connect(ui.close_camear_button, &QPushButton::clicked, this, &QtWidgetsApplication1::close_camera);
    connect(ui.start_acquisition_button, &QPushButton::clicked, this, &QtWidgetsApplication1::start_acquisition);
    connect(ui.close_acquisition_button, &QPushButton::clicked, this, &QtWidgetsApplication1::close_acquisition);

    QImage blackImage(ui.label_picture->size(), QImage::Format_RGB888);
    blackImage.fill(Qt::black);
    ui.label_picture->setPixmap(QPixmap::fromImage(blackImage));// 设置 label 的 pixmap 为黑色背景图片

    UpdataUI();
}

void QtWidgetsApplication1::open_camera()
{
    IGXFactory::GetInstance().Init();                                               //初始化设备
    GxIAPICPP::gxdeviceinfo_vector vectorDeviceInfo;
    IGXFactory::GetInstance().UpdateDeviceList(1000, vectorDeviceInfo);             //枚举设备

    if (vectorDeviceInfo.size() <= 0)
    {
        qDebug() << "无可用设备!";
        m_bisOpen = false;
    }
    else
    {
        qDebug() << vectorDeviceInfo[0].GetModelName() << endl;
        GxIAPICPP::gxstring strSN = vectorDeviceInfo[0].GetSN();
        objDeviceptr = IGXFactory::GetInstance().OpenDeviceBySN(strSN, GX_ACCESS_EXCLUSIVE);        //通过SN码连接设备
        m_bisOpen = true;
    }
    UpdataUI();
}

void QtWidgetsApplication1::close_camera()
{
    try
    {
        
        if (m_bisSnap)
        {
        	
            ObjFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();      //发送停止命令
            ObjStreamPtr->StopGrab();										//停止流通道采集 		
            ObjStreamPtr->UnregisterCaptureCallback();
            delete pCaptureEventHandler;
            pCaptureEventHandler = nullptr;						//停采、注销采集回调函数 
            
            ObjStreamPtr->Close();							//关闭流通道 
            objDeviceptr->Close();	
            IGXFactory::GetInstance().Uninit();   			//反初始化设备
        }
    }
    catch (...)
    {

    }
    qDebug() << "成功关闭设备";
    m_bisOpen = false;
    m_bisSnap = false;

    QImage blackImage(ui.label_picture->size(), QImage::Format_RGB888);
    blackImage.fill(Qt::black);
    ui.label_picture->setPixmap(QPixmap::fromImage(blackImage));// 设置 label 的 pixmap 为黑色背景图片

    UpdataUI();
}

void QtWidgetsApplication1::start_acquisition()
{
    if (m_bisOpen && !m_bisSnap)
    {
        
        ObjStreamPtr = objDeviceptr->OpenStream(0);

        ICaptureEventHandler* pCaptureEventHandler = new CSampleCaptureEventHandler(this);
        ObjStreamPtr->RegisterCaptureCallback(pCaptureEventHandler, NULL);                      //注册回调采集函数

        ObjStreamPtr->StartGrab();                                                      //开启流通道采集

        ObjFeatureControlPtr = objDeviceptr->GetRemoteFeatureControl();
        ObjFeatureControlPtr->GetEnumFeature("ExposureAuto")->SetValue("Off");                  //设置曝光模式
        ObjFeatureControlPtr->GetFloatFeature("ExposureTime")->SetValue(9000);                    //设置曝光度

        ObjFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();                     //发送开采命令

        m_bisSnap = true;
        qDebug() << "开始采集";
        UpdataUI();
    }
}

void QtWidgetsApplication1::close_acquisition()
{
    if (m_bisSnap)
    {
      
        // 等待子线程完成
        ObjFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();              //停止采集
        ObjStreamPtr->StopGrab();
        ObjStreamPtr->UnregisterCaptureCallback();

        delete pCaptureEventHandler;
        pCaptureEventHandler = nullptr;
        ObjStreamPtr->Close();                                                              //关闭通道
        m_bisSnap = false;
        UpdataUI();
    }
}

void QtWidgetsApplication1::UpdataUI()                                                      //更新
{
    ui.open_camear_button->setEnabled(!m_bisOpen);
    ui.close_camear_button->setEnabled(m_bisOpen);
    ui.start_acquisition_button->setEnabled(m_bisOpen && !m_bisSnap);
    ui.close_acquisition_button->setEnabled(m_bisOpen && m_bisSnap);                        //更新按钮UI

    if (!ImageQueue.empty())
    {
        QPixmap newImage = ImageQueue.dequeue();
        QMetaObject::invokeMethod(this, [this, newImage]() {
            ui.label_picture->setScaledContents(true);
            ui.label_picture->setPixmap(newImage);
         });
    }                                                                           //读取队列里的图片并显示

}                               
void CSampleCaptureEventHandler::DoOnImageCaptured(CImageDataPointer& objImageDataPointer, void* pUserParam)
{
        if (objImageDataPointer->GetStatus() == GX_FRAME_STATUS_SUCCESS)
        {
            qint64 m_width = objImageDataPointer->GetWidth();
            qint64 m_height = objImageDataPointer->GetHeight();
            uchar* pbit = (uchar*)objImageDataPointer->GetBuffer();

            QImage newImage(pbit, m_width, m_height, QImage::Format_Indexed8);
            newImage = newImage.scaled(m_width, m_height, Qt::KeepAspectRatio, Qt::SmoothTransformation);

            double d = ObjFeatureControlPtr->GetFloatFeature("CurrentAcquisitionFrameRate")->GetValue();
            qDebug() << "当前帧率:" << d;

            pMainWindow->ImageQueue.enqueue(QPixmap::fromImage(newImage));
            pMainWindow->UpdataUI();
            
        }
}


QtWidgetsApplication1

里面的四个connect分别对应了UI画面里的四个按钮功能,而下面的设置黑色背景图片可以设置也可以不设置。不设置就是在不获取图像的时候因为label没有东西可以显示,它是白色的也就是看不到了。设置完成了,记得更新画面UpdataUI这个程序相当于一个初始化的作用了。

   QImage blackImage(ui.label_picture->size(), QImage::Format_RGB888);
    blackImage.fill(Qt::black);
    ui.label_picture->setPixmap(QPixmap::fromImage(blackImage));// 设置 label 的 pixmap 为黑色背景图片

    UpdataUI();

open_camera(打开设备)

根据上面流程图可以知道进来的第一步就是先初始化设备,然后就是寻找设备了(枚举设备),上面的程序有注释这里就不显示代码了。
枚举设备后,根据找到的设备数量进行判断。m_bisOpen就是设置按钮的状态了,如果成功打开设备UI上面的按钮就不能再按钮了,所以要如果成功打开了就设置为不可用,后面的其他程序也是一个道理。qDebug相当于cout输出一些信息在控制台方便调试,连接设备的方式有四种,这里使用SN码的方式连接,其他方法请查看手册

    if (vectorDeviceInfo.size() <= 0)
    {
        qDebug() << "无可用设备!";
        m_bisOpen = false;
    }
    else
    {
        qDebug() << vectorDeviceInfo[0].GetModelName() << endl;
        GxIAPICPP::gxstring strSN = vectorDeviceInfo[0].GetSN();
        objDeviceptr = IGXFactory::GetInstance().OpenDeviceBySN(strSN, GX_ACCESS_EXCLUSIVE);        //通过SN码连接设备
        m_bisOpen = true;
    }
    UpdataUI();

close_camera(关闭设备)

有开必有关,根据m_bisOpen判断设备是否被打开了。后面就不管相机处于什么状态都给它停止并关闭了,这个程序跟close_acquisition意思是差不多的,后面我就不讲这个程序了。两个程序的区别一个是停止设备,另一个是先停止关闭这个很好理解。

start_acquisition(开始采集)

打开设备后,已经完成了流程图上面的三个步骤,这里将完成设置参数开始采集的操作。具体的程序上面的注释已经有了,但这里我讲一个需要注意的小点也是我自己做的时候遇到的问题——曝光度。通过程序去设置的曝光度是会影响画面的帧率的,设置的越高帧率越低,后面可以自行尝试。到这一个程序的最后一步,只是发送了开采命令并没有对图像进行读取与显示的操作,后面的两个程序才是关键的。

DoOnImageCaptured(回调采集类里面的操作)

到这里就正式开始了对图像的处理了,首先建立的三个变量是获取图片的基本信息。

qint64 m_width = objImageDataPointer->GetWidth();
qint64 m_height = objImageDataPointer->GetHeight();
uchar* pbit = (uchar*)objImageDataPointer->GetBuffer();

建立一个QImage将刚刚获取的图像数据放进去,注意QImage::Format_Indexed8根据自己相机能获取到的图像数据进行赋值,具体是写什么去官网查看自己相机的参数,我的相机的原始数据是Mono8的一个灰度图。而且下面的代码调用了scaled()函数对新创建的图像进行缩放操作,使用Qt::KeepAspectRatio保持宽高比的方式进行缩放,并使用Qt::SmoothTransformation参数来指定平滑的缩放。

QImage newImage(pbit, m_width, m_height, QImage::Format_Indexed8);
newImage = newImage.scaled(m_width, m_height, Qt::KeepAspectRatio, Qt::SmoothTransformation);

下面就是一个读取当前相机帧率的操作,可有可无,自己看着办。

double d = ObjFeatureControlPtr->GetFloatFeature("CurrentAcquisitionFrameRate")->GetValue();
qDebug() << "当前帧率:" << d;

这个就重要了,再上面处理好的图像先放到缓存队列enqueue里面,再调用UpdataUI进行画面的更新,这个函数是专门用来进行UI画面的显示与更新的。传上去这里就不管它了,它的任务已经完成了。

pMainWindow->ImageQueue.enqueue(QPixmap::fromImage(newImage));
pMainWindow->UpdataUI();

会不会有疑问,明明没有调用这个程序,为什么它能采集图像呢?它是相机SDK里面的一个虚基类,开启回调采集的时候,相机获取到图片的时候就告诉你,它有一张图片。你拿到之后又告诉相机“我要下一张图片”,相机获取到了图片又跟你说了一遍,一直循环下去。而且控制它开始与停止的就是下面的两个函数,它们位于是start_acquisitionclose_acquistion里面。

ObjFeatureControlPtr->GetCommandFeature("AcquisitionStart")->Execute();                     //发送开采命令
ObjFeatureControlPtr->GetCommandFeature("AcquisitionStop")->Execute();              //停止采集

UpdataUI(更新画面)

这是最后一个了,累死啦!!!
程序分两个部分,上半部分是更新UI界面的按钮的,下半部分就是更新图片进行实时显示的操作。

    ui.open_camear_button->setEnabled(!m_bisOpen);
    ui.close_camear_button->setEnabled(m_bisOpen);
    ui.start_acquisition_button->setEnabled(m_bisOpen && !m_bisSnap);
    ui.close_acquisition_button->setEnabled(m_bisOpen && m_bisSnap);                        //更新按钮UI

这一部分就是通过读取缓存队列里面的图像,然后进行显示。在缓存队列里面我们是先进先出的原则,这样做无论在什么时候对采集进行停止还是关闭设备,都不会因为没有来得及显示图片而进行内存管理错误,来不及显示的图片就暂时放在了队列里面,保证了程序的安全与稳定。

    if (!ImageQueue.empty())
    {
        QPixmap newImage = ImageQueue.dequeue();
        QMetaObject::invokeMethod(this, [this, newImage]() {
            ui.label_picture->setScaledContents(true);
            ui.label_picture->setPixmap(newImage);
         });
    }                                                                           //读取队列里的图片并显示

4.结果显示

在这里插入图片描述

### Qt 中导入模型并控制对象运动的实现方式 在 Qt 中,可以通过多种方法来加载 STL 文件或其他 3D 模型,并通过 OpenGL 或者 Qt3D 来渲染和控制这些模型的运动。以下是详细的说明: #### 加载 STL 文件 为了在 Qt 中加载 STL 文件,可以采用以下几种方法之一: 1. **解析 ASCII 格式的 STL 文件** 使用 C++ 编写代码手动解析 STL 文件的内容[^1]。这通常涉及逐行读取文件中的三角形顶点数据,并将其转换为适合 OpenGL 渲染的数据结构。 2. **使用第三方库** 可以借助像 `assimp` 这样的开源库来简化 STL 和其他格式(如 OBJ、FBX 等)的加载过程[^3]。Assimp 提供了跨平台的支持,能够轻松处理各种复杂模型。 ```cpp #include <assimp/Importer.hpp> #include <assimp/scene.h> #include <assimp/postprocess.h> void loadModel(const std::string& path) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { qDebug() << "Error loading model:" << importer.GetErrorString(); return; } processNode(scene->mRootNode, scene); } ``` 上述代码展示了如何利用 Assimp 库加载一个 3D 模型。 --- #### 显示 STL 模型 一旦成功加载 STL 数据,就可以通过 OpenGL 将其绘制出来。如果希望更方便地操作,则可以选择基于 QML 的解决方案——即使用 Qt3D[^4]。 ##### 使用 Qt3D 显示模型 Qt3D 是一种现代框架,允许开发者构建高性能的 3D 场景。下面是一个简单的例子展示如何设置基本环境并将模型加入其中[^2]: ```qml import Qt3D.Core 2.0 import Qt3D.Render 2.0 import Qt3D.Input 2.0 import Qt3D.Extras 2.0 Entity { id: rootEntity components: [ RenderSettings {}, Viewport {} ] Camera { id: cameraEntity position: Qt.vector3d(0, 0, 40) } FirstPersonCameraController { target: cameraEntity } // 添加相机控制器以便交互 Mesh { source: "path/to/model.stl" } } ``` 此片段设置了基础场景配置,包括摄像机视角调整功能以及指定要加载的目标 STL 文件路径。 --- #### 动态控制物体运动 对于六轴机械臂这样的多关节设备来说,除了单纯显示外还需要支持实时更新位置姿态等功能。这就需要用到矩阵变换技术完成旋转平移等动作模拟。 假设我们已经获取到了某个特定时间点下各个关节的角度参数列表 `{theta_1,..., theta_n}` ,那么可以根据正向运动学原理计算末端执行器的确切坐标系方位关系如下所示伪算法描述: ```pseudo for each joint i from base to end effector do compute transformation matrix T_i based on current angle θ_i and link length l_i end for final_position <- multiply all individual transformations together into one final homogeneous transform H_final apply H_final onto the graphical representation of arm tip within rendering pipeline. ``` 实际编码时可能需要依赖于线性代数运算库比如 Eigen 完成具体数值求解任务. ---
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

畵颜卿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值