1.1使用Vimba控制相机需要经历以下几个步骤。
1.打开Vimba
2.查找相机列表
3.打开特定相机
4.配置参数(可选)
5.采集 :采集过程共分为5步,具体见1.2
6.关闭相机
7.关闭Vimba
1.2采集的步骤:
1.准备图像采集 : 让Vimba知道缓冲区(camera.AnnounceFrame()) -> 启动捕获引擎(camera.StartCapture()) -> 将缓冲区交给Vimba(camera.QueueFrame())
2.开始图像采集 :运行相机命令功能(AcquisitionStart)
3.图像在回调函数内:帧入队列(camera.QueueFrame())
4.停止图像采集:运行相机命令功能(AcquisitionStop)
5.清理释放:停止捕获引擎(camera.EndCapture()) -> 刷新捕获队列(camera.FlushQueue()) -> 撤销所有的帧(camera.RevokeAllFrames())
CPP语言强调封装和继承,所以SDK以类的形式提供给开发者进行调用,其中比较重要的类有以下:
2.1 VimbaSystem类
重要成员函数(完整函数列表请参考VimbaCPP手册)
Startup() 启动Vimba
ShutDown() 关闭Vimba
GetCameras() 获得相机列表
GetCameraByID() 通过ID获得具体的某一个相机
OpenCameraByID() 通过ID打开具体的某一个相机
RegisterCameraListObserver() 注册相机观察者
UnregisterCameraListObserver() 取消相机观察者
2.2 Camera类
Open() 打开相机
Close() 关闭相机
GetFeatures() 获得Feature列表
GetFeatureByName() 根据名称获得特定的Feature
AnnounceFrame() 声明Frame
RevokeFrame() 抛弃Frame
QueueFrame() Frame进队列
FlushFrame() 冲洗Frame
2.3 Feature类
GetValue() 获得Feature的值
SetValue() 设置Feature的值
RunCommand() 执行Feature命令
RegisterObserver() 注册观察者
UnregisterObserver() 取消观察者
2.4 Frame类
GetImage() 获得图像数据
RegisterObserver() 注册观察者
关于观察者Observer的说明,可以看到在VimbaSystem,Feature和Frame类中,都有所谓的观察者方法,观察者的作用是,当相机(硬件)发生改变时,会通知SDK所发生的变化,比如相机插入和拔出,会通过VimbaSystem::RegisterCameraListObserver()获取,当相机的Feature改变时,会通过Feature::RegisterObserver()来通知SDK,当一帧数据到达时,会通过Frame::RegisterObserver()来通知SDK,具体实现的机理类似硬件中断,开发者可以不必考虑,只需知道如何使用即可。
所有和相机相关的类,都封装在AVT::VmbAPI::Examples命名空间下,打开后是这样的结构:
可以看到这里并没有使用任何VimbaCPP提供的基础类,而是做了一定的封装,封装在了ApiController类中。
在这个类里,第一个重要的成员就是m_system,这个成员就是前面提到的VimbaSystem实例后的一个对象,在应用程序中有且只有一个。
另外m_pCamera是指向Camera类的一个指针,我们在后面要操作的相机,也会被赋予这个指针变量。
m_pFrameObserver是一个执行那个前面提到的在Frame类里的观察者,通过这个观察者,SDK可以获得帧到达的消息:
其他几个成员比较简单,不一一解释。
1.构造函数
初始化成员m_system,VmbaSystem::GetInstance返回一个指向VimbaSystem的引用,这个引用在一个程序中只存在一个,初始化完成后,m_system就是本程序中的VmbaSystem
2.Startup
在这里面,唯一做的工作就是注册相机列表观察者,注册完成后,当相机插入或者 拔出时,会自动调转到回调函数CameraListChanged()里面,这个函数的实现,在CameraObserver类里实现。
CameraObserver中只有一个函数CameraListChange(),其主要作用,发送消息给MFC的APP对象,这样在MFC界面中,就会更新相机的列表。
在CAsynchronousGrabDlg中定义了消息处理函数OnCameraListChanged(),专门用来接收和处理这个消息。
注:这两个是有关联的,CameraListChanged是响应Vimba事件的回调函数,当相机变化时,这个函数就会执行,但是MFC的消息循环并不知道发生了什么,所以在这个回调函数里,发消息给MFC,让界面做出相应。
这是将在每个接收帧上执行的回调函数,由API触发。
当接收到关于新帧的通知后,他就可有接收新帧,然后从内部队列中删除它。
清除内部(双缓存)帧队列
那在FrameReceived()里面都做了什么?
两个内容,一是把当前的数据帧(其实是帧的指针)压入一个队列中,注意互斥锁的用法;二是给MFC的APP对象发消息,告诉他有数据啦!
APP如何处理帧到达的消息,在Dlg中,OnFrameReady()是用来接收和处理帧到达信号的
第一步,还记得刚才进入队列的指向帧的指针吗,这里要把它取出来。有了这个指针,接下来就可以直接拿到图像数据了,这里使用了Frame类的GetImage()方法,这个方法把图像数据传递给了VmbUchar_t变量,其实就是uchar类型的,也就是图像数据的指针,指向8位无符号整形,把这个值赋值给Mat变量的data即可。然后那到数据后,就可以进行以后的图像处理了。
注:CameraObserver和FrameObserver了解功能即可,具体内部怎么实现就不用去管了。
当用户在界面中,选中了具体的相机,然后点击界面上的Start Image Acquisition 按钮时,下面的事件处理函数开始执行。
void CDefectdetectingDlg::OnBnClickedButtonStartstop()
{
// TODO: 在此添加控件通知处理程序代码
VmbErrorType err;
int nRow = m_ListBoxCameras.GetCurSel();
if (false == m_bIsStreaming) {
if (-1 < nRow) {
//start acquisition
err = m_ApiController.StartContinuousImageAcquisition(m_cameras[nRow]);
//set up image for MFC picture box
if (VmbErrorSuccess == err && NULL == m_Image) {
m_Image.Create(m_ApiController.GetWidth(),
-m_ApiController.GetHeight(),
NUMCOLORS * BIT_DEPTH);
m_ClearBackground = true;
}
Log(_TEXT("Start Acquisition..."),err);
m_bIsStreaming = VmbErrorSuccess == err;
}
else {
Log(_TEXT("Please Select a Camera..."));
}
}
else {
flag_pBuffer = false;
m_bIsStreaming = false;
//stop acquisition
err = m_ApiController.StopContinuousImageAcquisition();
m_ApiController.ClearFrameQueue();
if (NULL != m_Image) {
m_Image.Destroy();
}
Log(_TEXT("Stop Acquisition,,,"),err);
}
if (false == m_bIsStreaming) {
m_ButtonStartStop.SetWindowText(_TEXT("Start Image Acquisition..."));
}
else {
m_ButtonStartStop.SetWindowText(_TEXT("Stop Image Acquisition..."));
}
}
1.先获得相机列表。
2.判断相机当前状态
2.1开始连续图像采集
2.2设置MFC图像框的图像
2.3否则停止图像采集