Pylon C++ Low Level API
注意:
Low Level API应仅用于现有应用程序以及使用Instant Camera类无法解决问题的少数高级用例。
请尽可能使用Instant Camera类而不是Low Level API。
Low Level Camera Classes (deprecated)
Low Level API相机对象封装了一个pylon设备,通过使用GenApi参数类,为相机(Camera
)、流抓取器(Stream Grabber
)、事件抓取器(Event Grabber
)和传输层(Transport Layer
)的参数提供了更便利的访问方式。
下表显示了当前可用的类:
Transport Layer | Name of Class | Kind of Devices |
---|---|---|
PylonGigE | Pylon::CBaslerGigECamera | GigE Vision compliant cameras |
PylonUsb | Pylon::CBaslerUsbCamera | USB3 Vision compliant cameras |
PylonCLSer | Pylon::CBaslerCameraLinkCamera | Serial Camera Link cameras |
Grabbing Images
Terminology
Acquire, Transfer, and Grab Images
在本文档中,我们区分图像采集、图像数据传输和图像抓取三个过程。
- 我们将相机内部的过程称为图像采集。当相机开始图像采集时,传感器会被曝光。曝光完成后,图像数据从传感器中读出。
- 采集的图像数据通过如USB或千兆以太网等接口从相机的内存传输到计算机。
- 将图像数据写入计算机主内存的过程称为“抓取”图像。
Image Data Stream and Stream Grabber
一个相机可能提供不同的图像数据来源,每个来源都可以传送一个图像数据流。在pylon中,所谓的Stream Grabber对象负责管理从数据流中抓取数据的过程,即将数据写入计算机的主内存。
一个Stream Grabber只从一个数据流中抓取图像。要从多个数据流中抓取数据,需要多个Stream Grabber。
Paw5zx注:
A camera may provide different sources for image data, where each source can deliver a stream of image data.
暂时不太理解一个相机中的多个不同的sources指的是什么。
Using Stream Grabbers
以下部分将描述Stream Grabber对象的使用。本节的介绍顺序反映了一个典型抓取应用程序使用Stream Grabber对象的顺序。
Getting a Stream Grabber
Stream Grabber对象由Camera对象管理。可以通过相机对象的IPylonDevice::GetNumStreamGrabberChannels()方法确定可用的Stream Grabber数量。IPylonDevice::GetStreamGrabber()方法返回指向Pylon::IStreamGrabber对象的指针。在检索Stream Grabber对象之前,必须先打开Camera对象。您应该检查IPylonDevice::GetNumStreamGrabberChannels()返回的值,以确定在调用IPylonDevice::GetStreamGrabber()时可以使用哪个索引参数。Stream Grabber对象本身在使用前也必须被打开。一些相机对象,例如Camera Link,可能不支持Stream Grabbers,并在调用IPylonDevice::GetNumStreamGrabberChannels()时返回0。
示例:
1 | camera.Open(); |
注意:
永远不要尝试对从Camera对象检索到的Stream Grabber指针调用delete或free。Camera对象保留对Stream Grabber对象的所有权,并管理其生命周期。
Configuring a Stream Grabber
独立于所使用的传输层,每个Stream Grabber都提供两个必需的参数:
MaxBufferSize
:用于抓取图像的缓冲区的最大字节数MaxNumBuffer
:用于抓取图像的缓冲区的最大数量
抓取应用程序必须在抓取开始之前设置上述两个参数。
根据传输层的不同,Stream Grabber提供进一步的参数,例如与流相关的超时参数。所有这些参数都被设置为默认值,可以在不调整默认设置的情况下执行图像抓取。
访问Stream Grabber参数有两种方式:
最舒适的方式是使用Stream Grabber对象的具体类。每个Camera类提供了对应的Stream Grabber类的typedef
。Stream Grabber类拥有通过GetStreamGrabber()方法返回的IStreamGrabber指针的所有权。Stream Grabber类具有访问Stream Grabber对象参数的成员。
示例:
1 | camera.Open(); |
当使用泛型编程方法(generic programming approach
),即使用Pylon::IPylonDevice和Pylon::IStreamGrabber接口而不是Camera和Stream Grabber类时,必须使用IStreamGrabber::GetNodeMap()方法检索持有Stream Grabber参数的GenApi节点图(node map
)。
Stream Grabber节点图的使用方式与Camera对象的节点图相同。Camera对象的节点图的使用在Accessing Parameters部分有描述。
Preparing a Stream Grabber for Grabbing
根据用于抓取图像的传输层,所需的系统资源不同,例如:
- DMA资源
- 存储驱动程序数据结构的内存
Stream Grabber的PrepareGrab()方法用于分配所需资源。
除了资源分配外,调用PrepareGrab()还会导致相机对象执行状态更改。通常,在调用PrepareGrab()后,控制图像大小的相机参数(AOI、像素格式、二次采样等)将变为只读。这些参数必须在调用PrepareGrab()之前设置,并且在图像抓取活动期间不得更改。
Providing Memory for Grabbing
所有的pylon传输层都可以将图像数据抓取到由用户应用程序分配的内存缓冲区中。用户分配的内存缓冲区必须在Stream Grabber对象上注册(使用IStreamGrabber::RegisterBuffer())。处于性能原因,需要注册步骤,注册过程允许Stream Grabber准备并缓存用于处理用户提供的内存的内部数据结构。
缓冲区注册过程返回已注册缓冲区的句柄,这些句柄将在缓冲区注册后的步骤中使用。
示例:
1 | StreamGrabber.Open(); |
缓冲区注册机制限制了缓冲区的所有权。虽然已注册缓冲区的内容可以由用户应用程序更改,但应用程序不得删除已注册的内存缓冲区的内存。只有在使用IStreamGrabber::DeregisterBuffer()注销缓冲区后,才允许释放内存。
Feeding the Stream Grabber’s Input Queue
每个Stream Grabber维护两个不同的缓冲区队列。待填充的缓冲区必须被送入Grabber的输入队列。抓取(被抓取到的图像数据填充)的缓冲区可以从Grabber的输出队列中检索。
IStreamGrabber::QueueBuffer()方法用于将一个缓冲区放入Grabber的输入队列。QueueBuffer()方法接受两个参数,一个缓冲区句柄和一个可选的,用户提供的指向用户提供的上下文信息的指针。当从Grabber的输出队列检索抓取结果时,上下文指针与缓冲区一起被传回给用户。Stream Grabber永远不会改变上下文指针所指向的内存。
示例:
1 | MyContext context[numBuffers]; |
注意:
Stream Grabber临时拥有排队缓冲区的所有权。当缓冲区被放入Stream Grabber的输入队列时,永远不要尝试修改或删除缓冲区。
注意:
将缓冲区加入到Stream Grabber的输入队列并不会使相机开始采集图像!在缓冲区入队之后,Stream Grabber准备好从相机抓取数据到队列的缓冲区中。图像采集必须显式启动。
Starting and Stopping Image Acquisition
要启动图像采集,请使用Camera对象的Pylon::CBaslerGigECamera::AcquisitionStart参数。Pylon::CBaslerGigECamera::AcquisitionStart是一个命令参数,即,调用Pylon::CBaslerGigECamera::AcquisitionStart参数的Execute()方法会向相机发送一个开始采集的命令。
相机设备通常提供两种采集模式:
- 单帧模式:相机采集一张图像。
- 连续模式:相机持续不断地采集和传输图像,直到停止采集。
准确地说,开始采集的命令并不一定立即启动相机内部的采集。当配置为外部触发或软件触发时,开始采集的命令会让相机准备进行图像采集。实际的采集是在相机感应到外部触发信号或收到软件触发命令时开始的。
当启用相机的连续采集模式时,使用Pylon::CBaslerGigECamera::AcquisitionStop参数来停止图像采集。
通常,相机会在开始采集后尽可能快地开始传输图像,不需要特殊命令来启动图像传输。
示例:
1 | using namespace Basler_GigECameraParams; |
Retrieving Grabbed Images
传输到PC的图像数据被写入到Stream Grabber输入队列中的缓冲区。当缓冲区被抓取的图像数据填满时,Stream Grabber会将其放入输出队列,用户应用程序可以从中检索到被填满的缓冲区。
有一个等待对象与Stream Grabber的输出队列关联。这个等待对象允许应用程序等待,直到抓取的图像到达输出队列或超时过期。
当等待操作成功返回时,可以使用Stream Grabber对象的RetrieveResult()方法返回抓取的缓冲区。RetrieveResult()方法填充一个Pylon::GrabResult对象。该对象包含以下信息:
- 抓取的状态(成功、取消、失败)
- 缓冲区的句柄
- 指向缓冲区的指针
- 用户提供的上下文指针
- AOI和图像格式
- 如果抓取失败了,则提供错误号和错误描述
从Stream Grabber的输出队列获取缓冲区时,缓冲区的所有权转移给应用程序。从输出队列检索到的缓冲区不会被覆写,直到它再次被放入Stream Grabber的输入队列。
请记住,从输出队列检索的缓冲区必须在其内存被释放前取消注册(先注销再释放)。
我们建议使用Grab Result对象中的缓冲区句柄将缓冲区重新入队到Stream Grabber的输入队列。
当相机不发送数据时,缓冲区将一直留在Stream Grabber的输入队列中,直到调用Stream Grabber对象的CancelGrab()方法。CancelGrab()将输入队列中的所有缓冲区放到输出队列,包括任何当前正在填充的缓冲区。通过检查Grab Result对象的状态,您可以确定一个缓冲区是否已被取消。
以下示例展示了一个典型的抓取循环:
1 | const int numGrabs = 100; |
Finish Grabbing
如果相机设置为连续采集模式,首先应该停止采集:
1 | camera.AcquisitionStop.Execute(); |
如果您不确定Stream Grabber的输入队列是否真的为空,应该使用Stream Grabber对象的CancelGrab()方法来清空输入队列。之后,被取消的缓冲区在Stream Grabber的输出队列中可用。
应用程序应在关闭Stream Grabber对象之前,从Stream Grabber的输出队列中检索所有条目。
先注销缓冲区,再释放他们的内存。
当所有缓冲区都已取消注册后,调用Stream Grabber对象的FinishGrab()方法以释放与抓取相关的所有资源。当Stream Grabber的输入队列中还有缓冲区时,不能调用FinishGrab()!
当抓取完成后,应该关闭Stream Grabber对象。
示例:
1 | // The camera is in continuous mode, stop the image acquisition |
Complete Sample program
下面是在连续模式下从GigE相机获取图像的完整示例程序:
1 |
|
Handling Camera Events
Basler GigE Vision和USB3 Vision相机可以发送事件消息。例如,当传感器曝光结束时,相机可以向计算机发送曝光结束事件。在完成曝光的图像数据完全传输之前,计算机可以接收到该事件。本节描述了如何检索和处理事件消息。
Event Grabbers
Grabbing Images部分描述了如何使用Stream Grabber对象从相机抓取图像。类似地,Event Grabber对象用于从相机接收事件消息。
Creating and Preparing Event Grabbers
Event Grabber对象由Camera对象创建并返回
1 | // Get the event grabber |
永远不要尝试对IEventGrabber指针调用free
或delete
。Camera对象拥有Event Grabbers并管理它们的生命周期。
Event Grabbers使用内部内存缓冲区来接收事件消息。可以使用Event Grabber的Pylon::CPylonGigEEventGrabber::NumBuffer成员来设置缓冲区的数量:
1 | EventGrabber.NumBuffer.SetValue(20); |
注意:
在调用Event Grabber的Open()方法之前,必须参数化缓冲区的数量!
设备的连接和接收事件所需的所有资源可以通过调用Event Grabber的Open()方法分配:
1 | EventGrabber.Open(); |
Enabling Events
要让相机发送事件消息,必须使用Camera对象启用事件消息的发送。
首先,Pylon::CBaslerGigECamera::EventSelector必须设置为要启用的事件类型。在下面的示例中,选择器被设置为曝光结束事件:
1 | // Select the Exposure End event |
设置事件选择器后,可以使用Pylon::CBaslerGigECamera::EventNotification参数启用所需类型的事件发送:
1 | // Enable sending of events of the selected event type |
为确保您不会错过任何事件,在启用事件之前应准备事件抓取器(见上文的Creating and Preparing Event Grabbers)。
以下代码片段说明如何禁用发送曝光结束事件:
1 | // Select the Exposure End event |
Receiving Events
接收事件与抓取图像的非常相似。Event Grabber提供了一个等待对象,当有事件消息可用时会被触发。当事件消息可用时,可以通过调用Event Grabber的RetrieveEvent()方法来检索它。
与抓取图像不同,接收事件的内存缓冲区不需要由应用程序提供。存储事件消息的内存缓冲区由Event Grabber本身管理。
在典型的应用程序中,等待抓取的图像和事件消息是在一个循环中完成的。以下代码片段演示了这一点:
1 | // Add the stream grabber's and the event grabber's wait objects to a container |
Parsing and Dispatching Events
上一节解释了如何接收事件消息。本节描述如何解析事件消息。
事件消息的具体布局取决于事件类型和相机类型。pylon API使用GenICam支持来解析事件消息。这意味着消息布局在相机的XML文件中描述。
如GenApi Node Maps部分所述,一个GenApi节点图是从XML相机描述文件创建的。该节点图包含代表XML文件元素的节点对象。由于事件消息的布局在相机描述文件中有描述,事件消息携带的信息作为节点在节点图中暴露出来。相机对象提供用于访问与事件相关的节点的成员,方式与访问相机参数相关的节点相同。
例如,一个曝光结束事件携带以下信息:
ExposureEndEventFrameID
:表示已曝光的图像帧的编号ExposureEndEventTimestamp
:表示生成事件的时刻ExposureEndEventStreamChannelIndex
:表示用于传输已曝光帧的图像数据流的编号
示例:使用相机对象的Pylon::CBaslerGigECamera::ExposureEndEventFrameID成员来访问事件关联的帧的编号:
1 | int64_t frameNr = camera.ExposureEndEventFrameID.GetValue(); |
如Accessing Parameters部分所述,也可以通过直接使用相机对象的节点图来检索ExposureEndEventFrameID
:
1 | GenApi::INodeMap* pNodeMap = pCamera->GetNodeMap(); |
一个Event Adapter对象用于更新相机对象节点图中与事件相关的节点。通过将事件消息传递给Event Adapter来更新节点。
Event Adapter由相机对象创建:
1 | Pylon::IEventAdapter *pEventAdapter = camera.CreateEventAdapter(); |
要更新与事件相关的节点,对每个接收到的事件消息调用Event Adapter的DeliverMessage()方法:
1 | // Retrieve the event result |
仅通过将事件消息传递给Event Adapter,无法确定消息是否包含曝光结束事件。下一节描述如何使用节点回调来了解特定事件的发生情况。
Event Callbacks
上一节描述了Event Adapter如何用于将事件消息的内容推送到相机对象的节点图中。IEventAdapter::DeliverMessages()方法更新传入消息中包含的与事件相关的所有节点。
如Getting Informed About Parameter Changes所述,可以注册回调函数,当节点可能发生变化时触发这些函数。
这些回调可以用来确定事件消息是否包含某种特定的事件类型。例如,要获取关于曝光结束事件的通知,必须为曝光结束事件相关的节点之一注册回调。以下代码片段演示了如何为ExposureEndFrameId
节点注册回调函数:
1 | // Member function of this class will be registered as callback |
注册的回调将从IEventAdapter::DeliverMessage()函数的上下文中触发。
注意:
由于一个事件消息可以聚合多个事件,DeliverMessage
在同一类型的多个事件可用时将对回调函数发起多次调用。
Cleanup
在关闭和删除Camera对象之前,必须关闭和销毁与事件相关的对象,如下面的代码片段所示:
1 | // Disable sending of Exposure End events |
Chunk Parser: Accessing Chunk Features
Basler相机可以将额外信息附加到图像数据中,例如帧计数器、时间戳和CRC校验和。本节将解释如何启用块特征以及如何访问附加的数据。
Enabling Chunks
在激活生成块数据的特征之前,必须开启相机的块模式(chunk mode
)
1 | // Open the camera |
当相机处于块模式时,它会传输被划分为多个块的数据块。第一个块总是图像数据。当启用块特征后,图像数据块之后会跟随包含由块特征生成的信息的块。
一旦块模式被激活,就可以启用块特征:
1 | camera.ChunkSelector.SetValue( ChunkSelector_Timestamp ); |
Grabbing Buffers
从带有块的图像流抓取与从不带块的图像流抓取非常相似。必须提供足够大的内存缓冲区,以存储图像数据和附加的块数据。
相机的PayloadSize
参数反映所需的缓冲区大小(以字节为单位):
1 | // Ask for the buffer size |
现在一个图像和其附加的块可以被抓取:
1 | // Allocate resources related to grabbing |
Accessing the Chunk Data
包含图像块和其他块的数据块具有自描述的布局。在访问添加的块中的数据之前,必须使用Chunk Parser对象解析数据块。
相机对象负责创建Chunk Parser:
1 | // Create ChunkParser |
一旦创建了Chunk Parser,抓取的缓冲区可以绑定到Chunk Parser上。当缓冲区绑定到Chunk Parser时,它将被解析,块数据访问由相机对象的成员提供。
1 | // Attach image buffer with chunk data to the parser. The parser extracts |
要检查CRC校验和块特征的结果,请使用Chunk Parser的HasCRC()和CheckCRC()方法。注意,相机只在启用CRC校验和特征后才会发送CRC。
1 | // Enable crc chunks ( before PrepareGrab()! ) |
在重用缓冲区进行抓取之前,必须将缓冲区从Chunk Parser中分离。
1 | // After detaching the buffer, the chunk data is no longer accessible! |
分离缓冲区后,下一个抓取的缓冲区可以附加并读取包含的块数据。
当你完成抓取后,必须删除Chunk Parser:
1 | // Destroy the chunk parser |
Getting Informed About Device Removal
可以注册回调函数,当相机设备被移除时触发。一旦调用了相机对象的Open()方法,就可以注册C函数或C++类成员函数作为回调。
注册一个C函数:
1 | void RemovalCallbackFunction( IPylonDevice* pDevice ) |
注册一个C++类成员函数:
1 | // A class with a member function that can be registered for device removal notifications |
所有注册的回调在调用相机对象的Close()方法之前必须注销
1 | if ( ! pCamera->DeregisterRemovalCallback( hCb1 ) ) |
- 标题: Pylon C++ Low Level API
- 作者: paw5zx
- 创建于 : 2024-11-24 20:49:00
- 更新于 : 2024-11-26 13:49:11
- 链接: https://paw5zx.github.io/pylon-cpp-low-level-api/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。