https://blog.csdn.net/leo_888/article/details/81084526

https://blog.csdn.net/lyc_daniel/article/details/9065475 基于Qt的Sapera LT开发

应用背景:晶体表面疵病工业检测,导轨运动的光栅尺反馈系统产生的脉冲用于外触发Dalsa相机进行图像采集。

解决问题:Dalsa线阵CCD直接采集的图像是当前一行的图像,配套的采集卡中用于存储图像的缓冲区有限,当平台连续长距离运动时,如果不及时读取缓冲区的图像,新采集的图像将覆盖之前采集的图像。

阅读Dalsa相机的开发文档中的继承图,如下:

我们最为关心的是缓冲区的内容SapBuffer和将采集内容转运到缓冲区的SapAcqToBuf,细心一点的话还能看到采集内容转运到缓冲区的回调函数的Info。

查看官方提供的一些开发Demo

 
  1. // Transfer callback function is called each time a complete frame is transferred.

  2. // The function below is a user defined callback function.

  3.  

  4. void XferCallback(SapXferCallbackInfo *pInfo)

  5. {

  6.    // Display the last transferred frame

  7.    SapView *pView = (SapView *) pInfo->GetContext();

  8.    pView->Show();

  9. }

  10. // Example program

  11. //

  12. main()

  13. {

  14.    // Allocate acquisition object

  15.    SapAcquisition *pAcq =

  16.       new SapAcquisition(SapLocation (“X64-CL_1”, 0), “MyCamera.ccf”);

  17.  

  18.    // Allocate buffer object, taking settings directly from the acquisition

  19.    SapBuffer *pBuffer = new SapBuffer(1, pAcq);

  20.  

  21.    // Allocate view object, images will be displayed directly on the desktop

  22.    SapView *pView = new SapView(pBuffer, SapHwndDesktop);

  23.  

  24.    // Allocate transfer object to link acquisition and buffer

  25.    SapTransfer *pTransfer = new SapTransfer(XferCallback, pView);

  26.    pTransfer->AddPair(SapXferPair(pAcq, pBuffer));

  27.  

  28.    // Create resources for all objects

  29.    BOOL success = pAcq->Create();

  30.    success = pBuffer->Create();

  31.    success = pView->Create();

  32.    success = pTransfer->Create();

  33.  

  34.  

  35.    // Start a continuous transfer (live grab)

  36.    success = pTransfer->Grab();

  37.    printf("Press any key to stop grab\n");

  38.    getch();

  39.  

  40.  

  41.  

  42.  

  43.    // Stop the transfer and wait (timeout = 5 seconds)

  44.    success = pTransfer->Freeze();

  45.    success = pTransfer->Wait(5000);

  46.    printf("Press any key to terminate\n");

  47.    getch();

  48.  

  49.    // Release resources for all objects

  50.    success = pTransfer->Destroy();

  51.    success = pView->Destroy();

  52.    success = pBuffer->Destroy();

  53.    success = pAcq->Destroy();

  54.  

  55.    // Free all objects

  56.    delete pTransfer;

  57.    delete pView;

  58.    delete pBuffer;

  59.    delete pAcq;

  60.  

  61.    return 0;

  62. }

不难发现:

首先需要建立Acquisition,这里有设备信息,需要采集的图像信息和设置(如图像宽度,高度),亦可通过官方SDK自带的GUI对话框读取配置文件很快键地得到。

其次建立我们想要的缓冲区,由于我们的基本配置信息已经通过Acquisition得到,因此在SapBuffer的众多重载函数中选取了

 
  1. SapBuffer(

  2. int count,

  3. SapXferNode* pSrcNode,

  4. SapBuffer::Type type = SapBuffer::TypeScatterGather,

  5. SapLocation loc = SapLocation::ServerSystem

  6. );

其中count为缓冲区的数目,它们显然具有同配置文件中图像的大小,数据格式。SapXferNode为SapAcquisition的父类,直接传递Acquisition的指针即可,后面采用默认的参数。

(接下来是用于显示图像的SapView类,将缓冲区与用于显示的控件的窗口句柄绑定起来,就可以将图像显示在指定的控件上。这个与采集过程关系不大,但可视化这个,大家懂得。)

接下来是比较重要的步骤,建立了从图像数据采集到缓冲区的转移步骤。由于获取完整图像(理想情况下图像高度设置在80000,但缓冲区大小有限,设置在30000)时间较长,因此有足够的时间将缓冲区数据读取出来,不存在采集的速率高于转移的速率,用不上垃圾缓存区,因此使用下面的函数

 
  1. SapTransfer(

  2. SapXferCallback pCallback = NULL,

  3. void* pContext = NULL,

  4. SapLocation loc = SapLocation::ServerUnknown

  5. );

第一个为回调函数,第二个为回调函数的上下文信息,其实就是用于传递到回调函数的参数。

SDK中对此有一段话解答了我对这种线阵CCD图像和采集卡的使用方式:之前一直误以为一个Acquisition是获取当前一帧图像(即一次曝光获取的图像,一行图像),而事实上是已经拼接成整张图像(设置信息中的图像宽度和高度)。

