Acquiring images
This program uses Euresys::EGrabber
to acquire images from a camera
connected to a Coaxlink card:
#include <iostream>
#include <EGrabber.h>
void grab() {
Euresys::EGenTL gentl;
Euresys::EGrabber<> grabber(gentl); // 1
grabber.reallocBuffers(3); // 2
grabber.start(10); // 3
for (size_t i = 0; i < 10; ++i) {
Euresys::ScopedBuffer buf(grabber); // 4
void *ptr = buf.getInfo<void *>(GenTL::BUFFER_INFO_BASE); // 5
uint64_t ts = buf.getInfo<uint64_t>(GenTL::BUFFER_INFO_TIMESTAMP); // 6
std::cout << "buffer address: " << ptr << ", timestamp: "
<< ts << " us" << std::endl;
} // 7
}
int main() {
try {
grab();
} catch (const std::exception &e) {
std::cout << "error: " << e.what() << std::endl;
}
}
- Create a
Euresys::EGrabber
object. The second and third arguments of the constructor are omitted here. The grabber will use the first device of the first interface present in the system. - Allocate 3 buffers. The grabber automatically determines the required buffer size.
- Start the grabber. Here, we ask the grabber to fill 10 buffers. If we don't want the
grabber to stop after a specific number of buffers, we can do
grabber.start(GenTL::GENTL_INFINITE)
, or simplygrabber.start()
.Starting the grabber involves the following operations:
- the
AcquisitionStart
command is executed on the camera; - the
DSStartAcquisition
function is called to start the data stream.
In this example, we assume that the camera and frame grabber are properly configured. For a real application, it would be safer to run a configuration script before starting acquisitions (and before allocating buffers for that matter). This will be shown in another example.
- the
- Wait for a buffer filled by the grabber. The result is a
Euresys::ScopedBuffer
. The term scoped is used to indicate that the lifetime of the buffer is the current scope (i.e., the current block). - Retrieve the buffer address. This is done by calling the
getInfo
method of the buffer. This method takes as argument aBUFFER_INFO_CMD
. In this case, we request theBUFFER_INFO_BASE
, which is defined in the standard GenTL header file:enum BUFFER_INFO_CMD_LIST { BUFFER_INFO_BASE = 0, /* PTR Base address of the buffer memory. */ BUFFER_INFO_SIZE = 1, /* SIZET Size of the buffer in bytes. */ BUFFER_INFO_USER_PTR = 2, /* PTR Private data pointer of the GenTL Consumer. */ BUFFER_INFO_TIMESTAMP = 3, /* UINT64 Timestamp the buffer was acquired. */ // ... // other BUFFER_INFO definitions omitted // ... BUFFER_INFO_CUSTOM_ID = 1000 /* Starting value for GenTL Producer custom IDs. */ }; typedef int32_t BUFFER_INFO_CMD;
Notice that
getInfo
is a template method, and when we call it we must specify the type of value we expect.BUFFER_INFO_BASE
returns a pointer; this is why we usegetInfo<void *>
. - Do the same to retrieve the timestamp of the buffer. This time, we use the
uint64_t
version ofgetInfo
to match the type ofBUFFER_INFO_TIMESTAMP
.Note that, for Coaxlink, timestamps are always 64-bit and expressed as the number of microseconds that have elapsed since the computer was started.
- We reach the end of the
for
block. The local variablebuf
gets out of scope and is destroyed: theScopedBuffer
destructor is called. This causes the GenTL buffer contained inbuf
to be re-queued (given back) to the data stream of the grabber.
Example of program output:
buffer address: 0x7f3c32c54010, timestamp: 11247531003686 us
buffer address: 0x7f3c2c4bf010, timestamp: 11247531058080 us
buffer address: 0x7f3c2c37e010, timestamp: 11247531085003 us
buffer address: 0x7f3c32c54010, timestamp: 11247531111944 us
buffer address: 0x7f3c2c4bf010, timestamp: 11247531137956 us
buffer address: 0x7f3c2c37e010, timestamp: 11247531163306 us
buffer address: 0x7f3c32c54010, timestamp: 11247531188600 us
buffer address: 0x7f3c2c4bf010, timestamp: 11247531213807 us
buffer address: 0x7f3c2c37e010, timestamp: 11247531239158 us
buffer address: 0x7f3c32c54010, timestamp: 11247531265053 us
We can see that the three buffers that were allocated (let's call them A at
0x7f3c32c54010
, B at 0x7f3c2c4bf010
, and C at
0x7f3c2c37e010
) are used in a round-robin fashion: A → B → C → A →
B → C → ... This is the result of:
- the FIFO nature of input and output buffer queues:
- the Coaxlink driver pops a buffer from the front of the input queue, and gives it to the Coaxlink card for DMA transfer;
- when the transfer is complete, the buffer is pushed to the back of the output queue;
- the use of
ScopedBuffer
:- the
ScopedBuffer
constructor pops a buffer from the front of the output queue (i.e., it takes the oldest buffer); - the
ScopedBuffer
destructor pushes that buffer to the back of the input queue (hence, this buffer will be used for a new transfer after all buffers already in the input queue).
- the