赞
踩
本次的开发环境是基于vs2019使用QT的框架对大恒相机的SDK进行实时采集的操作。我们从零开始讲,根据上面的资料文档我们来添加一个新的项目,并且将C++的库文件添加进去。
首先,我们新建的时候使用QT的模板。
记住我们新建的路径,下面添加库文件的时候用得到
创建之后会让我们选择QT的模板,直接默认就好了,等待项目创建完成。待项目新建完成,根据资料文档根据以下路径/Samples/C++ SDK 找到两个文件夹,分别是inc和lib这两个文件都是大恒相机的库文件,我们导入就可以了。
库文件导入后需要在vs中进行设置,打开vs–右键解决方案–属性
到这里,其实我们已经基本搭建完了。不过下面还有一项我们可以选择一下,默认是窗口模式,我们选择控制台。因为窗口模式在我们调试的时候window的控制台不会显示从来,打印的信息就看不到了,所以可以设置一下。
前面的环境我们已经配置完成了,现在先来验证一下。在 .h的头文件里添加#include <GalaxyIncludes.h>,这是相机的SDK基本都在里面,具体是什么意思可以查看资料文件。添加完成后,运行程序如果正常运行且不报错,环境搭建就算是完成了,我们可以进行下一步了。
这是整体的流程思路。点击vs里面的资源管理器,有一个UI的文件,打开并建立四个按钮控制设备的打开与关闭,采集和停止,再插入一个标签的控件用来显示图像。所以我们的画面一共有五个控件。在左侧的属性控制器——属性——objectName,可以修改控件对象名字。对于UI的画面,这里就已经完成了,后面就是代码的部分。
这里是部分代码块的讲解,后是会有完整代码的。可能会有些不足,但大体思路是这样的,可以根据自己的理解去进行调整。
#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_acquisition和close_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);
});
} //读取队列里的图片并显示
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。