By default, regular and trash buffer callback functions are called at each end of frame event, that is, when a complete image has been transferred.

SDK中特别指出

If you use this class , you must use the AddPair method to add transfer pairs of source and destination nodes. You must do this before calling the Create method.

所以后面紧接着写了addPair,将缓存区与采集区绑定起来。

接下里就是他们各自的创建了Create,使用默认的创建方法就好,其他定制化的方法没必要,也不会。

仿照着写了一个初始化的代码

 
  1. // TODO: 在此添加额外的初始化代码

  2. CAcqConfigDlg dlg(this, NULL);

  3. if (dlg.DoModal() == IDOK)

  4. {

  5. // Define on-line objects

  6. m_pAcq = new SapAcquisition(dlg.GetAcquisition());

  7. m_pBuffer = new SapBuffer(2, m_pAcq);

  8. m_pView = new SapView(m_pBuffer, GetDlgItem(IDC_STATIC_VIEW)->GetSafeHwnd());

  9. m_pTransfer = new SapTransfer(XferCallback, this);

  10. m_pTransfer->AddPair(SapXferPair(m_pAcq, m_pBuffer));

  11. }

  12. else

  13. {

  14. // Define off-line objects

  15. m_pBuffer = new SapBuffer();

  16. }

  17. m_pAcq->Create();

  18. m_pBuffer->Create();

  19. m_pTransfer->Create();

  20. m_pView->Create();

这里用到了官方的GUI配置对话框CAcqConfigDlg,而且开启了两个缓存区,这是因为在连续采集的过程中,如果要读取图像需要时间,这时候只有一个缓冲区,该缓冲区的前半部分将会被新采集转移的覆盖,而开启两个缓冲区,可以将新采集图像数据的转移到另一个缓冲区,循环错开就能避免这个问题。

重点就是回调函数的写法了。上一个代码区主要是用来显示新采集的图像。

我重写个及时保存图像的代码

 
  1. void CDalsaCameraDlg::XferCallback( SapXferCallbackInfo *pInfo )

  2. {

  3. CDalsaCameraDlg* pDlg = (CDalsaCameraDlg*)(pInfo->GetContext());

  4. int pitch = pDlg->m_pBuffer->GetPitch();

  5. // Get the buffer data address

  6. BYTE pData;

  7. void* pDataAddr = &pData;

  8. bool success = pDlg->m_pBuffer->GetAddress(staticCount, &pDataAddr);

  9. int width = pDlg->m_pBuffer->GetWidth();

  10. int height = pDlg->m_pBuffer->GetHeight();

  11. Mat img = Mat::zeros(cv::Size(width, height), CV_8U);

  12. memcpy(img.data, pDataAddr, width*height);

  13. if (staticCount== 0)

  14. {

  15. imwrite("C:\\123.bmp", img);

  16. staticCount = 1;

  17. }

  18. else (staticCount == 1)

  19. {

  20. imwrite("C:\\456.bmp", img);

  21. staticCount = 0;

  22. }

  23. success = pDlg->m_pBuffer->ReleaseAddress(pDataAddr);

  24. }

由于函调函数只能是静态成员函数,因此无法调用非静态成员函数和非静态成员变量,我们又需要读取缓冲区的内容,因此只能将整个类的指针传递到回调函数中来,并重新生成了这个类(这个有点像太乙真人用莲藕重造了哪吒的感觉)

这样就能操纵采集到的缓冲区类SapBuffer了,利用getAddress获取图像数据的首地址,这里用了

BOOL GetAddress(int index, void** pData);

其中index为缓冲区的序号,这样控制序号就可以交替读取两个缓冲区的数据内容了。每采集到一个完整图像之后读取一个缓冲区,而这段时间内新采集的图像存储在下一个缓冲区,数据之间不会存在覆盖的问题,而且能及时读出图像,保存在硬盘里。

这里使用了OpenCV图像库来保存图像,主要是windows自带的Bitmap不会(囧)。

最后程序终结直接记得释放新开辟的指针变量

 
  1. void CDalsaCameraDlg::OnDestroy()

  2. {

  3. CDialogEx::OnDestroy();

  4. // TODO: 在此处添加消息处理程序代码

  5. // Release and free resources for SapBuffer object

  6. if (nullptr != m_pView)

  7. {

  8. m_pView->Destroy();

  9. delete m_pView;

  10. }

  11. if (nullptr != m_pTransfer)

  12. {

  13. m_pTransfer->Destroy();

  14. delete m_pTransfer;

  15. }

  16. if (nullptr != m_pBuffer)

  17. {

  18. m_pBuffer->Destroy();

  19. delete m_pBuffer;

  20. }

  21. if (nullptr != m_pAcq)

  22. {

  23. m_pAcq->Destroy();

  24. delete m_pAcq;

  25. }

  26. }

最好是按照开辟的顺序反过来一一释放,原因在于如SapTransfer是依赖于绑定的Acquisition和Buffer的,倘若先释放销毁掉Acquisition和Buffer,再销毁SapTransfer时欲解除与Acquisition和Buffer的联系时,发现已经找不到这两位了。

