DALSA线阵CCD开发纪要(C++)
来源:互联网 发布:在线赌博网站源码 编辑:程序博客网 时间:2024/05/22 11:54
应用背景:晶体表面疵病工业检测,导轨运动的光栅尺反馈系统产生的脉冲用于外触发Dalsa相机进行图像采集。
解决问题:Dalsa线阵CCD直接采集的图像是当前一行的图像,配套的采集卡中用于存储图像的缓冲区有限,当平台连续长距离运动时,如果不及时读取缓冲区的图像,新采集的图像将覆盖之前采集的图像。
阅读Dalsa相机的开发文档中的继承图,如下:
我们最为关心的是缓冲区的内容SapBuffer和将采集内容转运到缓冲区的SapAcqToBuf,细心一点的话还能看到采集内容转运到缓冲区的回调函数的Info。
查看官方提供的一些开发Demo
// Transfer callback function is called each time a complete frame is transferred.// The function below is a user defined callback function.void XferCallback(SapXferCallbackInfo *pInfo){ // Display the last transferred frame SapView *pView = (SapView *) pInfo->GetContext(); pView->Show();}// Example program//main(){ // Allocate acquisition object SapAcquisition *pAcq = new SapAcquisition(SapLocation (“X64-CL_1”, 0), “MyCamera.ccf”); // Allocate buffer object, taking settings directly from the acquisition SapBuffer *pBuffer = new SapBuffer(1, pAcq); // Allocate view object, images will be displayed directly on the desktop SapView *pView = new SapView(pBuffer, SapHwndDesktop); // Allocate transfer object to link acquisition and buffer SapTransfer *pTransfer = new SapTransfer(XferCallback, pView); pTransfer->AddPair(SapXferPair(pAcq, pBuffer)); // Create resources for all objects BOOL success = pAcq->Create(); success = pBuffer->Create(); success = pView->Create(); success = pTransfer->Create(); // Start a continuous transfer (live grab) success = pTransfer->Grab(); printf("Press any key to stop grab\n"); getch(); // Stop the transfer and wait (timeout = 5 seconds) success = pTransfer->Freeze(); success = pTransfer->Wait(5000); printf("Press any key to terminate\n"); getch(); // Release resources for all objects success = pTransfer->Destroy(); success = pView->Destroy(); success = pBuffer->Destroy(); success = pAcq->Destroy(); // Free all objects delete pTransfer; delete pView; delete pBuffer; delete pAcq; return 0;}不难发现:
首先需要建立Acquisition,这里有设备信息,需要采集的图像信息和设置(如图像宽度,高度),亦可通过官方SDK自带的GUI对话框读取配置文件很快键地得到。
其次建立我们想要的缓冲区,由于我们的基本配置信息已经通过Acquisition得到,因此在SapBuffer的众多重载函数中选取了
SapBuffer(
int count,
SapXferNode* pSrcNode,
SapBuffer::Type type = SapBuffer::TypeScatterGather,
SapLocation loc = SapLocation::ServerSystem
);
其中count为缓冲区的数目,它们显然具有同配置文件中图像的大小,数据格式。SapXferNode为SapAcquisition的父类,直接传递Acquisition的指针即可,后面采用默认的参数。
(接下来是用于显示图像的SapView类,将缓冲区与用于显示的控件的窗口句柄绑定起来,就可以将图像显示在指定的控件上。这个与采集过程关系不大,但可视化这个,大家懂得。)
接下来是比较重要的步骤,建立了从图像数据采集到缓冲区的转移步骤。由于获取完整图像(理想情况下图像高度设置在80000,但缓冲区大小有限,设置在30000)时间较长,因此有足够的时间将缓冲区数据读取出来,不存在采集的速率高于转移的速率,用不上垃圾缓存区,因此使用下面的函数
SapTransfer(
SapXferCallback pCallback = NULL,
void* pContext = NULL,
SapLocation loc = SapLocation::ServerUnknown
);
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,使用默认的创建方法就好,其他定制化的方法没必要,也不会。
仿照着写了一个初始化的代码
// TODO: 在此添加额外的初始化代码CAcqConfigDlg dlg(this, NULL);if (dlg.DoModal() == IDOK){// Define on-line objectsm_pAcq= new SapAcquisition(dlg.GetAcquisition());m_pBuffer= new SapBuffer(2, m_pAcq);m_pView= new SapView(m_pBuffer, GetDlgItem(IDC_STATIC_VIEW)->GetSafeHwnd());m_pTransfer = new SapTransfer(XferCallback, this);m_pTransfer->AddPair(SapXferPair(m_pAcq, m_pBuffer));}else{// Define off-line objectsm_pBuffer= new SapBuffer();}m_pAcq->Create();m_pBuffer->Create();m_pTransfer->Create();m_pView->Create();
这里用到了官方的GUI配置对话框CAcqConfigDlg,而且开启了两个缓存区,这是因为在连续采集的过程中,如果要读取图像需要时间,这时候只有一个缓冲区,该缓冲区的前半部分将会被新采集转移的覆盖,而开启两个缓冲区,可以将新采集图像数据的转移到另一个缓冲区,循环错开就能避免这个问题。
重点就是回调函数的写法了。上一个代码区主要是用来显示新采集的图像。
我重写个及时保存图像的代码
void CDalsaCameraDlg::XferCallback( SapXferCallbackInfo *pInfo ){CDalsaCameraDlg* pDlg = (CDalsaCameraDlg*)(pInfo->GetContext());int pitch = pDlg->m_pBuffer->GetPitch();// Get the buffer data addressBYTE pData;void* pDataAddr = &pData;bool success = pDlg->m_pBuffer->GetAddress(staticCount, &pDataAddr);int width = pDlg->m_pBuffer->GetWidth();int height = pDlg->m_pBuffer->GetHeight();Mat img = Mat::zeros(cv::Size(width, height), CV_8U);memcpy(img.data, pDataAddr, width*height);if (staticCount== 0){imwrite("C:\\123.bmp", img);staticCount = 1;}else (staticCount == 1){imwrite("C:\\456.bmp", img);staticCount = 0;}success = pDlg->m_pBuffer->ReleaseAddress(pDataAddr);}由于函调函数只能是静态成员函数,因此无法调用非静态成员函数和非静态成员变量,我们又需要读取缓冲区的内容,因此只能将整个类的指针传递到回调函数中来,并重新生成了这个类(这个有点像太乙真人用莲藕重造了哪吒的感觉)
这样就能操纵采集到的缓冲区类SapBuffer了,利用getAddress获取图像数据的首地址,这里用了
BOOL GetAddress(int index, void** pData);
其中index为缓冲区的序号,这样控制序号就可以交替读取两个缓冲区的数据内容了。每采集到一个完整图像之后读取一个缓冲区,而这段时间内新采集的图像存储在下一个缓冲区,数据之间不会存在覆盖的问题,而且能及时读出图像,保存在硬盘里。
这里使用了OpenCV图像库来保存图像,主要是windows自带的Bitmap不会(囧)。
最后程序终结直接记得释放新开辟的指针变量
void CDalsaCameraDlg::OnDestroy(){CDialogEx::OnDestroy();// TODO: 在此处添加消息处理程序代码// Release and free resources for SapBuffer objectif (nullptr != m_pView){m_pView->Destroy();delete m_pView;}if (nullptr != m_pTransfer){m_pTransfer->Destroy();delete m_pTransfer;}if (nullptr != m_pBuffer){m_pBuffer->Destroy();delete m_pBuffer;}if (nullptr != m_pAcq){m_pAcq->Destroy();delete m_pAcq;}}最好是按照开辟的顺序反过来一一释放,原因在于如SapTransfer是依赖于绑定的Acquisition和Buffer的,倘若先释放销毁掉Acquisition和Buffer,再销毁SapTransfer时欲解除与Acquisition和Buffer的联系时,发现已经找不到这两位了。
希望赶紧做我的毕设,这个Dalsa相机的开发到此结束。
- DALSA线阵CCD开发纪要(C++)
- DALSA相机开发记录(01)
- 孟-摘取(Dalsa)
- 开发纪要
- 【DALSA Coreco - Sapera LT】开发步骤
- dalsa 8k线阵网口相机c#开发
- C语言纪要
- SyncML开发纪要
- 开发大概流程纪要
- android NDK 开发纪要
- Django项目开发纪要
- CCD
- CCD
- CCD
- CCD
- MISRA-C:2004培训纪要
- javascript开发纪要,浏览器差异
- java开发平台搭建纪要:
- Android自定义控件系列五:自定义ViewGroup(一)实现ViewPager效果
- ip地址后边加个/8(16,24,32)是什么意思?
- C/C++/C#问题记录(二)获取ComboBox中SelectedItem的值
- 吾生也有涯,而知也无涯!
- SQL语句中使用回车换行符
- DALSA线阵CCD开发纪要(C++)
- JDBC各数据库的加载驱动和c3p0的设置
- 百度校园招聘开发岗位回忆
- 第七周项目 4 String类的构造
- 徐汉彬:大规模网站架构的缓存机制和几何分形学
- java分解质因数
- Unity3d 真实的植物渲染
- 4.17
- How to Change the Default Boot Device by Using the Boot PROM