Pylon C++ Low Level API

paw5zx Lv4

注意:

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
camera.Open();

// get the number of stream grabbers available
const unsigned int numGrabbers = camera.GetNumStreamGrabberChannels();

if (numGrabbers > 0) {
IStreamGrabber* pGrabber = camera.GetStreamGrabber(0);
pGrabber->Open();

// use the grabber
// ...

pGrabber->Close()
}

// ...

注意:

永远不要尝试对从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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
camera.Open();
if (camera.GetNumStreamGrabberChannels() == 0) {
// device doesn't support stream grabbers
return;
}

IStreamGrabber* pGrabber = camera.GetStreamGrabber(0);
CBaslerGigECamera::StreamGrabber_t StreamGrabber( pGrabber );

// First open the stream grabber
StreamGrabber.Open();

// Set the maximum buffer size according to the amount of data
// the camera will send
StreamGrabber.MaxBufferSize = camera.PayloadSize();

// We are going to use 10 buffers
StreamGrabber.MaxNumBuffer = 10;

当使用泛型编程方法(generic programming approach),即使用Pylon::IPylonDevicePylon::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
2
3
4
5
6
7
8
9
10
11
12
13
14
StreamGrabber.Open();
const int bufferSize = (int) camera.PayloadSize();
const int numBuffers = 10;
unsigned char* ppBuffers[numBuffers];
StreamBufferHandle handles[numBuffers];

StreamGrabber.MaxBufferSize = bufferSize;
StreamGrabber.MaxNumBuffer = numBuffers;

StreamGrabber.PrepareGrab();
for ( int i = 0; i < numBuffers; ++i ) {
ppBuffers[i] = new unsigned char[bufferSize];
handles[i] = StreamGrabber.RegisterBuffer( ppBuffers[i], bufferSize);
}

缓冲区注册机制限制了缓冲区的所有权。虽然已注册缓冲区的内容可以由用户应用程序更改,但应用程序不得删除已注册的内存缓冲区的内存。只有在使用IStreamGrabber::DeregisterBuffer()注销缓冲区后,才允许释放内存。

Feeding the Stream Grabber’s Input Queue

每个Stream Grabber维护两个不同的缓冲区队列。待填充的缓冲区必须被送入Grabber的输入队列。抓取(被抓取到的图像数据填充)的缓冲区可以从Grabber的输出队列中检索。

IStreamGrabber::QueueBuffer()方法用于将一个缓冲区放入Grabber的输入队列。QueueBuffer()方法接受两个参数,一个缓冲区句柄和一个可选的,用户提供的指向用户提供的上下文信息的指针。当从Grabber的输出队列检索抓取结果时,上下文指针与缓冲区一起被传回给用户。Stream Grabber永远不会改变上下文指针所指向的内存。

示例:

1
2
3
4
5
6
MyContext context[numBuffers];
for ( int i = 0; i < numBuffers; ++i ) {
// Enqueue image buffers and use the buffer's index as context
// information
StreamGrabber.QueueBuffer( handles[i], & context[i] );
}