希望赶紧做我的毕设,这个Dalsa相机的开发到此结束。

DALSA线阵CCD开发纪要(C++)-- 缓冲区读相关推荐

  1. DALSA线阵CCD开发纪要(C++)

    应用背景:晶体表面疵病工业检测,导轨运动的光栅尺反馈系统产生的脉冲用于外触发Dalsa相机进行图像采集. 解决问题:Dalsa线阵CCD直接采集的图像是当前一行的图像,配套的采集卡中用于存储图像的缓冲 ...

  2. DALSA线阵CCD相机开发 之 OpenCV配置

    DALSA线阵CCD相机开发 之 OpenCV配置 如果出现以下情况: 在VS MFC中其他程序使用加载OpenCV没有问题. 在dalsa中可以包含头文件,并且代码输入时有提示OpenCV的函数,同 ...

  3. DALSA线阵CCD相机开发 之 opencv读取图片

    SDK的下载 Sapera_LT_8.31_SDK 百度网盘下载地址:链接:密码:ckm7. SDK的安装 解压运行.exe文件,按照提示确定,下一步即可. 帮助文档和demo 如果要获得完整的帮助文 ...

  4. 基于FPGA的线阵CCD实时图像采集系统

    基于FPGA的线阵CCD实时图像采集系统 2015年微型机与应用第13期 作者:章金敏,张 菁,陈梦苇 2016/2/8 20:52:00 关键词: 实时采集 电荷耦合器件 现场可编程逻辑器件 信号处 ...

  5. LCAMV06-TCD1304线阵CCD模组

    LCAMV06-TCD1304线阵CCD模组 LCAMV06系列是我司自主研发的低噪声线阵CCD模组 ,搭配TCD1304 sensor,电路采用了16bit专业ccd处理器,有TTL接口和Micro ...

  6. 线阵CCD FPGA CCD测量 直径测量 FPGA代码 CCD光学传感器 TCD1501

    线阵CCD FPGA CCD测量 直径测量 FPGA代码 CCD光学传感器 TCD1501,自制USB接口线阵CCD驱动板及核心控制电路板四层单板,包括FPGA线阵CCD驱动程序&STM32单 ...

  7. 线阵CCD相机与面阵相机的区别

    一.线阵相机 线阵CCD工业相机主要应用于工业.医疗.科研与安全领域的图象处理.在机器视觉领域中,线阵工业相机是一类特殊的视觉机器.与面阵工业相机相比,它的传感器只有一行感光元素,因此使高扫描频率和高 ...

  8. 线阵CCD(摄像机)与面阵CCD(摄像机)的区别

    (1)线阵CCD图像传感器是由一列MOS(金属-氧化物-半导体)光敏单元和一列CCD移位寄存器并行而构成的.如果要用线阵CCD获取二维图像,必须配以扫描运动,而且为了能确定图像每一像素点在被测件上的对 ...

  9. 利用STM32的定时器中断功能编写线阵CCD(ILX554B)的驱动时序

    利用STM32的定时器中断功能编写线阵CCD(ILX554B)的驱动时序 1.利用定时器完成线阵CCDILX554B的驱动时序,采用两个管脚1个用于产生ROG信号,一个用于产生CLK信号 2.使用一个 ...

最新文章

  1. vector容器 begin()与end()函数、front()与back()的用法
  2. 跟我学习php文件和目录常用函数-下篇
  3. std::cout char + int
  4. 各种卡的一些信息积累
  5. Entity Framework中的字符串插值引发担忧
  6. k8s的认证和service account简述
  7. MyBatis 延迟加载的三种加载方式深入,你get了吗?
  8. Style后台动态定义[转]
  9. 关于CityEngine导入shp数据
  10. JanusGraph快速入门
  11. 如何在搜狗高速浏览器设置代理IP
  12. html5 css3冷色调渐变色特效
  13. 弘辽科技:淘宝流失率是什么意思?客户流失的原因有哪些?
  14. 【AWS云从业者基础知识笔记】——模块3:全球基础设施和可靠性
  15. 特别策划:非计算机专业如何转行做程序员?
  16. statement的意思和用法
  17. 一文让你了解RFID标签芯片厂家有哪些?
  18. 共享充电宝PCBA方案开发设计
  19. TRS复合矩阵的拆解
  20. 计算机为什么经常更新,电脑老是配置更新怎么办

热门文章

  1. python读取pdf表格_[转]Python 解析 PDF 文本和表格的四大方法介绍
  2. LeapMotion Demo2
  3. 关于松下A6伺服的再生电阻问题
  4. ModuleNotFoundError: No module named ‘docx‘
  5. 计算机与日常使用计算器的本质区别,计算机和计算器的区别
  6. 基于python绘制ROC曲线
  7. 青蛙上楼梯matlab,一个小青蛙,可以一次跳两节楼梯,也可以一次跳一节楼梯,请问他如果要跳101节楼梯,一共有几种跳法方案? - 菲波那切数列...
  8. 做3D建模师工资到底如何?
  9. 平面图转换为2.5D(二)
  10. 自学STC32G12K128单片机总结——1.点灯