写在前面
本篇文章仅为工作记录,以下是本篇文章的开发环境
开发平台:Windows11
开发环境:QT5.15.2
开发语言:C++
编译器:MSVC 2019 64bit
线扫相机:LA-CM-16K05A-00-R
SDK版本:SaperaLTSDKSetup_8.20
采集卡:Xtium-CL_MX4
1、安装Sapera Lt
这没什么好说的,去官网下载SaperaLTSDKSetup_8.20安装即可;
默认安装路径;C:\Program Files\Teledyne DALSA;说明文档和示例程序(c++、c#)以及动/静态库都在该文件夹下
本项目基于c++开发,需要的文件有Classes、Include、Lib,Classes是一些基本类,Include是必须的头文件,Lib是编译好的库,
help包含各种接口参数说明,不懂得就去里面找。
Sapera文件结构如下:
需要把Classes、Include、Lib拷贝到QT项目工作目录,如下图:
2、QT项目构建
在使用api接口时.pro文件一定要包含一下命令,链接基本的库文件
win32: LIBS += -L$$PWD/Lib/Win64/ -lSapClassBasic
win32: LIBS += -L$$PWD/Lib/Win64/ -lcorapi
INCLUDEPATH += $$PWD/Include
DEPENDPATH += $$PWD/Include
INCLUDEPATH += $$PWD/Classes/Basic
DEPENDPATH += $$PWD/Classes/Basic
构造相机类CCamera
基本流程:设备初始化(相机、采集卡)->创建底层资源->等待采集命令;
定义头文件包含一些基础功能:相机初始化、采集卡初始化、连续采图函数、单次采图函数、保存图像、各种硬件设备的抽象表示等等,头文件如下:
#pragma once
#include <QObject>
#include <QDebug>
#include "SapClassBasic.h"//所有类的头文件,必须包含
#include <SapBuffer.h>
class CCamera : public QObject
{
Q_OBJECT
public:
CCamera(QObject *parent = nullptr);
~CCamera();
void free(); //释放资源
bool init_camera(); //初始化相机
bool initDevice(char *m_serverName, const char *ccfPath); //初始化采集卡
bool grabOnce(); //非连续采图
bool grabContinues(); //连续采图
void saveImages(); //保存图像
void stop(); //停止采图
char *m_ServerName{nullptr}; //服务器名称、即采集卡名称
SapAcquisition *m_Acquisition{nullptr}; //控制与板卡相连接的设备,即线扫相机,仅用于存储采集资源参数,
SapBufferWithTrash *m_Buffers{nullptr}; //垃圾缓冲区,实时处理时建立垃圾缓冲区用于存放转换数据(常用于处理速度跟不上数据采集速度时)
//SapView *m_View{nullptr}; //在窗口中显示SapBuffer对象的资源
SapTransfer *m_Xfer{nullptr}; //管理通用传输过程的功能,即将数据从一个源节点传输到目标节点的操作
private:
};
如果需要在窗口上实时显示采集到的图像打开SapView的注释即可,记得也要打开.cpp里面相应的注释,如果开显示功能,在获取图像进行后续操作后记得释放缓冲区地址,通过函数ReleaseAddress()释放,使用方法见Sapera++Prog.pdf,如果显示卡顿且相机采样率不能改变,可以将缓冲区开的多一点。
定义文件功能实现,代码如下:
注意ccfPath 为相机的配置文件,可以通过相机专家软件根据操作指南配置相机,然后保存配置参数就行了,ccfPath 就是保存的参数文件
#include <QDebug>
#include "CCamera.h"
BYTE *pData{nullptr};
static int framcount = 0; //传输帧计数
void XferCallBack(SapXferCallbackInfo *pInfo) //缓冲区传输图像数据时的回调函数
{
CCamera *myCamera = static_cast<CCamera *>(pInfo->GetContext());
// myCamera->m_View->Show();
myCamera->saveImage();
//以下代码测试用
// if (framcount <= 10) {
// myCamera->saveImage(); //保存图像
// } else {
// qDebug() << "Grab Finished"; //停止采集
// // myCamera->stopGrab();
// qDebug() << "count: " << count;
// qDebug() << "framcount: " << framcount;
// myCamera->m_Xfer->Abort();
// }
}
CCamera::CCamera(QObject *parent)
: QObject(parent)
{
init_camera();
}
CCamera::~CCamera()
{
free();
}
bool CCamera::init_camera() //初始化相机
{
m_ServerName = new char[MAX_PATH]; //采集卡名称
std::string ccfPath = "C:\\Program Files\\Teledyne DALSA\\Sapera\\gxf\\ExternalConfig.ccf"; //配置文件路径
SapManager::GetServerName(0, SapManager::ResourceAcq, m_ServerName); //获得采集卡名称
if (initDevice(m_ServerName, ccfPath.c_str())) {
qDebug() << "open " << m_ServerName << " success";
} else {
qDebug() << "m_ServerName: " << m_ServerName;
qDebug() << "ccfPath: " << QString::fromStdString(ccfPath);
qDebug() << "Open " << m_ServerName << " Failed!";
free();
return false;
}
m_Buffers->GetAddress((void **)&pData);
return true;
}
bool CCamera::initDevice(char *m_serverName, const char *ccfPath) //初始化采集卡
{
qDebug() << "Sapera Console Grab Example (C++ version)";
SapLocation loc(m_serverName, 0); //初始化采集卡,物理设备的抽象表示,采集卡、资源序号
qDebug() << SapManager::GetResourceCount(m_serverName, SapManager::ResourceAcq);
if (SapManager::GetResourceCount(m_serverName, SapManager::ResourceAcq) > 0) {
m_Acquisition = new SapAcquisition(loc, ccfPath); //控制与板卡相连接的设备
m_Buffers = new SapBufferWithTrash(10, m_Acquisition); //带有垃圾缓冲区的缓冲区
//m_View = new SapView(m_Buffers, SapHwndAutomatic);
m_Xfer = new SapAcqToBuf(m_Acquisition, m_Buffers, XferCallBack, this); //管理通用传输过程的功能
}
//创建底层资源
if (m_Acquisition && !*m_Acquisition && !m_Acquisition->Create()) {
return false;
}
if (m_Buffers && !*m_Buffers) {
if (!m_Buffers->Create()) {
return false;
} else {
m_Buffers->Clear(); //创建底层资源后清空缓冲区内容
}
}
// if (m_View && !*m_View && !m_View->Create()) {
// return false;
// }
if (m_Xfer && m_Xfer->GetPair(0)) {
if (!m_Xfer->GetPair(0)->SetCycleMode(SapXferPair::CycleNextWithTrash)) { //缓冲模式:下一个空缓冲区->垃圾缓冲区
return false;
}
}
if (m_Xfer && !*m_Xfer && !m_Xfer->Create()) {
return false;
}
return true;
}
bool CCamera::grabContinues() //连续传输
{
framcount = 1;
count = 1;
bool isok = m_Xfer->IsGrabbing();
qDebug() << isok;
if (!isok) {
isok = m_Xfer->Grab();
m_Xfer->Wait(1000);
}
qDebug() << isok;
return isok;
}
bool CCamera::grabOnce() //传输5张
{
framcount = 1;
count = 1;
bool isok = false;
if (m_Xfer->IsGrabbing()) {
m_Xfer->Freeze();
if (!m_Xfer->Wait(1000)) {
m_Xfer->Abort();
}
return false;
} else {
isok = m_Xfer->Snap(5);
m_Xfer->Wait(1000);
}
return isok;
}
bool CCamera::stopGrab() //关闭采集
{
bool isok = m_Xfer->IsGrabbing();
qDebug() << isok;
if (isok) {
isok = m_Xfer->Freeze();
qDebug() << isok;
if (!m_Xfer->Wait(5000)) {
isok = m_Xfer->Abort();
qDebug() << isok;
}
}
return isok;
}
void CCamera::saveImage()
{
std::stringstream ss;
ss << "D:\\test\\bmp\\" << framcount << ".bmp";//图像保存位置
std::string name = ss.str();
const char *savename = name.c_str();
m_Buffers->Save(savename, "-format bmp"); //保存为bmp格式
qDebug() << "framcount: " << framcount;
framcount++;
}
void CCamera::free() //释放资源
{
if (m_ServerName) {
delete[] m_ServerName;
m_ServerName = nullptr;
}
if (m_Xfer) {
m_Xfer->Destroy();
delete m_Xfer;
m_Xfer = nullptr;
}
// if (m_View) {
// m_View->Destroy();
// delete m_View;
// m_View = nullptr;
// }
if (m_Buffers) {
m_Buffers->Destroy();
delete m_Buffers;
m_Buffers = nullptr;
}
if (m_Acquisition) {
m_Acquisition->Destroy();
delete m_Acquisition;
m_Acquisition = nullptr;
}
}
至此内触发模式下的相机类封装完成,为了测试采图功能是否正常可以写个QT界面,
本文仅测试单次采图、连续采图、停止彩图、保存图像是否正常,仅写了简单的界面,没有显示功能
图像保存在 “D:\test\bmp\”,路径一定要用双反斜杠,每次文件命名从1开始,会覆盖之前的数据,跑对此文件记得备份;
另外如果图像的分辨率和相机采样率很高,切记不要跑太久,存储空间占用会很大,
QT界面文件如下:
头文件:
#pragma once
#include <QMainWindow>
#include <QPushButton>
#include <QHBoxLayout>
#include <QWidget>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QPushButton *grabContinues, *gradOnce, *stopgrab;
QHBoxLayout *mainlayout;
};
源文件:
#include "MainWindow.h"
#include "CCamera.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
grabContinues = new QPushButton("连续采集100张", this);
gradOnce = new QPushButton("单词采集1张", this);
stopgrab = new QPushButton("停止采集", this);
QWidget *centralwidget = new QWidget(this); //设置中央部件和布局管理
setCentralWidget(centralwidget);
mainlayout = new QHBoxLayout(centralwidget);
mainlayout->addWidget(grabContinues);
mainlayout->addWidget(gradOnce);
mainlayout->addWidget(stopgrab);
CCamera *cam = new CCamera();
connect(grabContinues, &QPushButton::clicked, cam, &CCamera::grabContinues);
connect(gradOnce, &QPushButton::clicked, cam, &CCamera::grabOnce);
connect(stopgrab, &QPushButton::clicked, cam, &CCamera::stopGrab);
// connect()
}
MainWindow::~MainWindow()
{
}
main文件:
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.resize(800, 600);
w.show();
return a.exec();
}
以上工作为个人笔记,如有错误欢迎指教!
下篇文章:Dalsa线扫相机SDK二次开发(外触发 QT开发)