Dalsa线扫相机SDK二次开发_2
写在前面
本篇文章仅为工作记录,以下是本篇文章的开发环境
开发平台: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文件一定要包含一下命令,链接基本的库文件
1 2 3 4 5 6 7 8 | 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
基本流程:设备初始化(相机、采集卡)->创建底层资源->等待采集命令;
定义头文件包含一些基础功能:相机初始化、采集卡初始化、连续采图函数、单次采图函数、保存图像、各种硬件设备的抽象表示等等,头文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #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 就是保存的参数文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #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界面文件如下:
头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #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; }; |
源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #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文件:
1 2 3 4 5 6 7 8 9 10 11 12 | #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(); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