Dalsa线扫描相机的二次开发,因为官方只有MFC和命令行版本的,我需要使用QT进行开发,于是自己花时间研究,然后写了一个,效果如下:
可能GIF动图有点模糊,在图片中,上面为实时画面,我使用的是4096*128分辨率,然后使用手机的闪光灯在相机旁边摇晃,加上没有调焦调距,效果确实是如此。下面是实时拼接的图,将结果缩小旋转90度,然后依次拼接起来,就是下面拼接图片的效果。
拼接图的右边那个黑框是截图时候参数没有设置好,后面已经改好了。
相机:HL-FM 采集卡版本灰度相机
SDK版本为:SaperaLTSDKSetup_8.60、
采集卡驱动为:xtium2-clhs_fx8lc_110010122、
QT版本:5.12
编译器:MSVC 2017 64bit
当然,实际只要版本差不多就行,我后面会提供我这个版本的软件驱动。
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += SAPERA_DOT_NET
DEFINES += COR_WIN64
#QMAKE_CXXFLAGS += -fno-case-insensitive
QMAKE_CXXFLAGS += -fno-code-hoisting
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
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
#include
#include"SapClassBasic.h"
#include
#include
#include
#include
#include
//#include
#include
#include
#include
using namespace std;
SapManager* m_pManager;
SapAcquisition* m_Acquisition;
SapBufferWithTrash* m_Buffers;
SapTransfer* m_Xfer;
SapView* m_View;
SapAcqDevice *m_AcqDevice;
static int framcount = 0;
BYTE *pData;
static void XferCallBack(SapXferCallbackInfo* pInfo)
{
m_View->Show();
}
bool initDevice(char* m_serverName, const char*ccfpath)
{
printf("Sapera Console Grab Example (C++ version)\n");
SapLocation loc(m_serverName, 0);
if (SapManager::GetResourceCount(m_serverName, SapManager::ResourceAcq) > 0)
{
m_Acquisition = new SapAcquisition(loc, ccfpath);
m_Buffers = new SapBufferWithTrash(2, m_Acquisition);
m_View = new SapView(m_Buffers, SapHwndAutomatic);
m_Xfer = new SapAcqToBuf(m_Acquisition, m_Buffers, XferCallBack, m_View);
}
if (m_Acquisition && !*m_Acquisition && !m_Acquisition->Create()) return FALSE;
if (m_Buffers && !*m_Buffers)
{
if (!m_Buffers->Create())
{
return FALSE;
}
m_Buffers->Clear();
}
if (m_View && !*m_View && !m_View->Create())
{
return FALSE;
}
// Set next empty with trash cycle mode for transfer
if (m_Xfer && m_Xfer->GetPair(0))
{
if (!m_Xfer->GetPair(0)->SetCycleMode(SapXferPair::CycleNextWithTrash))
{
return FALSE;
}
}
// Create transfer object
if (m_Xfer && !*m_Xfer && !m_Xfer->Create())
{
return FALSE;
}
return true;
}
int main(int argc, char** argv)
{
//QApplication a(argc, argv);
char* m_SerName = new char[MAX_PATH];//采集卡名称
char* configFilename = new char[MAX_PATH];
SapBuffer sapBuffer;
int flag = 0;
string ccf_path = "C:\\Program Files\\Teledyne DALSA\\Sapera\\CamFiles\\User\\FrameGrabber.ccf";
uchar *imgData = new uchar[150 * 100/1.75*7000];
m_pManager->GetServerName(0, SapManager::ResourceAcq, m_SerName);
//初始化设备
if (initDevice(m_SerName, ccf_path.c_str()))
{
cout << "Open " << m_SerName << " Success!" << endl;
}
else
{
printf("m_SerName: %s \n",m_SerName);
printf("configFilename: %s \n",configFilename);
cout << "Open " << m_SerName << " Failed!" << endl;
return 0;
}
//开始采集
if (!m_Xfer->IsGrabbing())
{
m_Xfer->Grab();
flag = 1;
m_Buffers->GetAddress((void**)&pData);
//int width = m_Buffers->GetWidth();
//int height = m_Buffers->GetHeight();
}
while (true)
{
if (flag == 1)
{
std::cout << "Grab" << std::endl;
//Sleep(200);
std::stringstream ss;
ss << "D:\\test\\bmp\\" << framcount << ".bmp";
std::string name = ss.str();
const char* savename = name.c_str();
QImage image(4096 , 128, QImage::Format_Grayscale8);
m_Buffers->Clear();
if (!m_Xfer->IsGrabbing())
m_Xfer->Grab();
// 保存文件:
// m_Buffers->Save(savename, "-format bmp");
//m_Mats.push_back(m_Mat);
framcount++;
if (framcount > int(1000))
{
framcount = 0;
cout << "Grab Finished" << endl;//停止采集
m_Xfer->Freeze();
break;
}
}
}
delete imgData;
// Destroy transfer object
if (m_Xfer && *m_Xfer) m_Xfer->Destroy();
if (m_View && *m_View) m_View->Destroy();
if (m_Buffers && *m_Buffers) m_Buffers->Destroy();
if (m_Acquisition && *m_Acquisition) m_Acquisition->Destroy();
//Delete all pointer
if (m_View) delete m_View;
if (m_Buffers) delete m_Buffers;
if (m_Xfer) delete m_Xfer;
if (m_Acquisition) delete m_Acquisition;
//return a.exec();
return 0;
}
其中 ccf_path 路径需要配置为自己刚刚保存的路径,然后路径中需要使用两个\ 来分割。m_Buffers->Save(savename, "-format bmp"); 这行代码我注释起来了,因为会保存文件,想看保存文件的,可以打开注释,路径在D盘的test/bmp路径下,记得别跑太长时间,因为几秒钟保存的图片文件就有好几个G那么大了。
1. 新建 SapCameraDev 的类,继承自 Qthread,头文件源码如下:
#ifndef SAPCAMERADEV_H
#define SAPCAMERADEV_H
#include "SapClassBasic.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class SapCameraDev : public QThread
{
Q_OBJECT
public:
explicit SapCameraDev(QObject *parent = nullptr);
~SapCameraDev();
void setMax(int v);
static void XferCallBack(SapXferCallbackInfo* pInfo);
bool initDevice(char* m_serverName, const char*ccfpath);
void setStopFlag(bool flag);
void setSaveFlag(bool flag);
void setFreezeFlag(bool flag);
void setCcf_path(QString s);
BYTE *getpData();
// static SapCameraDev* instance;
// static SapCameraDev* getSapCameraDevInstance()
// {
// if (instance == nullptr)
// {
// instance = new SapCameraDev();
// }
// return instance;
// }
signals:
// void getNewImage(QImage image); // 接受到新信号
void getNewImage(BYTE *pData);
void getNewImage_Image(QImage );
void imageOK(bool flag);
private:
void run() override;
private:
string saveImagePath = string("D:\\test\\bmp\\");
string ccf_path = string("C:\\Program Files\\Teledyne DALSA\\Sapera\\CamFiles\\User\\FrameGrabber.ccf");
int max = 1000;
bool isStop = false;
bool isSave = false;
bool isFreeze = false; // 停止
SapManager* m_pManager;
SapAcquisition* m_Acquisition;
SapBufferWithTrash* m_Buffers;
SapTransfer* m_Xfer;
SapView* m_View = nullptr;
SapAcqDevice *m_AcqDevice;
int framcount = 0;
BYTE *pData;
uchar *imgData = new uchar[150 * 100/1.75*7000];
};
#endif // SAPCAMERADEV_H
2. SapCameraDev 的cpp文件源码如下:
#include "sapCameraDev.h"
//SapCameraDev* SapCameraDev::instance = nullptr;
//BYTE *temp_pData;
void SapCameraDev::XferCallBack(SapXferCallbackInfo* pInfo)
{
// printf("XferCallBack\n");
// SapCameraDev* temp = getSapCameraDevInstance();
// if(temp){
// QImage image(4096 , 128, QImage::Format_Grayscale8);
// memcpy(image.bits(), temp_pData, 4096 * 128);
// emit temp->getNewImage_Image(image);
// }
}
bool SapCameraDev::initDevice(char* m_serverName, const char*ccfpath)
{
printf("Sapera Console Grab Example (C++ version)\n");
SapLocation loc(m_serverName, 0);
if (SapManager::GetResourceCount(m_serverName, SapManager::ResourceAcq) > 0)
{
m_Acquisition = new SapAcquisition(loc, ccfpath);
m_Buffers = new SapBufferWithTrash(2, m_Acquisition);
// m_View = new SapView((SapBuffer*)m_Buffers, SapHwndAutomatic);
m_Xfer = new SapAcqToBuf(m_Acquisition, (SapBuffer*)m_Buffers, SapCameraDev::XferCallBack);
}
if (m_Acquisition && !*m_Acquisition && !m_Acquisition->Create()) return FALSE;
if (m_Buffers)
{
if (!m_Buffers->Create())
{
return FALSE;
}
m_Buffers->Clear();
}
if (m_Xfer && m_Xfer->GetPair(0))
{
if (!m_Xfer->GetPair(0)->SetCycleMode(SapXferPair::CycleNextWithTrash))
{
return FALSE;
}
}
// Create transfer object
if (m_Xfer && !*m_Xfer && !m_Xfer->Create())
{
return FALSE;
}
return true;
}
SapCameraDev::SapCameraDev(QObject *parent)
:QThread(parent)
{
}
void SapCameraDev::setStopFlag(bool flag)
{
isStop = flag;
}
void SapCameraDev::setSaveFlag(bool flag)
{
isSave = flag;
}
void SapCameraDev::setFreezeFlag(bool flag)
{
isFreeze = flag;
}
void SapCameraDev::setCcf_path(QString s)
{
ccf_path = s.toStdString();
}
BYTE * SapCameraDev::getpData()
{
return pData;
}
SapCameraDev::~SapCameraDev()
{
delete[] imgData;
// Destroy transfer object
if (m_Xfer && *m_Xfer) m_Xfer->Destroy();
if (m_View && *m_View) m_View->Destroy();
if (m_Buffers ) m_Buffers->Destroy();
if (m_Acquisition && *m_Acquisition) m_Acquisition->Destroy();
//Delete all pointer
if (m_View) delete m_View;
if (m_Buffers) delete m_Buffers;
if (m_Xfer) delete m_Xfer;
if (m_Acquisition) delete m_Acquisition;
}
void SapCameraDev::setMax(int v)
{
max = v;
}
void SapCameraDev::run()
{
char* m_SerName = new char[MAX_PATH];//采集卡名称
char* configFilename = new char[MAX_PATH];
int flag = 0;
//vector m_Mats;
// cv::Mat m_Mat_all = cv::Mat::zeros(cv::Size(700,85700), CV_8U);
m_pManager->GetServerName(0, SapManager::ResourceAcq, m_SerName);
//初始化设备
if (initDevice(m_SerName, ccf_path.c_str()))
{
cout << "Open " << m_SerName << " Success!" << endl;
}
else
{
printf("m_SerName: %s \n",m_SerName);
printf("configFilename: %s \n",ccf_path.c_str());
cout << "Open " << m_SerName << " Failed!" << endl;
return ;
}
//开始采集
if (!m_Xfer->IsGrabbing())
{
m_Xfer->Grab();
flag = 1;
m_Buffers->GetAddress((void**)&pData);
//temp_pData = pData;
//int width = m_Buffers->GetWidth();
//int height = m_Buffers->GetHeight();
}
std::cout << "Grab" << std::endl;
while (true)
{
if (flag == 1)
{
//std::cout << "Grab" << std::endl;
//Sleep(1);
std::stringstream ss;
ss << saveImagePath << framcount << ".bmp";
std::string name = ss.str();
const char* savename = name.c_str();
QImage image(4096 , 128, QImage::Format_Grayscale8);
if (isFreeze)
{
m_Xfer->Freeze();
emit imageOK(false);
}else{
//m_Buffers->Clear();
m_Xfer->Grab();
// 传递QT图片信号
// memcpy(image.bits(), pData, 4096 * 128);
// emit getNewImage_Image(image);
emit getNewImage(pData);
QThread::usleep(1000);
}
if(isSave){
m_Buffers->Save(savename, "-format bmp");
framcount++;
}
if(isStop){
framcount = 0;
cout << "Grab Finished" << endl;//停止采集
m_Xfer->Freeze();
break;
}
if (framcount > max)
{
framcount = 0;
isSave = false;
cout << "save Finished" << endl;//停止采集
//m_Xfer->Freeze();
//break;
}
}
}
}
3. 为了更好的展示,我自己新建了一个界面:
4. mainwindow.h 源码:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "sapCameraDev.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
SapCameraDev *sap;
QImage *sapImage;
QImage *resultImage;
//QImage *middleImage;
QPixmap *pixmap;
QTimer *showTimer;
QPainter *painter;
int offset_x = 0;
bool isImageOK = false;
bool isSplic = false;
};
#endif // MAINWINDOW_H
5. mainwindow.cpp源码:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
sap = new SapCameraDev(this);
// 绑定按钮的信号
connect(ui->pushButton_start,&QPushButton::clicked,this,[=](){
sap->setStopFlag(false);
sap->setFreezeFlag(false);
sap->start();
});
connect(ui->pushButton_freeze,&QPushButton::clicked,this,[=](){ //
sap->setFreezeFlag(true);
});
connect(ui->pushButton_stop,&QPushButton::clicked,this,[=](){
sap->setStopFlag(true);
});
connect(ui->pushButton_continue,&QPushButton::clicked,this,[=](){
sap->setFreezeFlag(false);
});
connect(ui->checkBox_saveFile,&QCheckBox::stateChanged,this,[=](int flag){
sap->setSaveFlag(flag);
});
connect(ui->checkBox_splic,&QCheckBox::stateChanged,this,[=](int flag){
isSplic = flag;
resultImage->fill(Qt::transparent);
});
// connect(sap,&SapCameraDev::imageOK,this,[=](bool flag){
// isImageOK = flag;
// offset_x = 0;
// });
// 实时图像:
sapImage = new QImage(4096, 128, QImage::Format_Grayscale8);
// 拼接图像:
resultImage = new QImage(1024,256,QImage::Format_Grayscale8);
resultImage->fill(Qt::transparent); // 透明色
painter = new QPainter(resultImage);
ui->label->setScaledContents(true); // 允许自动缩放
ui->label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 水平方向上自动伸展,垂直方向上保持固定高度
ui->label_2->setScaledContents(true); // 允许自动缩放
ui->label_2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 水平方向上自动伸展,垂直方向上保持固定高度
ui->label_2->setPixmap(QPixmap::fromImage(*resultImage));
//ui->label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); // 忽略大小策略
// 接收线程类发送来的信号进行图片渲染:
connect(sap,&SapCameraDev::getNewImage,this,[=](BYTE *pData){
// 显示实时画面:
memcpy(sapImage->bits(), pData, 4096 * 128);
ui->label->setPixmap(QPixmap::fromImage(*sapImage));
// 是否拼接:
if(isSplic){
// 显示拼接画面:
// 旋转:
QImage rotatedImage = sapImage->scaled(256, 8, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
rotatedImage = rotatedImage.transformed(QTransform().rotate(90));
if(offset_x<1024){
painter->drawImage(offset_x,0,rotatedImage);
offset_x += rotatedImage.width();
ui->label_2->setPixmap(QPixmap::fromImage(*resultImage));
}else{
offset_x = 0;
}
}
});
}
MainWindow::~MainWindow()
{
delete ui;
sap->quit();
}
OK,撒花完结!!!
其实源码95%都已经给你们放出来了,有能力的自己已经可以做出来了,毕竟当时踩了很多坑,而且当时下载别人的案例,也花了一点小钱,而且他们的还运行不起来,我把我自己的源码放出来,收回一点成本可以吧。不求大富大贵,最起码回本啊,兄弟们,还望理解。
额,不知道为什么,必须设置为免费的,还是0积分,晕死,我先看看如何设置再分享出来。
相关资源链接:
【免费】Dalsa线扫描相机资源分享(一)-安装驱动和配置说明资源-CSDN文库
【免费】Dalsa线扫描相机资源分享(二)-开发文档资源-CSDN文库
【免费】Dalsa线扫描相机资源分享(三)-简单的QT测试程序,未封装类资源-CSDN文库
参考文章:
Dalsa线扫相机SDK二次开发_dalsa相机二次开发-CSDN博客