注意:

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
2
3
using namespace Basler_GigECameraParams;
camera.AcquisitionMode.SetValue( AcquisitionMode_Continuous );
camera.AcquisitionStart.Execute();

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const int numGrabs = 100;
GrabResult Result;
for ( int i = 0; i < numGrabs; ++i ) {
// Wait for the grabbed image with a timeout of 3 seconds
if ( StreamGrabber.GetWaitObject().Wait( 3000 )) {
// Get an item from the grabber's output queue
if ( ! StreamGrabber.RetrieveResult( Result ) ) {
cerr << "Failed to retrieve an item from the output queue" << endl;
break;
}

if ( Result.Succeeded() ) {
// Grabbing was successful. Process the image.
ProcessImage( (unsigned char*) Result.Buffer() );
} else {
cerr << "Grab failed: " << Result.GetErrorDescription() << endl;
break;
}
// Requeue the buffer
if ( i + numBuffers < numGrabs )
StreamGrabber.QueueBuffer( Result.Handle(), Result.Context() );
} else {
cerr << "timeout occurred when waiting for a grabbed image" << endl;
break;
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// The camera is in continuous mode, stop the image acquisition
camera.AcquisitionStop.Execute();
// Flush the input queue
StreamGrabber.CancelGrab();
// Consume all items from the output queue
while ( StreamGrabber.GetWaitObject().Wait(0) ) {
if ( ! StreamGrabber.RetrieveResult( Result ) ) {
cerr << "Faile to retrieve item from output queueu" << endl;
} else {
if ( Result.Status() == Canceled ) {
cout << "Got canceled buffer" << endl;
}
}
}

for ( int i = 0; i < numBuffers; ++i ) {
StreamGrabber.DeregisterBuffer(handles[i]);
delete [] ppBuffers[i];
}

StreamGrabber.FinishGrab();
StreamGrabber.Close();

Complete Sample program

下面是在连续模式下从GigE相机获取图像的完整示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <pylon/PylonIncludes.h>
#include <pylon/gige/BaslerGigECamera.h>
#include <ostream>
using namespace Pylon;
using namespace Basler_GigECameraParams;
using namespace std;

typedef CBaslerGigECamera Camera_t;

void ProcessImage( unsigned char* pImage, int imageSizeX, int imageSizeY )
{
// Do something with the image data
}

struct MyContext
{
// Define some application specific context information here
};

int main()
{
PylonAutoInitTerm autoInitTerm;

try
{
// Enumerate GigE cameras
CTlFactory& TlFactory = CTlFactory::GetInstance();
ITransportLayer *pTl = TlFactory.CreateTl( Camera_t::DeviceClass() );
DeviceInfoList_t devices;
if ( 0 == pTl->EnumerateDevices( devices ) ) {
cerr << "No camera present!" << endl;
return 1;
}

// Create a camera object
Camera_t camera ( pTl->CreateDevice( devices[ 0 ] ) );

// Open the camera object
camera.Open();

// Parameterize the camera

// Mono8 pixel format
camera.PixelFormat.SetValue( PixelFormat_Mono8 );

// Maximized AOI
camera.OffsetX.SetValue( 0 );
camera.OffsetY.SetValue( 0 );
camera.Width.SetValue( camera.Width.GetMax() );
camera.Height.SetValue( camera.Height.GetMax() );

// Continuous mode, no external trigger used
camera.TriggerSelector.SetValue( TriggerSelector_AcquisitionStart );
camera.TriggerMode.SetValue( TriggerMode_Off );
camera.AcquisitionMode.SetValue( AcquisitionMode_Continuous );

// Configure exposure time and mode
camera.ExposureMode.SetValue( ExposureMode_Timed );
camera.ExposureTimeRaw.SetValue( 100 );

// check whether stream grabbers are avalaible
if (camera.GetNumStreamGrabberChannels() == 0) {
cerr << "Camera doesn't support stream grabbers." << endl;
} else {
// Get and open a stream grabber
IStreamGrabber* pGrabber = camera.GetStreamGrabber(0);
CBaslerGigECamera::StreamGrabber_t StreamGrabber( camera.GetStreamGrabber(0) );
StreamGrabber.Open();

// Parameterize the stream grabber
const int bufferSize = (int) camera.PayloadSize();
const int numBuffers = 10;
StreamGrabber.MaxBufferSize = bufferSize;
StreamGrabber.MaxNumBuffer = numBuffers;
StreamGrabber.PrepareGrab();

// Allocate and register image buffers, put them into the
// grabber's input queue
unsigned char* ppBuffers[numBuffers];
MyContext context[numBuffers];
StreamBufferHandle handles[numBuffers];
for ( int i = 0; i < numBuffers; ++i )
{
ppBuffers[i] = new unsigned char[bufferSize];
handles[i] = StreamGrabber.RegisterBuffer( ppBuffers[i], bufferSize);
StreamGrabber.QueueBuffer( handles[i], &context[i] );
}

// Start image acquisition
camera.AcquisitionStart.Execute();

// Grab and process 100 images
const int numGrabs = 100;
GrabResult Result;
for ( int i = 0; i < numGrabs; ++i ) {
// Wait for the grabbed image with a timeout of 3 seconds
if ( StreamGrabber.GetWaitObject().Wait( 3000 )) {
// Get an item from the grabber's output queue
if ( ! StreamGrabber.RetrieveResult( Result ) ) {
cerr << "Failed to retrieve an item from the output queue" << endl;
break;
}
if ( Result.Succeeded() ) {
// Grabbing was successful. Process the image.
ProcessImage( (unsigned char*) Result.Buffer(), Result.GetSizeX(), Result.GetSizeY() );
} else {
cerr << "Grab failed: " << Result.GetErrorDescription() << endl;
break;
}
// Requeue the buffer
if ( i + numBuffers < numGrabs )
StreamGrabber.QueueBuffer( Result.Handle(), Result.Context() );
} else {
cerr << "timeout occurred when waiting for a grabbed image" << endl;
break;
}
}

// Finished. Stop grabbing and do clean-up

// The camera is in continuous mode, stop image acquisition
camera.AcquisitionStop.Execute();

// Flush the input queue, grabbing may have failed
StreamGrabber.CancelGrab();

// Consume all items from the output queue
while ( StreamGrabber.GetWaitObject().Wait(0) ) {
StreamGrabber.RetrieveResult( Result );
if ( Result.Status() == Canceled )
cout << "Got canceled buffer" << endl;
}

// Deregister and free buffers
for ( int i = 0; i < numBuffers; ++i ) {
StreamGrabber.DeregisterBuffer(handles[i]);
delete [] ppBuffers[i];
}

// Clean up
StreamGrabber.FinishGrab();
StreamGrabber.Close();
}

camera.Close();
TlFactory.ReleaseTl( pTl );
}
catch( Pylon::GenericException &e )
{
// Error handling
cerr << "An exception occurred!" << endl << e.GetDescription() << endl;
return 1;
}

// Quit application
return 0;
}

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
2
3
4
5
6
// Get the event grabber
Camera_t::EventGrabber_t EventGrabber(camera.GetEventGrabber());
if ( ! EventGrabber.IsAttached() ) {
cerr << "The camera does not support event grabbing" << endl;
return false;
}

永远不要尝试对IEventGrabber指针调用freedeleteCamera对象拥有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
2
// Select the Exposure End event
camera.EventSelector = EventSelector_ExposureEnd;

设置事件选择器后,可以使用Pylon::CBaslerGigECamera::EventNotification参数启用所需类型的事件发送:

1
2
// Enable sending of events of the selected event type
camera.EventNotification.SetValue( EventNotification_GenICamEvent );

为确保您不会错过任何事件,在启用事件之前应准备事件抓取器(见上文的Creating and Preparing Event Grabbers)。

以下代码片段说明如何禁用发送曝光结束事件:

1
2
3
4
// Select the Exposure End event
camera.EventSelector = EventSelector_ExposureEnd;
// Disable sending of Exposure End events
camera.EventNotification.SetValue( EventNotification_Off );

Receiving Events

接收事件与抓取图像的非常相似。Event Grabber提供了一个等待对象,当有事件消息可用时会被触发。当事件消息可用时,可以通过调用Event Grabber的RetrieveEvent()方法来检索它。

与抓取图像不同,接收事件的内存缓冲区不需要由应用程序提供。存储事件消息的内存缓冲区由Event Grabber本身管理。

在典型的应用程序中,等待抓取的图像和事件消息是在一个循环中完成的。以下代码片段演示了这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Add the stream grabber's and the event grabber's wait objects to a container
WaitObjects waitset;
waitset.Add( EventGrabber.GetWaitObject() );
waitset.Add( StreamGrabber.GetWaitObject() );

while ( doGrabbing ) {
// Wait for an image or an event to occur (5 sec timeout)
int idx;
if ( waitset.WaitForAny( 5000, &idx ) ) {
// Got event or image
switch ( idx )
{
case 0: // Event available, get the message
{
EventResult EvResult;
if ( EventGrabber.RetrieveEvent( EvResult ) ) {
if ( EvResult.Succeeded() ) {
// Successfully got the event message.
// EvResult.Buffer points to the message
} else {
// Error occurred
cerr << "Error retrieving event:" << EvResult.ErrorDescription() << endl;
}
} else {
// No event available?
// Should never happen in this sample because the wait object
// was in signaled state when reaching this point.
}
break;
} // Case 0
case 1: // Image available, process it
{
GrabResult GrResult;
if (StreamGrabber.RetrieveResult( GrResult )) {
if (GrResult.Succeeded()) {
// Process the image

// Reuse the buffer for further grabbing
StreamGrabber.QueueBuffer( GrResult.Handle(), GrResult.Context() );
} else {
// handle error
// ...
}
}
} // Case 1
} // Switch
} // if
else {
// handle timeout
// ...
}
} // While

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
2
3
4
5
6
7
GenApi::INodeMap* pNodeMap = pCamera->GetNodeMap();
CIntegerPtr ptrExposureEndFrameId = pNodeMap->GetNode("ExposureEndFrameId");
if ( ! ptrExposureEndFrameId ) {
cerr << "There is no exposure time parameter" << endl;
exit( 1 );
}
int64_t frameNr = ptrExposureEndFrameId->GetValue();

一个Event Adapter对象用于更新相机对象节点图中与事件相关的节点。通过将事件消息传递给Event Adapter来更新节点。

Event Adapter由相机对象创建:

1
2
3
4
Pylon::IEventAdapter *pEventAdapter = camera.CreateEventAdapter();
if ( pEventAdapter == NULL ) {
cerr << "Failed to create an event adapter" << endl;
}

要更新与事件相关的节点,对每个接收到的事件消息调用Event Adapter的DeliverMessage()方法:

1
2
3
4
5
6
7
8
9
10
11
// Retrieve the event result
EventResult EvResult;
if ( EventGrabber.RetrieveEvent( EvResult ) ) {
if ( EvResult.Succeeded() ) {
cout << "Successfully got an event message!" << endl;
// Let the event adapter update the camera object's node map
EventAdapter.DeliverMessage( EvResult.Buffer, sizeof EvResult.Buffer );
} else {
cerr << "Error retrieving event:" << EvResult.ErrorDescription() << endl;
}
}

仅通过将事件消息传递给Event Adapter,无法确定消息是否包含曝光结束事件。下一节描述如何使用节点回调来了解特定事件的发生情况。

Event Callbacks

上一节描述了Event Adapter如何用于将事件消息的内容推送到相机对象的节点图中。IEventAdapter::DeliverMessages()方法更新传入消息中包含的与事件相关的所有节点。

Getting Informed About Parameter Changes所述,可以注册回调函数,当节点可能发生变化时触发这些函数。

这些回调可以用来确定事件消息是否包含某种特定的事件类型。例如,要获取关于曝光结束事件的通知,必须为曝光结束事件相关的节点之一注册回调。以下代码片段演示了如何为ExposureEndFrameId节点注册回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Member function of this class will be registered as callback
struct CallbackTarget
{
CallbackTarget( Camera_t& camera )
: m_Camera( camera )
{ }

// Will be fired when an Exposure End event occurs
void EndOfExposureCallback( GenApi::INode* pNode )
{
try
{
cout << "The message contains an Exposure End event." << endl;
cout << "Timestamp: " << m_pCamera->ExposureEndEventTimestamp.GetValue()
<< "Frame number: " << m_pCamera->ExposureEndEventFrameID.GetValue() << endl;
}
catch ( Pylon::GenericException& e )
{
cerr << "Failed to get event information. Exception occurred:"
<< e.GetDescription() << endl;
}
}

Camera_t& m_Camera;
} callbackTarget( camera );

// Register the callback for the ExposureEndEventTimestamp node.
GenApi::CallbackHandleType hCb = GenApi::Register(
pCamera->ExposureEndEventTimestamp.GetNode(),
callbackTarget,
&CallbackTarget::EndOfExposureCallback );

注册的回调将从IEventAdapter::DeliverMessage()函数的上下文中触发。

注意:

由于一个事件消息可以聚合多个事件,DeliverMessage在同一类型的多个事件可用时将对回调函数发起多次调用。

Cleanup

在关闭和删除Camera对象之前,必须关闭和销毁与事件相关的对象,如下面的代码片段所示:

1
2
3
4
5
6
7
8
9
10
11
12
// Disable sending of Exposure End events
camera.EventSelector = EventSelector_ExposureEnd;
camera.EventNotification.SetValue( EventNotification_Off );

// Cleanup of event grabber and event adapter
// Deregister the callback first, or else when shutting down the event grabber
camera.ExposureEndEventTimestamp.GetNode()->DeregisterCallback( hCb );
// Close the event grabber to tear down the connection
// and free the resources used for receiving events
EventGrabber.Close();
// Delete the event adapter object
camera.DestroyEventAdapter( pEventAdapter );

Chunk Parser: Accessing Chunk Features

Basler相机可以将额外信息附加到图像数据中,例如帧计数器、时间戳和CRC校验和。本节将解释如何启用块特征以及如何访问附加的数据。

Enabling Chunks

在激活生成块数据的特征之前,必须开启相机的块模式(chunk mode)

1
2
3
4
5
6
7
8
9
10
// Open the camera
camera.Open();

// Enable chunks in general
if ( GenApi::IsWritable( camera.ChunkModeActive ) ) {
camera.ChunkModeActive.SetValue( true );
} else {
cerr << "The camera does not support chunk features" << endl;
return 1;
}

当相机处于块模式时,它会传输被划分为多个块的数据块。第一个块总是图像数据。当启用块特征后,图像数据块之后会跟随包含由块特征生成的信息的块。

一旦块模式被激活,就可以启用块特征:

1
2
camera.ChunkSelector.SetValue( ChunkSelector_Timestamp );
camera.ChunkEnable.SetValue( true );

Grabbing Buffers

从带有块的图像流抓取与从不带块的图像流抓取非常相似。必须提供足够大的内存缓冲区,以存储图像数据和附加的块数据。

相机的PayloadSize参数反映所需的缓冲区大小(以字节为单位):

1
2
3
4
5
6
7
8
9
10
11
12
// Ask for the buffer size
const size_t ImageSize = (size_t) ( camera.PayloadSize.GetValue() );

// Allocate buffer(s)
uint8_t *pBuffer = new uint8_t[ ImageSize ];

// Inform the stream grabber about the buffer size
StreamGrabber.MaxBufferSize.SetValue( ImageSize );

// Tell the stream grabber how many buffers will be used
// ( in this example only 1 )
StreamGrabber.MaxNumBuffer.SetValue( 1 );

现在一个图像和其附加的块可以被抓取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Allocate resources related to grabbing
StreamGrabber.PrepareGrab();

// Subscribe the buffer
const StreamBufferHandle hBuffer =
StreamGrabber.RegisterBuffer( pBuffer, ImageSize );

// Put buffer into the grab queue so it will be filled with data
StreamGrabber.QueueBuffer( hBuffer, NULL );

// Let the camera acquire one image
camera.AcquisitionMode.SetValue( AcquisitionMode_SingleFrame );
camera.AcquisitionStart.Execute();


GrabResult Result;
// Wait for the buffer to be filled
if ( StreamGrabber.GetWaitObject().Wait( 3000 ) ) {
// Get the grab result from the grabber's result queue
StreamGrabber.RetrieveResult( Result );
} else {
// Timeout
cerr << "Timeout occurred!" << endl;
return 1;
}

if ( ! Result.Succeeded() ) {
// Error Handling
cerr << "No image acquired!" << endl;
cerr << "Error code : 0x" << hex
<< Result.GetErrorCode() << endl;
cerr << "Error description : "
<< Result.GetErrorDescription() << endl;
return 1;
}

// Check if a buffer containing chunk data has been received
if ( PayloadType_ChunkData != Result.GetPayloadType () ) {
cerr << "Unexpected payload type received" << endl;
return 1;
}

Accessing the Chunk Data

包含图像块和其他块的数据块具有自描述的布局。在访问添加的块中的数据之前,必须使用Chunk Parser对象解析数据块。

相机对象负责创建Chunk Parser:

1
2
// Create ChunkParser
IChunkParser &ChunkParser = *camera.CreateChunkParser();

一旦创建了Chunk Parser,抓取的缓冲区可以绑定到Chunk Parser上。当缓冲区绑定到Chunk Parser时,它将被解析,块数据访问由相机对象的成员提供。

1
2
3
4
5
6
7
8
9
10
// Attach image buffer with chunk data to the parser. The parser extracts
// the included data from the chunk.
pChunkParser->AttachBuffer( Result.Buffer(), Result.GetPayloadSize() );

// Access the chunk data.
// Before accessing the chunk data, it should be checked to see
// if the chunk is readable. When it is readable, the buffer
// contains the requested chunk data.
if ( IsReadable(camera.ChunkTimestamp) )
cout << "TimeStamp : " << camera.ChunkTimestamp.GetValue() << endl;

要检查CRC校验和块特征的结果,请使用Chunk Parser的HasCRC()CheckCRC()方法。注意,相机只在启用CRC校验和特征后才会发送CRC。

1
2
3
4
5
6
7
8
9
10
11
// Enable crc chunks  ( before PrepareGrab()! )
camera.ChunkSelector.SetValue( ChunkSelector_PayloadCRC16 );
camera.ChunkEnable.SetValue( true );

// ...

// Check the CRS sum ( after having the buffer attached to the Chunk Parser)
if ( pChunkParser->HasCRC() && !pChunkParser->CheckCRC() ) {
cerr << "Image was damaged!" << endl;
return 1;
}

在重用缓冲区进行抓取之前,必须将缓冲区从Chunk Parser中分离。

1
2
// After detaching the buffer, the chunk data is no longer accessible!
pChunkParser->DetachBuffer();

分离缓冲区后,下一个抓取的缓冲区可以附加并读取包含的块数据。

当你完成抓取后,必须删除Chunk Parser:

1
2
// Destroy the chunk parser
camera.DestroyChunkParser( pChunkParser );

Getting Informed About Device Removal

可以注册回调函数,当相机设备被移除时触发。一旦调用了相机对象的Open()方法,就可以注册C函数或C++类成员函数作为回调。

注册一个C函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
void RemovalCallbackFunction( IPylonDevice* pDevice )
{
cout << endl << "Callback function for removal of device "
<< pDevice->GetDeviceInfo().GetFullName().c_str() << " has been fired" << endl;
}

// ...

// Open the camera
pCamera->Open();

// Register a "normal" function
DeviceCallbackHandle hCb2 = RegisterRemovalCallback( pCamera, &RemovalCallbackFunction);

注册一个C++类成员函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// A class with a member function that can be registered for device removal notifications
class AClass
{
public:
// The member function to be registered
void RemovalCallbackMemberFunction( IPylonDevice* pDevice )
{
cout << endl << "Member function callback for removal of device "
<< pDevice->GetDeviceInfo().GetFullName().c_str() << " has been fired" << endl;
}
};

// ...

AClass a; // A member function of this class will be registered as a removal callback function

// ...

// Open the camera
pCamera->Open();

// Register a member function
DeviceCallbackHandle hCb1 = RegisterRemovalCallback( pCamera, a, &AClass::RemovalCallbackMemberFunction);

所有注册的回调在调用相机对象的Close()方法之前必须注销

1
2
3
4
if ( ! pCamera->DeregisterRemovalCallback( hCb1 ) )
cerr << "Failed to deregister the callback function" << endl;

pCamera->Close();
  • 标题: 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 进行许可。
评论