Sample Programs
Chunks Sample
/*
Basler cameras provide "chunk features": The cameras can generate certain information about each image,
e.g. frame counters, time stamps, and CRC checksums, which is appended to the image data as data "chunks".
This sample illustrates how to enable chunk features, how to grab
images, and how to process the appended data. When the camera is in chunk mode, it transfers data blocks
that are partitioned into chunks. The first chunk is always the image data. When chunk features are enabled,
the image data chunk is followed by chunks containing the information generated by the chunk features.
This sample also demonstrates how to use software triggers. Two buffers are used. Once a buffer is filled,
the acquisition of the next frame is triggered before processing the received buffer. This approach allows
performing image acquisition while the processing of the previous image proceeds.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <pylonc/PylonC.h>
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* Calculates the minimum and maximum gray value. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax );
#define NUM_GRABS 20 /* Number of images to grab. */
#define NUM_BUFFERS 2 /* Number of buffers used for grabbing. */
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
PYLON_STREAMGRABBER_HANDLE hGrabber; /* Handle for the pylon stream grabber. */
PYLON_CHUNKPARSER_HANDLE hChunkParser; /* Handle for the parser extracting the chunk data. */
PYLON_WAITOBJECT_HANDLE hWait; /* Handle used for waiting for a grab to be finished. */
size_t payloadSize; /* Size of an image frame in bytes. */
unsigned char* buffers[NUM_BUFFERS]; /* Buffers used for grabbing. */
PYLON_STREAMBUFFER_HANDLE bufHandles[NUM_BUFFERS]; /* Handles for the buffers. */
PylonGrabResult_t grabResult; /* Stores the result of a grab operation. */
int nGrabs; /* Counts the number of buffers grabbed. */
size_t nStreams; /* The number of streams the device provides. */
_Bool isAvail; /* Used for checking feature availability. */
_Bool isReady; /* Used as an output parameter. */
size_t i; /* Counter. */
int ret = EXIT_FAILURE; /* The return value. */
const char* triggerSelectorValue = "FrameStart"; /* Preselect the trigger for image acquisition. */
_Bool isAvailFrameStart; /* Used for checking feature availability. */
_Bool isAvailAcquisitionStart; /* Used for checking feature availability. */
hDev = PYLONC_INVALID_HANDLE;
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
printf( "Enumerating devices ...\n" );
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found.\n" );
/* Before exiting a program, PylonTerminate() should be called to release
all pylon related resources. */
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
printf( "Opening first device ...\n" );
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for setting
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
CHECK( res );
}
/* Check the available camera trigger mode(s) to select the appropriate one: acquisition start trigger mode (used by previous cameras;
do not confuse with acquisition start command) or frame start trigger mode (equivalent to previous acquisition start trigger mode). */
isAvailAcquisitionStart = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
isAvailFrameStart = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
/* Check to see if the camera implements the acquisition start trigger mode only. */
if (isAvailAcquisitionStart && !isAvailFrameStart)
{
/* ... Select the software trigger as the trigger source. */
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "On" );
CHECK( res );
triggerSelectorValue = "AcquisitionStart";
}
else
{
/* Camera may have the acquisition start trigger mode and the frame start trigger mode implemented.
In this case, the acquisition trigger mode must be switched off. */
if (isAvailAcquisitionStart)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame burst start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* To trigger each single frame by software or external hardware trigger: Enable the frame start trigger mode. */
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "On" );
CHECK( res );
}
/* Note: the trigger selector must be set to the appropriate trigger mode
before setting the trigger source or issuing software triggers.
Frame start trigger mode for newer cameras, acquisition start trigger mode for previous cameras. */
PylonDeviceFeatureFromString( hDev, "TriggerSelector", triggerSelectorValue );
/* Enable software triggering. */
/* ... Select the software trigger as the trigger source. */
res = PylonDeviceFeatureFromString( hDev, "TriggerSource", "Software" );
CHECK( res );
/* When using software triggering, the Continuous frame mode should be used. Once
acquisition is started, the camera sends one image each time a software trigger is
issued. */
res = PylonDeviceFeatureFromString( hDev, "AcquisitionMode", "Continuous" );
CHECK( res );
/* For GigE cameras, we recommend increasing the packet size for better
performance. When the network adapter supports jumbo frames, set the packet
size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
to 1500. */
/* ... Check first to see if the GigE camera packet size parameter is supported and if it is writable. */
isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
if (isAvail)
{
/* ... The device supports the packet size feature. Set a value. */
res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
CHECK( res );
}
/* Before enabling individual chunks, the chunk mode in general must be activated. */
isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkModeActive" );
if (!isAvail)
{
fprintf( stderr, "The device doesn't support the chunk mode.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Activate the chunk mode. */
res = PylonDeviceSetBooleanFeature( hDev, "ChunkModeActive", 1 );
CHECK( res );
/* Enable some individual chunks... */
/* ... The frame counter chunk feature. */
/* Is the chunk available? */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_Framecounter" );
if (isAvail)
{
/* Select the frame counter chunk feature. */
res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "Framecounter" );
CHECK( res );
/* Can the chunk feature be activated? */
isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
if (isAvail)
{
/* Activate the chunk feature. */
res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
CHECK( res );
}
}
else
{
/* try setting Standard feature naming convention (SFNC) FrameID name*/
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_FrameID" );
if (isAvail)
{
/* Select the frame id chunk feature. */
res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "FrameID" );
CHECK( res );
/* Can the chunk feature be activated? */
isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
if (isAvail)
{
/* Activate the chunk feature. */
res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
CHECK( res );
}
}
}
/* ... The CRC checksum chunk feature. */
/* Note: Enabling the CRC chunk feature is not a prerequisite for using
chunks. Chunks can also be handled when the CRC feature is disabled. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_PayloadCRC16" );
if (isAvail)
{
/* Select the CRC chunk feature. */
res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "PayloadCRC16" );
CHECK( res );
/* Can the chunk feature be activated? */
isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
if (isAvail)
{
/* Activate the chunk feature. */
res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
CHECK( res );
}
}
/* ... The Timestamp chunk feature. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_ChunkSelector_Timestamp" );
if (isAvail)
{
/* Select the Timestamp chunk feature. */
res = PylonDeviceFeatureFromString( hDev, "ChunkSelector", "Timestamp" );
CHECK( res );
/* Can the chunk feature be activated? */
isAvail = PylonDeviceFeatureIsWritable( hDev, "ChunkEnable" );
if (isAvail)
{
/* Activate the chunk feature. */
res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 1 );
CHECK( res );
}
}
/* The data block containing the image chunk and the other chunks has a self-descriptive layout.
A chunk parser is used to extract the appended chunk data from the grabbed image frame.
Create a chunk parser. */
res = PylonDeviceCreateChunkParser( hDev, &hChunkParser );
CHECK( res );
if (hChunkParser == PYLONC_INVALID_HANDLE)
{
/* The transport layer doesn't provide a chunk parser. */
fprintf( stderr, "No chunk parser available.\n" );
goto exit;
}
/* Image grabbing is done using a stream grabber.
A device may be able to provide different streams. A separate stream grabber must
be used for each stream. In this sample, we create a stream grabber for the default
stream, i.e., the first stream ( index == 0 ).
*/
/* Get the number of streams supported by the device and the transport layer. */
res = PylonDeviceGetNumStreamGrabberChannels( hDev, &nStreams );
CHECK( res );
if (nStreams < 1)
{
fprintf( stderr, "The transport layer doesn't support image streams.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
CHECK( res );
res = PylonStreamGrabberOpen( hGrabber );
CHECK( res );
/* Get a handle for the stream grabber's wait object. The wait object
allows waiting for buffers to be filled with grabbed data. */
res = PylonStreamGrabberGetWaitObject( hGrabber, &hWait );
CHECK( res );
/* Determine the required size of the grab buffer. Since activating chunks will increase the
payload size and thus the required buffer size, do this after enabling the chunks. */
res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
CHECK( res );
/* Allocate memory for grabbing. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
buffers[i] = (unsigned char*) malloc( payloadSize );
if (NULL == buffers[i])
{
fprintf( stderr, "Out of memory.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
}
/* We must tell the stream grabber the number and size of the buffers
we are using. */
/* .. We will not use more than NUM_BUFFERS for grabbing. */
res = PylonStreamGrabberSetMaxNumBuffer( hGrabber, NUM_BUFFERS );
CHECK( res );
/* .. We will not use buffers bigger than payloadSize bytes. */
res = PylonStreamGrabberSetMaxBufferSize( hGrabber, payloadSize );
CHECK( res );
/* Allocate the resources required for grabbing. After this, critical parameters
that impact the payload size must not be changed until FinishGrab() is called. */
res = PylonStreamGrabberPrepareGrab( hGrabber );
CHECK( res );
/* Before using the buffers for grabbing, they must be registered at
the stream grabber. For each registered buffer, a buffer handle
is returned. After registering, these handles are used instead of the
raw pointers. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberRegisterBuffer( hGrabber, buffers[i], payloadSize, &bufHandles[i] );
CHECK( res );
}
/* Feed the buffers into the stream grabber's input queue. For each buffer, the API
allows passing in a pointer to additional context information. This pointer
will be returned unchanged when the grab is finished. In our example, we use the index of the
buffer as context information. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberQueueBuffer( hGrabber, bufHandles[i], (void*) i );
CHECK( res );
}
/* Start the image acquisition engine. */
res = PylonStreamGrabberStartStreamingIfMandatory( hGrabber );
CHECK( res );
/* Issue an acquisition start command. Because the trigger mode is enabled, issuing the acquisition start command
itself will not trigger any image acquisitions. Issuing the start command simply prepares the camera to acquire images.
Once the camera is prepared it will acquire one image for every trigger it receives. */
res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStart" );
CHECK( res );
/* Trigger the first image. */
res = PylonDeviceExecuteCommandFeature( hDev, "TriggerSoftware" );
CHECK( res );
/* Grab NUM_GRABS images */
nGrabs = 0; /* Counts the number of images grabbed. */
while (nGrabs < NUM_GRABS)
{
size_t bufferIndex; /* Index of the buffer. */
unsigned char min, max;
int32_t chunkWidth = 0; /* Data retrieved from the chunk parser. */
int32_t chunkHeight = 0; /* Data retrieved from the chunk parser. */
/* Wait for the next buffer to be filled. Wait up to 1000 ms. */
res = PylonWaitObjectWait( hWait, 1000, &isReady );
CHECK( res );
if (!isReady)
{
/* Timeout occurred. */
fprintf( stderr, "Grab timeout occurred.\n" );
break; /* Stop grabbing. */
}
/* Since the wait operation was successful, the result of at least one grab
operation is available. Retrieve it. */
res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
CHECK( res );
if (!isReady)
{
/* Oops. No grab result available? We should never have reached this point.
Since the wait operation above returned without a timeout, a grab result
should be available. */
fprintf( stderr, "Failed to retrieve a grab result\n" );
break;
}
nGrabs++;
/* Trigger the next image. Since we passed more than one buffer to the stream grabber,
the triggered image will be grabbed while the image processing is performed. */
res = PylonDeviceExecuteCommandFeature( hDev, "TriggerSoftware" );
CHECK( res );
/* Get the buffer index from the context information. */
bufferIndex = (size_t) grabResult.Context;
/* Check to see if the image was grabbed successfully. */
if (grabResult.Status == Grabbed)
{
/* The grab is successfull. */
unsigned char* buffer; /* Pointer to the buffer attached to the grab result. */
/* Get the buffer pointer from the result structure. Since we also got the buffer index,
we could alternatively use buffers[bufferIndex]. */
buffer = (unsigned char*) grabResult.pBuffer;
printf( "Grabbed frame #%2d into buffer %2d.\n", nGrabs, (int) bufferIndex );
/* Check to see if we really got image data plus chunk data. */
if (grabResult.PayloadType != PayloadType_ChunkData)
{
fprintf( stderr, "Received a buffer not containing chunk data?\n" );
}
else
{
/* Process the chunk data. This is done by passing the grabbed image buffer
to the chunk parser. When the chunk parser has processed the buffer, the chunk
data can be accessed in the same manner as "normal" camera parameters.
The only exception is the CRC feature. There are dedicated functions for
checking the CRC checksum. */
_Bool hasCRC;
/* Let the parser extract the data. */
res = PylonChunkParserAttachBuffer( hChunkParser, grabResult.pBuffer, (size_t) grabResult.PayloadSize );
CHECK( res );
/* Check the CRC. */
res = PylonChunkParserHasCRC( hChunkParser, &hasCRC );
CHECK( res );
if (hasCRC)
{
_Bool isOk;
res = PylonChunkParserCheckCRC( hChunkParser, &isOk );
CHECK( res );
printf( "Frame %d contains a CRC checksum. The checksum %s ok.\n", nGrabs, isOk ? "is" : "is not" );
}
{
const char *featureName = "ChunkFramecounter";
/* Retrieve the frame counter value. */
/* ... Check the availability. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, featureName );
if (!isAvail)
{
/*if not available try using the SFNC feature FrameID*/
featureName = "ChunkFrameID";
isAvail = PylonDeviceFeatureIsAvailable( hDev, featureName );
}
printf( "Frame %d %s a frame counter chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
if (isAvail)
{
/* ... Get the value. */
int64_t counter;
res = PylonDeviceGetIntegerFeature( hDev, featureName, &counter );
CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
printf( "Frame counter of frame %d: %lld.\n", nGrabs, (long long) counter );
#else
printf( "Frame counter of frame %d: %I64d.\n", nGrabs, counter );
#endif
}
}
/* Retrieve the frame width value. */
/* ... Check the availability. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "ChunkWidth" );
printf( "Frame %d %s a width chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
if (isAvail)
{
/* ... Get the value. */
res = PylonDeviceGetIntegerFeatureInt32( hDev, "ChunkWidth", &chunkWidth );
CHECK( res );
printf( "Width of frame %d: %d.\n", nGrabs, chunkWidth );
}
/* Retrieve the frame height value. */
/* ... Check the availability. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "ChunkHeight" );
printf( "Frame %d %s a height chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
if (isAvail)
{
/* ... Get the value. */
res = PylonDeviceGetIntegerFeatureInt32( hDev, "ChunkHeight", &chunkHeight );
CHECK( res );
printf( "Height of frame %d: %d.\n", nGrabs, chunkHeight );
}
/* Retrieve the frame timestamp value. */
/* ... Check the availability. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "ChunkTimestamp" );
printf( "Frame %d %s a timestamp chunk.\n", nGrabs, isAvail ? "contains" : "doesn't contain" );
if (isAvail)
{
/* ... Get the value. */
int64_t timestamp;
res = PylonDeviceGetIntegerFeature( hDev, "ChunkTimestamp", ×tamp );
CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
printf( "Frame timestamp of frame %d: %lld.\n", nGrabs, (long long)timestamp );
#else
printf( "Frame timestamp of frame %d: %I64d.\n", nGrabs, timestamp );
#endif
}
}
/* Perform the image processing. */
getMinMax( buffer, chunkWidth, chunkHeight, &min, &max );
printf( "Min. gray value = %3u, Max. gray value = %3u\n", min, max );
/* Before requeueing the buffer, you should detach it from the chunk parser. */
res = PylonChunkParserDetachBuffer( hChunkParser ); /* The chunk data in the buffer is now no longer accessible. */
CHECK( res );
}
else if (grabResult.Status == Failed)
{
fprintf( stderr, "Frame %d wasn't grabbed successfully. Error code = 0x%08X\n",
nGrabs, grabResult.ErrorCode );
}
/* Once finished with the processing, requeue the buffer to be filled again. */
res = PylonStreamGrabberQueueBuffer( hGrabber, grabResult.hBuffer, (void*) bufferIndex );
CHECK( res );
}
/* Clean up. */
/* ... Stop the camera. */
res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStop" );
CHECK( res );
/* ... Stop the image acquisition engine. */
res = PylonStreamGrabberStopStreamingIfMandatory( hGrabber );
CHECK( res );
/* ... We must issue a flush call to ensure that all pending buffers are put into the
stream grabber's output queue. */
res = PylonStreamGrabberFlushBuffersToOutput( hGrabber );
CHECK( res );
/* ... The buffers can now be retrieved from the stream grabber. */
do
{
res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
CHECK( res );
} while (isReady);
/* ... When all buffers are retrieved from the stream grabber, they can be deregistered.
After deregistering the buffers, it is safe to free the memory. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberDeregisterBuffer( hGrabber, bufHandles[i] );
CHECK( res );
free( buffers[i] );
}
/* ... Release grabbing related resources. */
res = PylonStreamGrabberFinishGrab( hGrabber );
CHECK( res );
/* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
the AOI width and height parameters) are unlocked and can be modified again. */
/* ... Close the stream grabber. */
res = PylonStreamGrabberClose( hGrabber );
CHECK( res );
/* ... Release the chunk parser. */
res = PylonDeviceDestroyChunkParser( hDev, hChunkParser );
CHECK( res );
ret = EXIT_SUCCESS;
exit:
/* Disable the software trigger and the chunk mode. */
if (hDev != PYLONC_INVALID_HANDLE)
{
res = PylonDeviceSetBooleanFeature( hDev, "ChunkEnable", 0 );
CHECK( res );
res = PylonDeviceSetBooleanFeature( hDev, "ChunkModeActive", 0 );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* ... Close and release the pylon device. The stream grabber becomes invalid
after closing the pylon device. Don't call stream grabber related methods after
closing or releasing the device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
/* ... Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
pressEnterToExit();
return ret;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... First find out how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve the more details about the error.
... First find out how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Simple "image processing" function returning the minimum and maximum gray
value of an image with 8 bit gray values. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax )
{
unsigned char min = 255;
unsigned char max = 0;
unsigned char val;
const unsigned char* p;
for (p = pImg; p < pImg + width * height; p++)
{
val = *p;
if (val > max)
max = val;
if (val < min)
min = val;
}
*pMin = min;
*pMax = max;
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
ImageDecompressor Sample
/*
This sample illustrates how to configure the Compression Beyond feature
on an ace 2 Pro camera.
This allows the camera to send compressed image data to the host computer.
The compressed image data can be decompressed on the host side using the
PylonImageDecompressor set of functions.
Using the image compression feature reduces the amount of data transferred,
which in turn can result in increasing the resulting frame rate.
When compression is used, the camera sends the compressed image data as
chunk data. You can use the PylonImageDecompressorDecompressImage function
to convert the the compressed chunk data into the pixel data.
Note: Not all camera models support image compression.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <genapic/GenApiC.h>
#include <pylonc/PylonC.h>
/* Simple error handling. */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message
for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* Calculating the minimum and maximum gray value of an image buffer. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax );
/* Configure the camera to use compression. */
GENAPIC_RESULT configureCompression( PYLON_DEVICE_HANDLE hDev, double ratio );
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
NODEMAP_HANDLE hDeviceNodeMap;/* Handle for nodemap. */
NODE_HANDLE hDescNode; /* Handle for the register node. */
const int numGrabs = 3; /* Number of images to grab. */
size_t payloadSize = 0; /* Size of the compressed image data in bytes. */
size_t decompressedImageSize = 0; /* Size of a decompressed image frame in bytes. */
size_t descriptorSize = 0; /* Size of the compression descriptor. */
unsigned char* descriptorBuf = NULL; /* Buffer used to store compression descriptor. */
unsigned char* imgBufCompressed = NULL; /* Buffer used to store compressed image data. */
unsigned char* imgBuf = NULL; /* Buffer used to store decompressed image data. */
_Bool isAvail;
int i;
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device! */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found!\n" );
/* Before exiting a program, PylonTerminate() must be called to release
all pylon-related resources. */
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* The device must be opened in order to configure parameters and grab images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Check whether the camera supports the compression feature. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "ImageCompressionMode" );
if (isAvail)
{
PYLON_IMAGE_DECOMPRESSOR_HANDLE hDecompressor = PYLONC_INVALID_HANDLE;
double fps = 0.;
/* Set the pixel format to Mono8, if available, where gray values will be output as 8-bit values for each pixel. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
CHECK( res );
}
/* Disable acquisition start trigger, if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame burst start trigger, if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame start trigger, if available */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* For GigE cameras, Basler recommends increasing the packet size for better performance.
If the network adapter supports jumbo frames,
set the packet size to a value > 1500, e.g., to 8192.
In this sample, we only set the packet size to 1500. */
isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
if (isAvail)
{
/* ... The device supports the packet size feature. Set a value. */
res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
CHECK( res );
}
/* Turn off the compression feature to read the FPS without compression. */
res = configureCompression( hDev, 0.0 );
CHECK( res );
res = PylonDeviceGetFloatFeature(hDev, "ResultingFrameRate", &fps);
CHECK( res );
printf( "Expected frames per second without compression: %.2f\n", fps );
/* Configure the camera for lossless compression using a compression ratio of 75 %.
You can adjust this value as required. */
res = configureCompression( hDev, 75.0 );
CHECK( res );
/* Show the FPS with compression turned on. */
res = PylonDeviceGetFloatFeature( hDev, "ResultingFrameRate", &fps );
CHECK( res );
printf( "Expected frames per second using compression: %.2f\n", fps );
/* Determine the required size of the grab buffer. */
{
PYLON_STREAMGRABBER_HANDLE hGrabber;
int64_t value;
/* Temporary create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
CHECK( res );
res = PylonStreamGrabberOpen( hGrabber );
CHECK( res );
/* With compression turned on, the returned payload size is the
estimated maximum size of the compressed image data.
The actual size of image data transferred may be smaller, depending on image contents. */
res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
CHECK( res );
res = PylonStreamGrabberClose( hGrabber );
CHECK( res );
/* Get the size of the decompressed image data. */
res = PylonDeviceGetIntegerFeature( hDev, "BslImageCompressionBCBDecompressedImageSize", &value );
CHECK( res );
decompressedImageSize = (size_t) value;
}
/* Create the decompressor. */
res = PylonImageDecompressorCreate( &hDecompressor );
CHECK( res );
/* Get size of the compression descriptor required to decompress the image data from a register node.
There is no pylon convenience function to read register nodes, so we use genapi functions. */
res = PylonDeviceGetNodeMap( hDev, &hDeviceNodeMap );
CHECK( res );
res = GenApiNodeMapGetNode( hDeviceNodeMap, "BslImageCompressionBCBDescriptor", &hDescNode );
CHECK( res );
/* Get the length of the register node. */
res = GenApiRegisterGetLength( hDescNode, &descriptorSize );
CHECK( res );
/* Allocate memory for the descriptor.
The descriptor is small, allocation should not fail. */
descriptorBuf = (unsigned char*) malloc( descriptorSize );
/* Read the compression descriptor from the camera. */
res = GenApiRegisterGetValue( hDescNode, descriptorBuf, &descriptorSize );
CHECK( res );
/* Set the descriptor in the decompressor. */
res = PylonImageDecompressorSetCompressionDescriptor( hDecompressor, descriptorBuf, descriptorSize );
CHECK( res );
/* The decompresser has stored a copy of the descriptor. We can now free the descriptor. */
free( descriptorBuf );
descriptorBuf = NULL;
/* Allocate memory. */
imgBufCompressed = (unsigned char*) malloc( payloadSize );
imgBuf = (unsigned char*) malloc( decompressedImageSize );
if (NULL == imgBufCompressed || NULL == imgBuf)
{
fprintf( stderr, "Out of memory.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Grab some images in a loop. */
for (i = 0; i < numGrabs; ++i)
{
PylonGrabResult_t grabResult = { 0 };
PylonCompressionInfo_t compInfo = { 0 };
_Bool bufferReady;
/* Grab a single frame from stream channel 0.
The camera is set to single frame acquisition mode.
Wait up to 2000 ms for the image to be grabbed. */
res = PylonDeviceGrabSingleFrame( hDev, 0, imgBufCompressed, payloadSize,
&grabResult, &bufferReady, 2000 );
if (GENAPI_E_OK == res && !bufferReady)
{
/* Timeout occurred. */
printf( "Frame %d: timeout\n", i + 1 );
}
CHECK( res );
/* Check to see if the data has been received successfully. */
if (grabResult.Status == Grabbed && grabResult.PayloadType == PayloadType_ChunkData)
{
/* Retrieve infos about the compressed image data (sent as chunk data). */
res = PylonImageDecompressorGetCompressionInfo( grabResult.pBuffer, (size_t)grabResult.PayloadSize, &compInfo );
CHECK( res );
/* Does the returned chunk payload contain a successfully compressed image? */
if (compInfo.HasCompressedImage && compInfo.CompressionStatus == CompressionStatus_Ok)
{
unsigned char min, max;
double ratio = 0;
/* Decompress the chunk data into imgBuf. */
res = PylonImageDecompressorDecompressImage( hDecompressor, imgBuf, &decompressedImageSize, grabResult.pBuffer, (size_t) grabResult.PayloadSize, NULL );
CHECK( res );
/* Use the actual size of the returned data. */
ratio = (double)grabResult.PayloadSize / (double) compInfo.DecompressedPayloadSize;
/* Compressed images are sent as chunk data (PayloadType_ChunkData).
Most members of grabResult don't contain valid data.
This information can be retrieved from compInfo. */
/* Success. Now you can perform image processing on the image. */
getMinMax( imgBuf, compInfo.SizeX, compInfo.SizeY, &min, &max );
printf( "Grabbed frame #%2d: Compression Ratio: %.2f%%, Min. gray value = %3u, Max. gray value = %3u\n", i + 1, ratio, min, max );
#ifdef GENAPIC_WIN_BUILD
/* Display image. */
res = PylonImageWindowDisplayImage( 0, imgBuf, decompressedImageSize, compInfo.PixelType, compInfo.SizeX, compInfo.SizeY, compInfo.PaddingX, ImageOrientation_TopDown );
CHECK( res );
#endif
}
else
{
printf( "Grabbed frame #%2d: Camera could not compress image. CompressionStatus = %d\n", i + 1, compInfo.CompressionStatus );
}
}
else if (grabResult.Status == Failed)
{
fprintf( stderr, "Frame %d wasn't grabbed successfully. Error code = 0x%08X\n",
i + 1, grabResult.ErrorCode );
}
}
/* Free memory. */
free( imgBuf );
free( imgBufCompressed );
/* Free all resources allocated by the decompressor. */
res = PylonImageDecompressorDestroy( hDecompressor );
CHECK( res );
/* Turn off compression. */
res = configureCompression( hDev, 0.0 );
CHECK( res );
}
else
{
printf( "Camera does not support compression.\n");
}
/* Clean up. Close and release the pylon device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
pressEnterToExit();
/* Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
return EXIT_SUCCESS;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... Find out first how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve more details about the error.
... Find out first how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Simple "image processing" function returning the minimum and maximum gray
value of an 8-bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax )
{
unsigned char min = 255;
unsigned char max = 0;
unsigned char val;
const unsigned char* p;
for (p = pImg; p < pImg + width * height; p++)
{
val = *p;
if (val > max)
max = val;
if (val < min)
min = val;
}
*pMin = min;
*pMax = max;
}
/* This function configures the camera to use compression.
The ratio parameter will set the BslImageCompressionRatio feature on the camera.
If you pass a ratio of 0 compression will be turned off. */
GENAPIC_RESULT configureCompression( PYLON_DEVICE_HANDLE hDev, double ratio )
{
GENAPIC_RESULT res = GENAPI_E_OK;
if (ratio == 0)
{
/* Turn compression off. */
res = PylonDeviceFeatureFromString( hDev, "ImageCompressionMode", "Off" );
}
else
{
/* Turn the compression feature on. */
res = PylonDeviceFeatureFromString( hDev, "ImageCompressionMode", "BaslerCompressionBeyond" );
if (res != GENAPI_E_OK) return res;
/* We're using lossless compression, so image quality is not affected. */
res = PylonDeviceFeatureFromString( hDev, "ImageCompressionRateOption", "Lossless" );
if (res != GENAPI_E_OK) return res;
/* In this sample we use a compression ratio of 75 %.
You can adjust this value depending on your image contents and the required frame-rate.
In addition, you can also configure the camera for lossy compression. This is not demonstrated in this sample.
For more information, refer to the Basler Product Documentation.*/
res = PylonDeviceSetFloatFeature( hDev, "BslImageCompressionRatio", ratio );
if (res != GENAPI_E_OK) return res;
}
return res;
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
Events Sample
/*
Basler GigE Vision cameras can send event messages. For example, when a sensor
exposure has finished, the camera can send an end-of-exposure event to the PC. The event
can be received by the PC before the image data for the finished exposure has been completely
transferred. This sample illustrates the retrieving and processing of event messages.
Receiving events is very similar to grabbing images. An event grabber provides a wait object that
is signalled when an event message is available. When an event message is available, it can be
retrieved from the event grabber. In contrast to grabbing images, memory buffers for receiving
events need not be provided by the application. Memory buffers to store event messages are organized
by the event grabber itself.
The specific layout of event messages depends on the event type and the camera type. The pylon API
uses GenICam support for parsing event messages. This means that the message layout is described in the
camera's XML description file. A GenApi node map is created from the XML camera description file.
This node map contains node objects representing the elements of the XML file. Since the layout of event
messages is described in the camera description file, the information carried by the event messages is
exposed as nodes in the node map and can be accessed like "normal" camera parameters.
You can register callback functions that are fired when a parameter has been changed. To be
informed that a received event message contains a specific event, a callback must be registered for
the parameter(s) associated with the event.
These mechanisms are demonstrated with the end-of-exposure event. The event carries the following
information:
* ExposureEndEventFrameID: indicates the number of the image frame that has been exposed.
* ExposureEndEventTimestamp: indicates the moment when the event has been generated.
* ExposureEndEventStreamChannelIndex: indicates the number of the image data stream used to
transfer the exposed frame.
A callback for the ExposureEndEventFrameID will be registered as an indicator for the arrival
of an end-of-exposure event.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <pylonc/PylonC.h>
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* Calculates the minimum and maximum gray value. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax );
/* The function to be fired when an end of exposure event has been received. */
void GENAPIC_CC endOfExposureCallback( NODE_HANDLE hNode );
#define NUM_GRABS 100 /* Number of images to grab. */
#define NUM_IMAGE_BUFFERS 5 /* Number of buffers used for grabbing. */
#define NUM_EVENT_BUFFERS 20 /* Number of buffers used for grabbing. */
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
PYLON_STREAMGRABBER_HANDLE hStreamGrabber; /* Handle for the pylon stream grabber. */
PYLON_EVENTGRABBER_HANDLE hEventGrabber; /* Handle for the event grabber used for receiving events. */
PYLON_EVENTADAPTER_HANDLE hEventAdapter; /* Handle for the event adapter used for dispatching events. */
PYLON_WAITOBJECT_HANDLE hWaitStream; /* Handle used for waiting for a grab to be finished. */
PYLON_WAITOBJECT_HANDLE hWaitEvent; /* Handle used for waiting for an event message. */
PYLON_WAITOBJECTS_HANDLE hWaitObjects; /* Container allowing waiting for multiple wait objects. */
NODEMAP_HANDLE hNodeMap; /* Handle for the node map containing the
camera parameters. */
NODE_CALLBACK_HANDLE hCallback; /* Used for deregistering a callback function. */
NODE_HANDLE hNode; /* Handle for a camera parameter. */
size_t payloadSize; /* Size of an image in bytes. */
unsigned char* buffers[NUM_IMAGE_BUFFERS]; /* Buffers used for grabbing. */
PYLON_STREAMBUFFER_HANDLE bufHandles[NUM_IMAGE_BUFFERS]; /* Handles for the buffers. */
PylonGrabResult_t grabResult; /* Stores the result of a grab operation. */
int nGrabs; /* Counts the number of buffers grabbed. */
size_t nStreams; /* The number of streams the device provides. */
_Bool isAvail; /* Used for checking feature availability. */
_Bool isReady; /* Used as an output parameter. */
size_t i; /* Counter. */
int32_t sfncVersionMajor; /* The major number of the Standard Feature Naming Convention (SFNC)
version used by the camera device. */
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for settig
parameters, for grabbing images, and for grabbing events. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM | PYLONC_ACCESS_MODE_EVENT );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera %s\n", buf );
}
}
/* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
CHECK( res );
}
/* Disable acquisition start trigger if available */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame burst start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame start trigger if available */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* We will use the Continuous frame mode, i.e., the camera delivers
images continuously. */
res = PylonDeviceFeatureFromString( hDev, "AcquisitionMode", "Continuous" );
CHECK( res );
/* For GigE cameras, we recommend increasing the packet size for better
performance. If the network adapter supports jumbo frames, set the packet
size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
to 1500. */
/* ... Check first to see if the GigE camera packet size parameter is supported and if it is writable. */
isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
if (isAvail)
{
/* ... The device supports the packet size feature, set a value. */
res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
CHECK( res );
}
isAvail = PylonDeviceFeatureIsWritable( hDev, "EventSelector" );
if (!isAvail)
{
/* Feature is not available. */
fprintf( stderr, "Device doesn't support events.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Determine the major number of the SFNC version used by the camera device. */
if (PylonDeviceGetIntegerFeatureInt32( hDev, "DeviceSFNCVersionMajor", &sfncVersionMajor ) != GENAPI_E_OK)
{
/* No SFNC version information is provided by the camera device. */
sfncVersionMajor = 0;
}
/* Enable camera event reporting. */
/* Select the end-of-exposure event reporting. */
res = PylonDeviceFeatureFromString( hDev, "EventSelector", "ExposureEnd" );
CHECK( res );
/* Enable the event reporting.
Select the enumeration entry name depending on the SFNC version used by the camera device.
*/
if (sfncVersionMajor >= 2)
res = PylonDeviceFeatureFromString( hDev, "EventNotification", "On" );
else
res = PylonDeviceFeatureFromString( hDev, "EventNotification", "GenICamEvent" );
CHECK( res );
/* Image grabbing is done using a stream grabber.
A device may be able to provide different streams. A separate stream grabber must
be used for each stream. In this sample, we create a stream grabber for the default
stream, i.e., the first stream ( index == 0 ).
*/
/* Get the number of streams supported by the device and the transport layer. */
res = PylonDeviceGetNumStreamGrabberChannels( hDev, &nStreams );
CHECK( res );
if (nStreams < 1)
{
fprintf( stderr, "The transport layer doesn't support image streams\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev, 0, &hStreamGrabber );
CHECK( res );
res = PylonStreamGrabberOpen( hStreamGrabber );
CHECK( res );
/* Get a handle for the stream grabber's wait object. The wait object
allows waiting for buffers to be grabbed. */
res = PylonStreamGrabberGetWaitObject( hStreamGrabber, &hWaitStream );
CHECK( res );
res = PylonStreamGrabberGetPayloadSize( hDev, hStreamGrabber, &payloadSize );
CHECK( res );
/* Allocate memory for grabbing. */
for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
{
buffers[i] = (unsigned char*) malloc( payloadSize );
if (NULL == buffers[i])
{
fprintf( stderr, "Out of memory!\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
}
/* We must tell the stream grabber the number and size of the buffers
we are using. */
/* .. We will not use more than NUM_BUFFERS for grabbing. */
res = PylonStreamGrabberSetMaxNumBuffer( hStreamGrabber, NUM_IMAGE_BUFFERS );
CHECK( res );
/* .. We will not use buffers bigger than payloadSize bytes. */
res = PylonStreamGrabberSetMaxBufferSize( hStreamGrabber, payloadSize );
CHECK( res );
/* Allocate the resources required for grabbing. After this, critical parameters
that impact the payload size must not be changed until FinishGrab() is called. */
res = PylonStreamGrabberPrepareGrab( hStreamGrabber );
CHECK( res );
/* Before using the buffers for grabbing, they must be registered at
the stream grabber. For each registered buffer, a buffer handle
is returned. After registering, these handles are used instead of the
raw pointers. */
for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
{
res = PylonStreamGrabberRegisterBuffer( hStreamGrabber, buffers[i], payloadSize, &bufHandles[i] );
CHECK( res );
}
/* Feed the buffers into the stream grabber's input queue. For each buffer, the API
allows passing in a pointer to additional context information. This pointer
will be returned unchanged when the grab is finished. In our example, we use the index of the
buffer as context information. */
for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
{
res = PylonStreamGrabberQueueBuffer( hStreamGrabber, bufHandles[i], (void*) i );
CHECK( res );
}
/* The stream grabber is now prepared. As soon as the camera starts to acquire images,
the image data will be grabbed into the provided buffers. */
/* Create and prepare an event grabber. */
/* ... Get a handle for the event grabber. */
res = PylonDeviceGetEventGrabber( hDev, &hEventGrabber );
CHECK( res );
if (hEventGrabber == PYLONC_INVALID_HANDLE)
{
/* The transport layer doesn't support event grabbers. */
fprintf( stderr, "No event grabber supported.\n" );
PylonTerminate();
pressEnterToExit();
return EXIT_FAILURE;
}
/* ... Tell the grabber how many buffers to use. */
res = PylonEventGrabberSetNumBuffers( hEventGrabber, NUM_EVENT_BUFFERS );
CHECK( res );
/* ... Open the event grabber. */
res = PylonEventGrabberOpen( hEventGrabber ); /* The event grabber is now ready
for receiving events. */
CHECK( res );
/* Retrieve the wait object that is associated with the event grabber. The event
will be signaled when an event message has been received. */
res = PylonEventGrabberGetWaitObject( hEventGrabber, &hWaitEvent );
CHECK( res );
/* For extracting the event data from an event message, an event adapter is used. */
res = PylonDeviceCreateEventAdapter( hDev, &hEventAdapter );
CHECK( res );
if (hEventAdapter == PYLONC_INVALID_HANDLE)
{
/* The transport layer doesn't support event grabbers. */
fprintf( stderr, "No event adapter supported.\n" );
PylonTerminate();
pressEnterToExit();
return EXIT_FAILURE;
}
/* Register the callback function for ExposureEndEventFrameID parameter. */
/*... Get the node map containing all parameters. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Get the ExposureEndEventFrameID parameter.
Select the parameter name depending on the SFNC version used by the camera device.
*/
if (sfncVersionMajor >= 2)
res = GenApiNodeMapGetNode( hNodeMap, "EventExposureEndFrameID", &hNode );
else
res = GenApiNodeMapGetNode( hNodeMap, "ExposureEndEventFrameID", &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
/* There is no ExposureEndEventFrameID parameter. */
fprintf( stderr, "There is no ExposureEndEventFrameID or EventExposureEndFrameID parameter.\n" );
PylonTerminate();
pressEnterToExit();
return EXIT_FAILURE;
}
/* ... Register the callback function. */
res = GenApiNodeRegisterCallback( hNode, endOfExposureCallback, &hCallback );
CHECK( res );
/* Put the wait objects into a container. */
/* ... Create the container. */
res = PylonWaitObjectsCreate( &hWaitObjects );
CHECK( res );
/* ... Add the wait objects' handles. */
res = PylonWaitObjectsAddMany( hWaitObjects, 2, hWaitEvent, hWaitStream );
CHECK( res );
/* Start the image acquisition engine. */
res = PylonStreamGrabberStartStreamingIfMandatory( hStreamGrabber );
CHECK( res );
/* Let the camera acquire images. */
res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStart" );
CHECK( res );
/* Grab NUM_GRABS images. */
nGrabs = 0; /* Counts the number of images grabbed. */
while (nGrabs < NUM_GRABS)
{
size_t bufferIndex; /* Index of the buffer. */
size_t waitObjectIndex; /* Index of the wait object that is signalled.*/
unsigned char min, max;
/* Wait for either an image buffer grabbed or an event received. Wait up to 1000 ms. */
res = PylonWaitObjectsWaitForAny( hWaitObjects, 1000, &waitObjectIndex, &isReady );
CHECK( res );
if (!isReady)
{
/* Timeout occurred. */
fprintf( stderr, "Timeout. Neither grabbed an image nor received an event.\n" );
break; /* Stop grabbing. */
}
if (0 == waitObjectIndex)
{
PylonEventResult_t eventMsg;
/* hWaitEvent has been signalled. At least one event message is available. Retrieve it. */
res = PylonEventGrabberRetrieveEvent( hEventGrabber, &eventMsg, &isReady );
CHECK( res );
if (!isReady)
{
/* Oops. No event message available? We should never have reached this point.
Since the wait operation above returned without a timeout, an event message
should be available. */
fprintf( stderr, "Failed to retrieve an event\n" );
break;
}
/* Check to see if the event was successfully received. */
if (0 == eventMsg.ErrorCode)
{
/* Successfully received an event message. */
/* Pass the event message to the event adapter. The event adapter will
update the parameters related to events and will fire the callbacks
registered to event related parameters. */
res = PylonEventAdapterDeliverMessage( hEventAdapter, &eventMsg );
CHECK( res );
}
else
{
fprintf( stderr, "Error when receiving an event: 0x%08x\n", eventMsg.ErrorCode );
}
}
else if (1 == waitObjectIndex)
{
/* hWaitStream has been signalled. The result of at least one grab
operation is available. Retrieve it. */
res = PylonStreamGrabberRetrieveResult( hStreamGrabber, &grabResult, &isReady );
CHECK( res );
if (!isReady)
{
/* Oops. No grab result available? We should never have reached this point.
Since the wait operation above returned without a timeout, a grab result
should be available. */
fprintf( stderr, "Failed to retrieve a grab result\n" );
break;
}
nGrabs++;
/* Get the buffer index from the context information. */
bufferIndex = (size_t) grabResult.Context;
/* Check to see if the image was grabbed successfully. */
if (grabResult.Status == Grabbed)
{
/* Success. Perform image processing. Since we passed more than one buffer
to the stream grabber, the remaining buffers are filled while
we do the image processing. The processed buffer won't be touched by
the stream grabber until we pass it back to the stream grabber. */
unsigned char* buffer; /* Pointer to the buffer attached to the grab result. */
/* Get the buffer pointer from the result structure. Since we also got the buffer index,
we could alternatively use buffers[bufferIndex]. */
buffer = (unsigned char*) grabResult.pBuffer;
getMinMax( buffer, grabResult.SizeX, grabResult.SizeY, &min, &max );
printf( "Grabbed frame #%2d into buffer %2d. Min. gray value = %3u, Max. gray value = %3u\n",
nGrabs, (int) bufferIndex, min, max );
}
else if (grabResult.Status == Failed)
{
fprintf( stderr, "Frame %d wasn't grabbed successfully. Error code = 0x%08X\n",
nGrabs, grabResult.ErrorCode );
}
/* Once finished with the processing, requeue the buffer to be filled again. */
res = PylonStreamGrabberQueueBuffer( hStreamGrabber, grabResult.hBuffer, (void*) bufferIndex );
CHECK( res );
}
}
/* Clean up. */
/* ... Stop the camera. */
res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStop" );
CHECK( res );
/* ... Stop the image acquisition engine. */
res = PylonStreamGrabberStopStreamingIfMandatory( hStreamGrabber );
CHECK( res );
/* ... Switch-off the events. */
res = PylonDeviceFeatureFromString( hDev, "EventSelector", "ExposureEnd" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "EventNotification", "Off" );
CHECK( res );
/* ... We must issue a flush call to ensure that all pending buffers are put into the
stream grabber's output queue. */
res = PylonStreamGrabberFlushBuffersToOutput( hStreamGrabber );
CHECK( res );
/* ... The buffers can now be retrieved from the stream grabber. */
do
{
res = PylonStreamGrabberRetrieveResult( hStreamGrabber, &grabResult, &isReady );
CHECK( res );
} while (isReady);
/* ... When all buffers are retrieved from the stream grabber, they can be deregistered.
After deregistering the buffers, it is safe to free the memory. */
for (i = 0; i < NUM_IMAGE_BUFFERS; ++i)
{
res = PylonStreamGrabberDeregisterBuffer( hStreamGrabber, bufHandles[i] );
CHECK( res );
free( buffers[i] );
}
/* ... Release grabbing related resources. */
res = PylonStreamGrabberFinishGrab( hStreamGrabber );
CHECK( res );
/* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
the AOI width and height parameters) are unlocked and can be modified again. */
/* ... Close the stream grabber. */
res = PylonStreamGrabberClose( hStreamGrabber );
CHECK( res );
/* ... Deregister the callback. */
res = GenApiNodeDeregisterCallback( hNode, hCallback );
CHECK( res );
/* ... Close the event grabber.*/
res = PylonEventGrabberClose( hEventGrabber );
CHECK( res );
/* ... Release the event adapter. */
res = PylonDeviceDestroyEventAdapter( hDev, hEventAdapter );
CHECK( res );
/* ... Release the wait object container. */
res = PylonWaitObjectsDestroy( hWaitObjects );
CHECK( res );
/* ... Close and release the pylon device. The stream grabber becomes invalid
after closing the pylon device. Don't call stream grabber related methods after
closing or releasing the device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
/* ... Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
pressEnterToExit();
return EXIT_SUCCESS;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... First find out how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve more details about the error
... First find out how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Simple "image processing" function returning the minumum and maximum gray
value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax )
{
unsigned char min = 255;
unsigned char max = 0;
unsigned char val;
const unsigned char* p;
for (p = pImg; p < pImg + width * height; p++)
{
val = *p;
if (val > max)
max = val;
if (val < min)
min = val;
}
*pMin = min;
*pMax = max;
}
/* Callback will be fired when an event message contains an end-of-exposure event. */
void GENAPIC_CC endOfExposureCallback( NODE_HANDLE hNode )
{
int64_t frame;
GENAPIC_RESULT res;
res = GenApiIntegerGetValue( hNode, &frame );
CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
printf( "Got end-of-exposure event. Frame number: %lld\n", (long long) frame );
#else
printf( "Got end-of-exposure event. Frame number: %I64d\n", frame );
#endif
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
GenApiParam Sample
/*
This sample illustrates how to access the different camera
parameter types. It uses the low-level functions provided by GenApiC
instead of those provided by pylonC.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <pylonc/PylonC.h>
#define STRING_BUFFER_SIZE 512
#define CHECK(errc) if (GENAPI_E_OK != errc) printErrorAndExit(errc)
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
static void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... First find out how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve more details about the error
... First find out how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
static void SetMigrationModeEnable( PYLON_DEVICE_HANDLE hDev )
{
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
const char* pFeatureName = "MigrationModeEnable"; /* The name of the feature. */
_Bool value, bval; /* The value of the feature. */
EGenApiNodeType nodeType;
GENAPIC_RESULT res;
/* Get a handle for the device's node map. */
res = PylonDeviceGetTLNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node. */
res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named %s.\n", pFeatureName );
return;
}
/* We want a boolean feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (BooleanNode != nodeType)
{
fprintf( stderr, "'%s' is not a boolean feature.", pFeatureName );
return;
}
/* Check to see if the feature is readable. */
res = GenApiNodeIsReadable( hNode, &bval );
CHECK( res );
if (bval)
{
/* Retrieve the current state of the feature. */
res = GenApiBooleanGetValue( hNode, &value );
CHECK( res );
fprintf( stdout, "The '%s' feature is %s.", pFeatureName, value ? "on" : "off" );
/* Set a new value. */
res = GenApiNodeIsWritable( hNode, &bval );
CHECK( res );
if (bval)
{
value = 1; /* New value to turn feature on. */
fprintf( stdout, "Switching the %s feature %s.\n", pFeatureName, value ? "on" : "off" );
res = GenApiBooleanSetValue( hNode, value );
CHECK( res );
}
else
{
fprintf( stderr, "Cannot set value for feature '%s' - node not writable.\n", pFeatureName );
}
}
else
{
fprintf( stderr, "Cannot read feature '%s' - node not readable.\n", pFeatureName );
}
}
/* This function demonstrates how to check the presence, readability, and writability
of a feature. */
static void demonstrateAccessibilityCheck( PYLON_DEVICE_HANDLE hDev )
{
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
const char* pFeatureName;
_Bool val, val_read, val_write;
GENAPIC_RESULT res;
/* Get a handle for the device's node map. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Check to see if a feature is implemented at all. The 'Width' feature is likely to
be implemented by just about every existing camera. */
pFeatureName = "Width";
res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE != hNode)
{
/* Node exists, check whether feature is implemented. */
res = GenApiNodeIsImplemented( hNode, &val );
CHECK( res );
}
else
{
/* Node does not exist --> feature is not implemented. */
val = 0;
}
printf( "The '%s' feature %s implemented\n", pFeatureName, val ? "is" : "is not" );
/* This feature most likely does not exist */
pFeatureName = "Weirdness";
res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE != hNode)
{
/* Node exists, check whether feature is implemented. */
res = GenApiNodeIsImplemented( hNode, &val );
CHECK( res );
}
else
{
/* Node does not exist --> feature is not implemented. */
val = 0;
}
printf( "The '%s' feature %s implemented\n", pFeatureName, val ? "is" : "is not" );
/* Although a feature is implemented by the device, it may not be available
with the device in its current state. Check to see if the feature is currently
available. The GenApiNodeIsAvailable sets val to 0 if either the feature
is not implemented or if the feature is not currently available. */
pFeatureName = "BinningVertical";
res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE != hNode)
{
/* Node exists, check whether feature is available. */
res = GenApiNodeIsAvailable( hNode, &val );
CHECK( res );
}
else
{
/* Node does not exist --> feature is not implemented, and hence not available. */
val = 0;
}
printf( "The '%s' feature %s available\n", pFeatureName, val ? "is" : "is not" );
/* If a feature is available, it could be read-only, write-only, or both
readable and writable. Use the GenApiNodeIsReadable() and the
GenApiNodeIsReadable() functions(). It is safe to call these functions
for features that are currently not available or not implemented by the device.
A feature that is not available or not implemented is neither readable nor writable.
The readability and writability of a feature can change depending on the current
state of the device. For example, the Width parameter might not be writable when
the camera is acquiring images. */
pFeatureName = "Width";
res = GenApiNodeMapGetNode( hNodeMap, pFeatureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE != hNode)
{
/* Node exists, check whether feature is readable. */
res = GenApiNodeIsReadable( hNode, &val_read );
CHECK( res );
res = GenApiNodeIsReadable( hNode, &val_write );
CHECK( res );
}
else
{
/* Node does not exist --> feature is neither readable nor witable. */
val_read = val_write = 0;
}
printf( "The '%s' feature %s readable\n", pFeatureName, val_read ? "is" : "is not" );
printf( "The '%s' feature %s writable\n", pFeatureName, val_write ? "is" : "is not" );
printf( "\n" );
}
/* This function demonstrates how to handle integer camera parameters. */
static void demonstrateIntFeature( PYLON_DEVICE_HANDLE hDev )
{
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
static const char featureName[] = "Width"; /* Name of the feature used in this sample: AOI Width. */
int64_t val, min, max, incr; /* Properties of the feature. */
GENAPIC_RESULT res; /* Return value. */
EGenApiNodeType nodeType;
_Bool bval;
/* Get a handle for the device's node map. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node */
res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named '%s'\n", featureName );
return;
}
/* We want an integer feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (IntegerNode != nodeType)
{
fprintf( stderr, "'%s' is not an integer feature\n", featureName );
return;
}
/*
Query the current value, the range of allowed values, and the increment of the feature.
For some integer features, you are not allowed to set every value within the
value range. For example, for some cameras the Width parameter must be a multiple
of 2. These constraints are expressed by the increment value. Valid values
follow the rule: val >= min && val <= max && val == min + n * inc.
*/
res = GenApiNodeIsReadable( hNode, &bval );
CHECK( res );
if (bval)
{
res = GenApiIntegerGetMin( hNode, &min ); /* Get the minimum value. */
CHECK( res );
res = GenApiIntegerGetMax( hNode, &max ); /* Get the maximum value. */
CHECK( res );
res = GenApiIntegerGetInc( hNode, &incr ); /* Get the increment value. */
CHECK( res );
res = GenApiIntegerGetValue( hNode, &val ); /* Get the current value. */
CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
printf( "%s: min= %lld max= %lld incr=%lld Value=%lld\n", featureName, (long long) min, (long long) max, (long long) incr, (long long) val );
#else
printf( "%s: min= %I64d max= %I64d incr=%I64d Value=%I64d\n", featureName, min, max, incr, val );
#endif
res = GenApiNodeIsWritable( hNode, &bval );
CHECK( res );
if (bval)
{
/* Set the Width half-way between minimum and maximum. */
res = GenApiIntegerSetValue( hNode, min + (max - min) / incr / 2 * incr );
CHECK( res );
}
else
fprintf( stderr, "Cannot set value for feature '%s' - node not writable\n", featureName );
}
else
fprintf( stderr, "Cannot read feature '%s' - node not readable\n", featureName );
}
/* Some features involve floating point parameters. This function illustrates how to set and get floating
point parameters. */
static void demonstrateFloatFeature( PYLON_DEVICE_HANDLE hDev )
{
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
static const char featureName[] = "Gamma"; /* The name of the feature used. */
_Bool bval; /* Is the feature available? */
double min, max, value; /* Value range and current value. */
EGenApiNodeType nodeType;
GENAPIC_RESULT res; /* Return value. */
/* Get a handle for the device's node map */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node. */
res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named '%s'\n", featureName );
return;
}
/* We want a float feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (FloatNode != nodeType)
{
fprintf( stderr, "'%s' is not an floating-point feature\n", featureName );
return;
}
res = GenApiNodeIsReadable( hNode, &bval );
CHECK( res );
if (bval)
{
/* Query the value range and the current value. */
res = GenApiFloatGetMin( hNode, &min );
CHECK( res );
res = GenApiFloatGetMax( hNode, &max );
CHECK( res );
res = GenApiFloatGetValue( hNode, &value );
CHECK( res );
printf( "%s: min = %4.2f, max = %4.2f, value = %4.2f\n", featureName, min, max, value );
/* Set a new value. */
GenApiNodeIsWritable( hNode, &bval );
CHECK( res );
if (bval)
{
value = 0.5 * (min + max);
printf( "Setting %s to %4.2f\n", featureName, value );
res = GenApiFloatSetValue( hNode, value );
CHECK( res );
}
else
printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
}
else
printf( "Cannot read feature '%s' - node not readable\n", featureName );
}
/* Some features are boolean features that can be switched on and off.
This function illustrates how to access boolean features. */
static void demonstrateBooleanFeature( PYLON_DEVICE_HANDLE hDev )
{
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
static const char featureName[] = "GammaEnable"; /* The name of the feature. */
_Bool value, bval; /* The value of the feature. */
EGenApiNodeType nodeType;
GENAPIC_RESULT res; /* Return value. */
/* Get a handle for the device's node map. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node. */
res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named '%s'\n", featureName );
return;
}
/* We want a boolean feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (BooleanNode != nodeType)
{
fprintf( stderr, "'%s' is not a boolean feature\n", featureName );
return;
}
/* Check to see if the feature is readable. */
res = GenApiNodeIsReadable( hNode, &bval );
CHECK( res );
if (bval)
{
/* Retrieve the current state of the feature. */
res = GenApiBooleanGetValue( hNode, &value );
CHECK( res );
printf( "The %s features is %s\n", featureName, value ? "on" : "off" );
/* Set a new value. */
GenApiNodeIsWritable( hNode, &bval );
CHECK( res );
if (bval)
{
value = (_Bool) !value; /* New value */
printf( "Switching the %s feature %s\n", featureName, value ? "on" : "off" );
res = GenApiBooleanSetValue( hNode, value );
CHECK( res );
}
else
printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
}
else
printf( "Cannot read feature '%s' - node not readable\n", featureName );
}
/*
Regardless of the parameter's type, any parameter value can be retrieved as a string. Each parameter
can be set by passing in a string correspondingly. This function illustrates how to set and get the
Width parameter as string. As demonstrated above, the Width parameter is of the integer type.
*/
static void demonstrateFromStringToString( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "Width"; /* The name of the feature. */
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
EGenApiNodeType nodeType;
_Bool bIsReadable;
GENAPIC_RESULT res; /* Return value. */
/* Get a handle for the device's node map */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node. */
res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named '%s'\n", featureName );
return;
}
/* We want an integer feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (IntegerNode != nodeType)
{
fprintf( stderr, "'%s' is not an integer feature\n", featureName );
return;
}
/* Check to see if the feature is readable. */
res = GenApiNodeIsReadable( hNode, &bIsReadable );
CHECK( res );
if (bIsReadable)
{
size_t len;
char* buf, fixBuf[32];
_Bool bIsWritable;
/* Get the value of a feature as a string. Normally, getting the value consists of 3 steps:
1.) Determine the required buffer size.
2.) Allocate the buffer.
3.) Retrieve the value. */
/* ... Get the required buffer size. The size is queried by
passing a NULL pointer as a pointer to the buffer. */
res = GenApiNodeToString( hNode, NULL, &len );
CHECK( res );
/* ... len is set to the required buffer size (terminating zero included).
Allocate the memory and retrieve the string. */
buf = (char*) malloc( len );
res = GenApiNodeToString( hNode, buf, &len );
CHECK( res );
printf( "%s: %s\n", featureName, buf );
free( buf );
/* You are not necessarily required to query the buffer size in advance. If the buffer is
big enough, passing in a buffer and a pointer to its length will work.
When the buffer is too small, an error is returned. */
/* Passing in a buffer that is too small. */
len = 1;
res = GenApiNodeToString( hNode, fixBuf, &len );
if (res == GENAPI_E_INSUFFICIENT_BUFFER)
{
/* The buffer was too small. The required size is indicated by len. */
printf( "Buffer is too small for the value of '%s'. The required buffer size is %d\n", featureName, (int) len );
}
else
CHECK( res ); /* Unexpected return value. */
/* Passing in a buffer with sufficient size. */
len = sizeof fixBuf;
res = GenApiNodeToString( hNode, fixBuf, &len );
CHECK( res );
/* A feature can be set as a string using the GenApiNodeFromString() function.
If the content of a string can not be converted to the type of the feature, an
error is returned. */
GenApiNodeIsWritable( hNode, &bIsWritable );
CHECK( res );
if (bIsWritable)
{
res = GenApiNodeFromString( hNode, "fourty-two" ); /* Can not be converted to an integer. */
if (res != GENAPI_E_OK)
{
/* Print out an error message. */
size_t l;
char* msg;
GenApiGetLastErrorMessage( NULL, &l ); /* Retrieve buffer size for the error message. */
msg = (char*) malloc( l ); /* Provide memory. */
GenApiGetLastErrorMessage( msg, &l ); /* Retrieve the message. */
printf( "%s\n", msg );
free( msg );
}
}
else
printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
}
else
printf( "Cannot read feature '%s' - node not readable\n", featureName );
}
/* There are camera features that behave like enumerations. These features can take a value from a fixed
set of possible values. One example is the pixel format feature. This function illustrates how to deal with
enumeration features.
*/
static void demonstrateEnumFeature( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "PixelFormat";
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
EGenApiNodeType nodeType;
_Bool bval;
GENAPIC_RESULT res; /* Return value. */
/* Get a handle for the device's node map. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node. */
res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named '%s'\n", featureName );
return;
}
/* We want an enumeration feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (EnumerationNode != nodeType)
{
fprintf( stderr, "'%s' is not an enumeration feature\n", featureName );
return;
}
/* Check to see if the feature is readable. */
res = GenApiNodeIsReadable( hNode, &bval );
CHECK( res );
/* The allowed values for an enumeration feature are represented as strings. Use the
GenApiNodeFromString() and GenApiNodeToString() methods for setting and getting
the value of an enumeration feature. */
if (bval)
{
/* Symbolic names of pixel formats. */
static const char symMono8[] = "Mono8";
static const char symMono16[] = "Mono16";
static const char symYUV422Packed[] = "YUV422Packed";
size_t len; /* The length of the string. */
char value[64]; /* The current value of the feature. */
_Bool supportsMono8, supportsYUV422Packed, supportsMono16;
NODE_HANDLE hEntry;
/* Get the current value of the enumeration feature. */
len = sizeof value;
res = GenApiNodeToString( hNode, value, &len );
CHECK( res );
printf( "PixelFormat: %s\n", value );
/*
For an enumeration feature, the pylon Viewer's "Feature Documentation" window lists the
names of the possible values. Some of the values may not be supported by the device.
To check if a certain "SomeValue" value for a "SomeFeature" feature can be set, call the
GenApiNodeIsAvailable() on the node of the enum entry.
*/
/* Check to see if the Mono8 pixel format can be set. */
res = GenApiEnumerationGetEntryByName( hNode, symMono8, &hEntry );
CHECK( res );
if (hEntry != GENAPIC_INVALID_HANDLE)
{
res = GenApiNodeIsAvailable( hEntry, &supportsMono8 );
CHECK( res );
}
else
{
supportsMono8 = 0;
}
printf( "%s %s a supported value for the PixelFormat feature\n", symMono8, supportsMono8 ? "is" : "is not" );
/* Check to see if the YUV422Packed pixel format can be set. */
res = GenApiEnumerationGetEntryByName( hNode, symYUV422Packed, &hEntry );
CHECK( res );
if (hEntry != GENAPIC_INVALID_HANDLE)
{
res = GenApiNodeIsAvailable( hEntry, &supportsYUV422Packed );
CHECK( res );
}
else
{
supportsYUV422Packed = 0;
}
printf( "%s %s a supported value for the PixelFormat feature\n", symYUV422Packed, supportsYUV422Packed ? "is" : "is not" );
/* Check to see if the Mono16 pixel format can be set. */
res = GenApiEnumerationGetEntryByName( hNode, symMono16, &hEntry );
CHECK( res );
if (hEntry != GENAPIC_INVALID_HANDLE)
{
res = GenApiNodeIsAvailable( hEntry, &supportsMono16 );
CHECK( res );
}
else
{
supportsMono16 = 0;
}
printf( "%s %s a supported value for the PixelFormat feature\n", symMono16, supportsMono16 ? "is" : "is not" );
/* Before writing a value, we recommend checking if the enumeration feature is
currently writable. */
res = GenApiNodeIsWritable( hNode, &bval );
CHECK( res );
if (bval)
{
/* The PixelFormat feature is writable, set it to one of the supported values. */
if (supportsMono16)
{
printf( "Setting PixelFormat to Mono16\n" );
res = GenApiNodeFromString( hNode, symMono16 );
CHECK( res );
}
else if (supportsYUV422Packed)
{
printf( "Setting PixelFormat to YUV422Packed\n" );
res = GenApiNodeFromString( hNode, symYUV422Packed );
CHECK( res );
}
else if (supportsMono8)
{
printf( "Setting PixelFormat to Mono8\n" );
res = GenApiNodeFromString( hNode, symMono8 );
CHECK( res );
}
/* Reset the PixelFormat feature to its previous value. */
res = GenApiNodeFromString( hNode, value );
CHECK( res );
}
else
printf( "Cannot set value for feature '%s' - node not writable\n", featureName );
}
else
printf( "Cannot read feature '%s' - node not readable\n", featureName );
}
/* Enumerate all possible entries for an enumerated feature. For every entry, a selection
of properties is displayed. A loop similar to the one shown below may be part of a
GUI program that wants to fill the entries of a menu. */
static void demonstrateEnumIteration( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "PixelFormat";
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
EGenApiNodeType nodeType;
_Bool bval;
GENAPIC_RESULT res; /* Return value. */
/* Get a handle for the device's node map */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the feature node. */
res = GenApiNodeMapGetNode( hNodeMap, featureName, &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
fprintf( stderr, "There is no feature named '%s'\n", featureName );
return;
}
/* We want an enumeration feature node. */
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (EnumerationNode != nodeType)
{
fprintf( stderr, "'%s' is not an enumeration feature\n", featureName );
return;
}
/* Check to see if the feature is readable. */
res = GenApiNodeIsReadable( hNode, &bval );
CHECK( res );
if (bval)
{
size_t max, i;
/* check entries. */
res = GenApiEnumerationGetNumEntries( hNode, &max );
CHECK( res );
/* Write out header. */
printf( "Allowed values for feature '%s':\n"
"--------------\n",
featureName );
/* A loop to visit every enumeration entry node once. */
for (i = 0; i < max; i++)
{
NODE_HANDLE hEntry;
char name[128], displayName[STRING_BUFFER_SIZE], description[STRING_BUFFER_SIZE];
size_t siz;
_Bool avail;
/* Get handle for enumeration entry node. */
res = GenApiEnumerationGetEntryByIndex( hNode, i, &hEntry );
CHECK( res );
/* Get node name. */
siz = sizeof name;
res = GenApiNodeGetName( hEntry, name, &siz );
CHECK( res );
/* Get display name. */
siz = sizeof displayName;
res = GenApiNodeGetDisplayName( hEntry, displayName, &siz );
CHECK( res );
/* Get description. */
siz = sizeof description;
res = GenApiNodeGetDescription( hEntry, description, &siz );
CHECK( res );
/* Get availability. */
res = GenApiNodeIsAvailable( hEntry, &avail );
CHECK( res );
/* Write out results. */
printf( "Node name: %s\n"
"Display name: %s\n"
"Description: %s\n"
"Available: %s\n"
"--------------\n",
name, displayName, description, avail ? "yes" : "no" );
}
}
else
printf( "Cannot read feature '%s' - node not readable\n", featureName );
}
/* Traverse the feature tree, displaying all categories and all features. */
static void handleCategory( NODE_HANDLE hRoot, char* buf, unsigned int depth )
{
GENAPIC_RESULT res;
size_t bufsiz, siz, numfeat, i;
/* Write out node name. */
siz = bufsiz = STRING_BUFFER_SIZE - depth * 2;
res = GenApiNodeGetName( hRoot, buf, &siz );
CHECK( res );
/* Get the number of feature nodes in this category. */
res = GenApiCategoryGetNumFeatures( hRoot, &numfeat );
CHECK( res );
printf( "%s category has %u children\n", buf - depth * 2, (unsigned int) numfeat );
/* Increase indentation. */
*buf++ = ' ';
*buf++ = ' ';
bufsiz -= 2;
++depth;
/* Now loop over all feature nodes. */
for (i = 0; i < numfeat; ++i)
{
NODE_HANDLE hNode;
EGenApiNodeType nodeType;
/* Get next feature node and check its type. */
res = GenApiCategoryGetFeatureByIndex( hRoot, i, &hNode );
CHECK( res );
res = GenApiNodeGetType( hNode, &nodeType );
CHECK( res );
if (Category != nodeType)
{
/* A regular feature. */
EGenApiAccessMode am;
const char* amode;
siz = bufsiz;
res = GenApiNodeGetName( hNode, buf, &siz );
CHECK( res );
res = GenApiNodeGetAccessMode( hNode, &am );
CHECK( res );
switch (am)
{
case NI:
amode = "not implemented";
break;
case NA:
amode = "not available";
break;
case WO:
amode = "write only";
break;
case RO:
amode = "read only";
break;
case RW:
amode = "read and write";
break;
default:
amode = "undefined";
break;
}
printf( "%s feature - access: %s\n", buf - depth * 2, amode );
}
else
/* Another category node. */
handleCategory( hNode, buf, depth );
}
}
static void demonstrateCategory( PYLON_DEVICE_HANDLE hDev )
{
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hNode;
char buf[512];
GENAPIC_RESULT res;
/* Get a handle for the device's node map. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the root node. */
res = GenApiNodeMapGetNode( hNodeMap, "Root", &hNode );
CHECK( res );
handleCategory( hNode, buf, 0 );
}
/* There are camera features, such as starting image acquisition, that represent a command.
This function that loads the factory settings, illustrates how to execute a command feature. */
static void demonstrateCommandFeature( PYLON_DEVICE_HANDLE hDev )
{
static const char selectorName[] = "UserSetSelector";
static const char commandName[] = "UserSetLoad";
NODEMAP_HANDLE hNodeMap;
NODE_HANDLE hCommand, hSelector;
EGenApiNodeType nodeType;
_Bool bval;
GENAPIC_RESULT res; /* Return value. */
/* Get a handle for the device's node map. */
res = PylonDeviceGetNodeMap( hDev, &hNodeMap );
CHECK( res );
/* Look up the command node. */
res = GenApiNodeMapGetNode( hNodeMap, commandName, &hCommand );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hCommand)
{
fprintf( stderr, "There is no feature named '%s'\n", commandName );
return;
}
/* Look up the selector node. */
res = GenApiNodeMapGetNode( hNodeMap, selectorName, &hSelector );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hSelector)
{
fprintf( stderr, "There is no feature named '%s'\n", selectorName );
return;
}
/* We want a command feature node. */
res = GenApiNodeGetType( hCommand, &nodeType );
CHECK( res );
if (CommandNode != nodeType)
{
fprintf( stderr, "'%s' is not a command feature\n", selectorName );
return;
}
/* Before executing the user set load command, the configuration set selector must be
set to the default set. */
/* Check to see if the selector is writable. */
res = GenApiNodeIsWritable( hSelector, &bval );
CHECK( res );
if (bval)
{
/* Choose the default configuration set (with one of the factory setups chosen). */
res = GenApiNodeFromString( hSelector, "Default" );
CHECK( res );
}
else
printf( "Cannot set selector '%s' - node not writable\n", selectorName );
/* Check to see if the command is writable. */
res = GenApiNodeIsWritable( hCommand, &bval );
CHECK( res );
if (bval)
{
/* Execute the configuration set load command. */
printf( "Loading the default set.\n" );
res = GenApiCommandExecute( hCommand );
CHECK( res );
}
else
printf( "Cannot execute command '%s' - node not writable\n", commandName );
}
int
main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for configuring
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera %s\n", buf );
}
}
/* Switch on the MigrationModeEnable feature. */
SetMigrationModeEnable( hDev );
/* Demonstrate how to check the accessibility of a feature. */
demonstrateAccessibilityCheck( hDev );
puts( "" );
/* Demonstrate how to handle integer camera parameters. */
demonstrateIntFeature( hDev );
puts( "" );
/* Demonstrate how to handle floating point camera parameters. */
demonstrateFloatFeature( hDev );
puts( "" );
/* Demonstrate how to handle boolean camera parameters. */
demonstrateBooleanFeature( hDev );
puts( "" );
/* Each feature can be read as a string and also set as a string. */
demonstrateFromStringToString( hDev );
puts( "" );
/* Demonstrate how to handle enumeration camera parameters. */
demonstrateEnumFeature( hDev );
puts( "" );
/* Demonstrate how to iterate enumeration entries. */
demonstrateEnumIteration( hDev );
puts( "" );
/* Demonstrate how to execute actions. */
demonstrateCommandFeature( hDev );
puts( "" );
/* Demonstrate category nodes. */
demonstrateCategory( hDev );
/* Clean up. Close and release the pylon device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
/* Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
pressEnterToExit();
return EXIT_SUCCESS;
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
GrabTwoCameras Sample
/*
This sample illustrates how to grab images and process images
using multiple cameras simultaneously.
The sample uses a pool of buffers that are passed to a stream grabber to be filled with
image data. Once a buffer is filled and ready for processing, the buffer is retrieved from
the stream grabber, processed, and passed back to the stream grabber to be filled again.
Buffers retrieved from the stream grabber are not overwritten as long as
they are not passed back to the stream grabber.
*/
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0400
#endif
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <pylonc/PylonC.h>
#ifdef GENAPIC_LINUX_BUILD
# include <sys/timerfd.h>
# include <alloca.h>
# include <errno.h>
# include <unistd.h>
#endif
/* Limits the amount of cameras used for grabbing.
It is important to manage the available bandwidth when grabbing with multiple
cameras. This applies, for instance, if two GigE cameras are connected to the
same network adapter via a switch. To manage the bandwidth, the GevSCPD
interpacket delay parameter and the GevSCFTD transmission delay parameter can
be set for each GigE camera device. The "Controlling Packet Transmission Timing
with the Interpacket and Frame Transmission Delays on Basler GigE Vision Cameras"
Application Note (AW000649xx000) provides more information about this topic. */
#define NUM_DEVICES 2
#define NUM_BUFFERS 5 /* Number of buffers used for grabbing. */
#define GIGE_PACKET_SIZE 1500 /* Size of one Ethernet packet. */
#define GIGE_PROTOCOL_OVERHEAD 36 /* Total number of bytes of protocol overhead. */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* Calculating the minimum and maximum gray value. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax );
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevicesAvail; /* Number of available devices. */
_Bool isAvail; /* Used for checking feature availability. */
int deviceIndex; /* Index of device used in following variables. */
PYLON_WAITOBJECTS_HANDLE wos; /* Wait objects. */
#ifdef GENAPIC_WIN_BUILD
HANDLE hTimer; /* Grab timer. */
#else
int fdTimer; /* Grab timer. */
#endif
PYLON_WAITOBJECT_HANDLE woTimer; /* Timer wait object. */
/* These are camera specific variables */
PYLON_DEVICE_HANDLE hDev[NUM_DEVICES]; /* Handle for the pylon device. */
PYLON_STREAMGRABBER_HANDLE hGrabber[NUM_DEVICES]; /* Handle for the pylon stream grabber. */
unsigned char* buffers[NUM_DEVICES][NUM_BUFFERS]; /* Buffers used for grabbing. */
PYLON_STREAMBUFFER_HANDLE bufHandles[NUM_DEVICES][NUM_BUFFERS]; /* Handles for the buffers. */
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevicesAvail );
CHECK( res );
if (numDevicesAvail < NUM_DEVICES)
{
fprintf( stderr, "Not enough devices found. Found %u devices. At least %i devices needed to run this sample.\n", (unsigned int) numDevicesAvail, NUM_DEVICES );
PylonTerminate();
pressEnterToExit();
exit( EXIT_SUCCESS );
}
/* Create wait objects (must be done outside of the loop). */
res = PylonWaitObjectsCreate( &wos );
CHECK( res );
/* In this sample, we want to grab for a given amount of time, then stop. */
#ifdef GENAPIC_WIN_BUILD
/* Create a Windows timer, wrap it in a pylon C wait object, and add it to
the wait object set. */
hTimer = CreateWaitableTimer( NULL, TRUE, NULL );
if (hTimer == NULL)
{
fprintf( stderr, "CreateWaitableTimer() failed.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
res = PylonWaitObjectFromW32( hTimer, 0, &woTimer );
CHECK( res );
#else
/* Create a Linux timer, wrap it in a pylon C wait object, and add it to
the wait object set. */
fdTimer = timerfd_create( CLOCK_MONOTONIC, 0 );
if (fdTimer == -1)
{
fprintf( stderr, "timerfd_create() failed. %s\n", strerror( errno ) );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
res = PylonWaitObjectFromFd( fdTimer, &woTimer );
CHECK( res );
#endif
res = PylonWaitObjectsAdd( wos, woTimer, NULL );
CHECK( res );
/* Open cameras and set parameters. */
for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
{
PylonDeviceInfo_t di;
/* Get a handle for the device. */
res = PylonCreateDeviceByIndex( deviceIndex, &hDev[deviceIndex] );
CHECK( res );
/* Before using the device, it must be opened. Open it for setting
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev[deviceIndex], PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev[deviceIndex], "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev[deviceIndex], "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera '%s'\n", buf );
}
}
/* Set the pixel format to Mono8, where gray values will be output as 8 bit values for each pixel. */
/* ... First check to see if the device supports the Mono8 format. */
isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_PixelFormat_Mono8" );
if (!isAvail)
{
/* Feature is not available. */
fprintf( stderr, "Device doesn't support the Mono8 pixel format" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* ... Set the pixel format to Mono8. */
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "PixelFormat", "Mono8" );
CHECK( res );
/* Disable acquisition start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_TriggerSelector_AcquisitionStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame burst start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_TriggerSelector_FrameBurstStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "FrameBurstStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_TriggerSelector_FrameStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "Off" );
CHECK( res );
}
/* We will use the Continuous frame mode, i.e., the camera delivers images continuously. */
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "AcquisitionMode", "Continuous" );
CHECK( res );
res = PylonDeviceGetDeviceInfo( hDev[deviceIndex], &di );
CHECK( res );
if (strcmp( di.DeviceClass, "BaslerGigE" ) == 0)
{
/* For GigE cameras, we recommend increasing the packet size for better
performance. When the network adapter supports jumbo frames, set the packet
size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
to 1500.
Also we set the Inter-Packet and the Frame Transmission delay
so the switch can line up packets better.
*/
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPSPacketSize", GIGE_PACKET_SIZE );
CHECK( res );
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * (NUM_DEVICES - 1) );
CHECK( res );
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCFTD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * deviceIndex );
CHECK( res );
}
}
/* Allocate and register buffers for grab. */
for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
{
size_t i;
PYLON_WAITOBJECT_HANDLE hWait;
size_t payloadSize;
/* Image grabbing is done using a stream grabber.
A device may be able to provide different streams. A separate stream grabber must
be used for each stream. In this sample, we create a stream grabber for the default
stream, i.e., the first stream ( index == 0 ). */
/* Get the number of streams supported by the device and the transport layer. */
res = PylonDeviceGetNumStreamGrabberChannels( hDev[deviceIndex], &i );
CHECK( res );
if (i < 1)
{
fprintf( stderr, "The transport layer doesn't support image streams.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev[deviceIndex], 0, &hGrabber[deviceIndex] );
CHECK( res );
res = PylonStreamGrabberOpen( hGrabber[deviceIndex] );
CHECK( res );
/* Get a handle for the stream grabber's wait object. The wait object
allows waiting for buffers to be filled with grabbed data. */
res = PylonStreamGrabberGetWaitObject( hGrabber[deviceIndex], &hWait );
CHECK( res );
/* Add the stream grabber's wait object to our wait objects.
This is needed to be able to wait until at least one camera has
grabbed an image in the grab loop below. */
res = PylonWaitObjectsAdd( wos, hWait, NULL );
CHECK( res );
res = PylonStreamGrabberGetPayloadSize( hDev[deviceIndex], hGrabber[deviceIndex], &payloadSize );
CHECK( res );
/* Allocate memory for grabbing. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
buffers[deviceIndex][i] = (unsigned char*) malloc( payloadSize );
if (NULL == buffers[deviceIndex][i])
{
fprintf( stderr, "Out of memory.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
}
/* We must tell the stream grabber the number and size of the buffers we are using. */
/* .. We will not use more than NUM_BUFFERS for grabbing. */
res = PylonStreamGrabberSetMaxNumBuffer( hGrabber[deviceIndex], NUM_BUFFERS );
CHECK( res );
/* .. We will not use buffers bigger than payloadSize bytes. */
res = PylonStreamGrabberSetMaxBufferSize( hGrabber[deviceIndex], payloadSize );
CHECK( res );
/* Allocate the resources required for grabbing. After this, critical parameters
that impact the payload size must not be changed until FinishGrab() is called. */
res = PylonStreamGrabberPrepareGrab( hGrabber[deviceIndex] );
CHECK( res );
/* Before using the buffers for grabbing, they must be registered at
the stream grabber. For each registered buffer, a buffer handle
is returned. After registering, these handles are used instead of the
raw pointers. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberRegisterBuffer( hGrabber[deviceIndex], buffers[deviceIndex][i], payloadSize, &bufHandles[deviceIndex][i] );
CHECK( res );
}
/* Feed the buffers into the stream grabber's input queue. For each buffer, the API
allows passing in a pointer to additional context information. This pointer
will be returned unchanged when the grab is finished. In our example, we use the index of the
buffer as context information. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberQueueBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex][i], (void*) i );
CHECK( res );
}
}
/* The stream grabber is now prepared. As soon the camera starts to acquire images,
the image data will be grabbed into the provided buffers. */
for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
{
/* Start the image acquisition engine. */
res = PylonStreamGrabberStartStreamingIfMandatory( hGrabber[deviceIndex] );
/* do not call CHECK() here! Instead exit the loop */
if (res != GENAPI_E_OK)
{
break;
}
/* Let the camera acquire images. */
res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStart" );
/* do not call CHECK() here! Instead exit the loop */
if (res != GENAPI_E_OK)
{
break;
}
}
/* Only start the grab loop if all cameras have been "started" */
if (res == GENAPI_E_OK)
{
unsigned int nGrabs = 0;
/* Set the timer to 5 s and start it. */
#ifdef GENAPIC_WIN_BUILD
LARGE_INTEGER intv;
intv.QuadPart = -50000000I64;
if (!SetWaitableTimer( hTimer, &intv, 0, NULL, NULL, FALSE ))
{
fprintf( stderr, "SetWaitableTimer() failed.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
#else
struct itimerspec timer_value;
timer_value.it_interval.tv_sec = 0;
timer_value.it_interval.tv_nsec = 0;
timer_value.it_value.tv_sec = 5;
timer_value.it_value.tv_nsec = 0;
if (timerfd_settime( fdTimer, 0, &timer_value, NULL ) == -1)
{
fprintf( stderr, "timerfd_settime() failed. %s\n", strerror( errno ) );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
#endif
/* Grab until the timer expires. */
for (;;)
{
_Bool isReady;
size_t woidx;
unsigned char min, max;
PylonGrabResult_t grabResult;
/* Wait for the next buffer to be filled. Wait up to 1000 ms. */
res = PylonWaitObjectsWaitForAny( wos, 1000, &woidx, &isReady );
CHECK( res );
if (!isReady)
{
/* Timeout occurred. */
fputs( "Grab timeout occurred.\n", stderr );
break; /* Stop grabbing. */
}
/* If the timer has expired, exit the grab loop */
if (woidx == 0)
{
fputs( "Grabbing completed successfully.\n", stderr );
break; /* timer expired */
}
/* Account for the timer. */
--woidx;
/* Retrieve the grab result. */
res = PylonStreamGrabberRetrieveResult( hGrabber[woidx], &grabResult, &isReady );
CHECK( res );
if (!isReady)
{
/* Oops. No grab result available? We should never have reached this point.
Since the wait operation above returned without a timeout, a grab result
should be available. */
fprintf( stderr, "Failed to retrieve a grab result\n" );
break;
}
/* Check to see if the image was grabbed successfully. */
if (grabResult.Status == Grabbed)
{
/* Success. Perform image processing. Since we passed more than one buffer
to the stream grabber, the remaining buffers are filled while
we do the image processing. The processed buffer won't be touched by
the stream grabber until we pass it back to the stream grabber. */
/* Pointer to the buffer attached to the grab result
Get the buffer pointer from the result structure. Since we also got the buffer index,
we could alternatively use buffers[bufferIndex]. */
unsigned char* buffer = (unsigned char*) grabResult.pBuffer;
/* Perform processing. */
getMinMax( buffer, grabResult.SizeX, grabResult.SizeY, &min, &max );
printf( "Grabbed frame #%2u from camera %2u into buffer %2p. Min. val=%3u, Max. val=%3u\n",
nGrabs, (unsigned int) woidx, grabResult.Context, min, max );
#ifdef GENAPIC_WIN_BUILD
/* Display image */
res = PylonImageWindowDisplayImageGrabResult( woidx, &grabResult );
CHECK( res );
#endif
}
else if (grabResult.Status == Failed)
{
fprintf( stderr, "Frame %u wasn't grabbed successfully. Error code = 0x%08X\n",
nGrabs, grabResult.ErrorCode );
}
/* Once finished with the processing, requeue the buffer to be filled again. */
res = PylonStreamGrabberQueueBuffer( hGrabber[woidx], grabResult.hBuffer, grabResult.Context );
CHECK( res );
nGrabs++;
}
}
/* Clean up. */
/* Stop the image acquisition on the cameras. */
for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
{
/* ... Stop the camera. */
res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStop" );
CHECK( res );
/* ... Stop the image acquisition engine. */
res = PylonStreamGrabberStopStreamingIfMandatory( hGrabber[deviceIndex] );
CHECK( res );
}
/* Remove all wait objects from waitobjects. */
res = PylonWaitObjectsRemoveAll( wos );
CHECK( res );
res = PylonWaitObjectDestroy( woTimer );
CHECK( res );
res = PylonWaitObjectsDestroy( wos );
CHECK( res );
for (deviceIndex = 0; deviceIndex < NUM_DEVICES; ++deviceIndex)
{
size_t i;
_Bool rdy;
PylonGrabResult_t grabResult;
/* ... We must issue a flush call to ensure that all pending buffers are put into the
stream grabber's output queue. */
res = PylonStreamGrabberFlushBuffersToOutput( hGrabber[deviceIndex] );
CHECK( res );
/* ... The buffers can now be retrieved from the stream grabber. */
do
{
res = PylonStreamGrabberRetrieveResult( hGrabber[deviceIndex], &grabResult, &rdy );
CHECK( res );
} while (rdy);
/* ... When all buffers are retrieved from the stream grabber, they can be deregistered.
After deregistering the buffers, it is safe to free the memory. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberDeregisterBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex][i] );
CHECK( res );
free( buffers[deviceIndex][i] );
}
/* ... Release grabbing related resources. */
res = PylonStreamGrabberFinishGrab( hGrabber[deviceIndex] );
CHECK( res );
/* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
the AOI width and height parameters) are unlocked and can be modified again. */
/* ... Close the stream grabber. */
res = PylonStreamGrabberClose( hGrabber[deviceIndex] );
CHECK( res );
/* ... Close and release the pylon device. The stream grabber becomes invalid
after closing the pylon device. Don't call stream grabber related methods after
closing or releasing the device. */
res = PylonDeviceClose( hDev[deviceIndex] );
CHECK( res );
res = PylonDestroyDevice( hDev[deviceIndex] );
CHECK( res );
}
pressEnterToExit();
/* ... Shut down the pylon runtime system. Don't call any pylon function after
calling PylonTerminate(). */
PylonTerminate();
#ifdef GENAPIC_LINUX_BUILD
close( fdTimer );
#endif
return EXIT_SUCCESS;
}
/* This method demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... First find out how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) alloca( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Simple "image processing" function returning the minimum and maximum gray
value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax )
{
unsigned char min = 255;
unsigned char max = 0;
unsigned char val;
const unsigned char* p;
for (p = pImg; p < pImg + width * height; p++)
{
val = *p;
if (val > max)
max = val;
if (val < min)
min = val;
}
*pMin = min;
*pMax = max;
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
OverlappedGrab Sample
/*
This sample illustrates how to grab and process images asynchronously, i.e.,
while the application is processing a buffer, the acquistion of the next buffer is done
in parallel.
The sample uses a pool of buffers that are passed to a stream grabber to be filled with
image data. Once a buffer is filled and ready for processing, the buffer is retrieved from
the stream grabber, processed, and passed back to the stream grabber to be filled again.
Buffers retrieved from the stream grabber are not overwritten as long as
they are not passed back to the stream grabber.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <pylonc/PylonC.h>
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* Calculating the minimum and maximum gray value */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax );
#define NUM_GRABS 100 /* Number of images to grab. */
#define NUM_BUFFERS 5 /* Number of buffers used for grabbing. */
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
PYLON_STREAMGRABBER_HANDLE hGrabber; /* Handle for the pylon stream grabber. */
PYLON_WAITOBJECT_HANDLE hWait; /* Handle used for waiting for a grab to be finished. */
size_t payloadSize; /* Size of an image frame in bytes. */
unsigned char* buffers[NUM_BUFFERS]; /* Buffers used for grabbing. */
PYLON_STREAMBUFFER_HANDLE bufHandles[NUM_BUFFERS]; /* Handles for the buffers. */
PylonGrabResult_t grabResult; /* Stores the result of a grab operation. */
int nGrabs; /* Counts the number of buffers grabbed. */
size_t nStreams; /* The number of streams the device provides. */
_Bool isAvail; /* Used for checking feature availability. */
_Bool isReady; /* Used as an output parameter. */
size_t i; /* Counter. */
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for configuring
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera %s\n", buf );
}
}
/* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
CHECK( res );
}
/* Disable acquisition start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame burst start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* We will use the Continuous frame acquisition mode, i.e., the camera delivers
images continuously. */
res = PylonDeviceFeatureFromString( hDev, "AcquisitionMode", "Continuous" );
CHECK( res );
/* For GigE cameras, we recommend increasing the packet size for better
performance. When the network adapter supports jumbo frames, set the packet
size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
to 1500. */
/* ... Check first to see if the GigE camera packet size parameter is supported and if it is writable. */
isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
if (isAvail)
{
/* ... The device supports the packet size feature, set a value. */
res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
CHECK( res );
}
/* Image grabbing is done using a stream grabber.
A device may be able to provide different streams. A separate stream grabber must
be used for each stream. In this sample, we create a stream grabber for the default
stream, i.e., the first stream ( index == 0 ).
*/
/* Get the number of streams supported by the device and the transport layer. */
res = PylonDeviceGetNumStreamGrabberChannels( hDev, &nStreams );
CHECK( res );
if (nStreams < 1)
{
fprintf( stderr, "The transport layer doesn't support image streams\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
CHECK( res );
res = PylonStreamGrabberOpen( hGrabber );
CHECK( res );
/* Get a handle for the stream grabber's wait object. The wait object
allows waiting for buffers to be filled with grabbed data. */
res = PylonStreamGrabberGetWaitObject( hGrabber, &hWait );
CHECK( res );
/* Determine the minimum size of the grab buffer.
The size is determined by the configuration of the camera
and the stream grabber. Be aware that this may change
by changing critical parameters after this call.*/
res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
CHECK( res );
/* Allocate memory for grabbing. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
buffers[i] = (unsigned char*) malloc( payloadSize );
if (NULL == buffers[i])
{
fprintf( stderr, "Out of memory!\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
}
/* We must tell the stream grabber the number and size of the buffers
we are using. */
/* .. We will not use more than NUM_BUFFERS for grabbing. */
res = PylonStreamGrabberSetMaxNumBuffer( hGrabber, NUM_BUFFERS );
CHECK( res );
/* .. We will not use buffers bigger than payloadSize bytes. */
res = PylonStreamGrabberSetMaxBufferSize( hGrabber, payloadSize );
CHECK( res );
/* Allocate the resources required for grabbing. After this, critical parameters
that impact the payload size must not be changed until FinishGrab() is called. */
res = PylonStreamGrabberPrepareGrab( hGrabber );
CHECK( res );
/* Before using the buffers for grabbing, they must be registered at
the stream grabber. For each registered buffer, a buffer handle
is returned. After registering, these handles are used instead of the
raw pointers. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberRegisterBuffer( hGrabber, buffers[i], payloadSize, &bufHandles[i] );
CHECK( res );
}
/* Feed the buffers into the stream grabber's input queue. For each buffer, the API
allows passing in a pointer to additional context information. This pointer
will be returned unchanged when the grab is finished. In our example, we use the index of the
buffer as context information. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberQueueBuffer( hGrabber, bufHandles[i], (void*) i );
CHECK( res );
}
/* Now the stream grabber is prepared. As soon as the camera starts to acquire images,
the image data will be grabbed into the buffers provided. */
/* Start the image acquisition engine. */
res = PylonStreamGrabberStartStreamingIfMandatory( hGrabber );
CHECK( res );
/* Let the camera acquire images. */
res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStart" );
CHECK( res );
/* Grab NUM_GRABS images */
nGrabs = 0; /* Counts the number of images grabbed */
while (nGrabs < NUM_GRABS)
{
size_t bufferIndex; /* Index of the buffer */
unsigned char min, max;
/* Wait for the next buffer to be filled. Wait up to 1000 ms. */
res = PylonWaitObjectWait( hWait, 1000, &isReady );
CHECK( res );
if (!isReady)
{
/* Timeout occurred. */
fprintf( stderr, "Grab timeout occurred\n" );
break; /* Stop grabbing. */
}
/* Since the wait operation was successful, the result of at least one grab
operation is available. Retrieve it. */
res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
CHECK( res );
if (!isReady)
{
/* Oops. No grab result available? We should never have reached this point.
Since the wait operation above returned without a timeout, a grab result
should be available. */
fprintf( stderr, "Failed to retrieve a grab result\n" );
break;
}
nGrabs++;
/* Get the buffer index from the context information. */
bufferIndex = (size_t) grabResult.Context;
/* Check to see if the image was grabbed successfully. */
if (grabResult.Status == Grabbed)
{
/* Success. Perform image processing. Since we passed more than one buffer
to the stream grabber, the remaining buffers are filled while
we do the image processing. The processed buffer won't be touched by
the stream grabber until we pass it back to the stream grabber. */
unsigned char* buffer; /* Pointer to the buffer attached to the grab result. */
/* Get the buffer pointer from the result structure. Since we also got the buffer index,
we could alternatively use buffers[bufferIndex]. */
buffer = (unsigned char*) grabResult.pBuffer;
/* Perform processing. */
getMinMax( buffer, grabResult.SizeX, grabResult.SizeY, &min, &max );
printf( "Grabbed frame %2d into buffer %2d. Min. gray value = %3u, Max. gray value = %3u\n",
nGrabs, (int) bufferIndex, min, max );
#ifdef GENAPIC_WIN_BUILD
/* Display image */
res = PylonImageWindowDisplayImageGrabResult( 0, &grabResult );
CHECK( res );
#endif
}
else if (grabResult.Status == Failed)
{
fprintf( stderr, "Frame %d wasn't grabbed successfully. Error code = 0x%08X\n",
nGrabs, grabResult.ErrorCode );
}
/* Once finished with the processing, requeue the buffer to be filled again. */
res = PylonStreamGrabberQueueBuffer( hGrabber, grabResult.hBuffer, (void*) bufferIndex );
CHECK( res );
}
/* Clean up. */
/* ... Stop the camera. */
res = PylonDeviceExecuteCommandFeature( hDev, "AcquisitionStop" );
CHECK( res );
/* ... Stop the image acquisition engine. */
res = PylonStreamGrabberStopStreamingIfMandatory( hGrabber );
CHECK( res );
/* ... We must issue a flush call to ensure that all pending buffers are put into the
stream grabber's output queue. */
res = PylonStreamGrabberFlushBuffersToOutput( hGrabber );
CHECK( res );
/* ... The buffers can now be retrieved from the stream grabber. */
do
{
res = PylonStreamGrabberRetrieveResult( hGrabber, &grabResult, &isReady );
CHECK( res );
} while (isReady);
/* ... When all buffers have been retrieved from the stream grabber, they can be deregistered.
After that, it is safe to free the memory. */
for (i = 0; i < NUM_BUFFERS; ++i)
{
res = PylonStreamGrabberDeregisterBuffer( hGrabber, bufHandles[i] );
CHECK( res );
free( buffers[i] );
}
/* ... Release grabbing related resources. */
res = PylonStreamGrabberFinishGrab( hGrabber );
CHECK( res );
/* After calling PylonStreamGrabberFinishGrab(), parameters that impact the payload size (e.g.,
the AOI width and height parameters) are unlocked and can be modified again. */
/* ... Close the stream grabber. */
res = PylonStreamGrabberClose( hGrabber );
CHECK( res );
/* ... Close and release the pylon device. The stream grabber becomes invalid
after closing the pylon device. Don't call stream grabber related methods after
closing or releasing the device. */
res = PylonDeviceClose( hDev );
CHECK( res );
/* ...The device is no longer used, destroy it. */
res = PylonDestroyDevice( hDev );
CHECK( res );
pressEnterToExit();
/* ... Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
return EXIT_SUCCESS;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... First find out how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve more details about the error.
... First find out how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Simple "image processing" function returning the minimum and maximum gray
value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax )
{
unsigned char min = 255;
unsigned char max = 0;
unsigned char val;
const unsigned char* p;
for (p = pImg; p < pImg + width * height; p++)
{
val = *p;
if (val > max)
max = val;
if (val < min)
min = val;
}
*pMin = min;
*pMax = max;
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
ParametrizeCamera Sample
/*
This sample illustrates how to read and write the different camera
parameter types.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#ifdef __GNUC__
# include <alloca.h>
#endif
#include <pylonc/PylonC.h>
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
void printErrorAndExit( GENAPIC_RESULT errc );
void demonstrateAccessibilityCheck( PYLON_DEVICE_HANDLE );
void demonstrateIntFeature( PYLON_DEVICE_HANDLE );
void demonstrateInt32Feature( PYLON_DEVICE_HANDLE );
void demonstrateFloatFeature( PYLON_DEVICE_HANDLE );
void demonstrateBooleanFeature( PYLON_DEVICE_HANDLE );
void demonstrateFromStringToString( PYLON_DEVICE_HANDLE );
void demonstrateEnumFeature( PYLON_DEVICE_HANDLE );
void demonstrateCommandFeature( PYLON_DEVICE_HANDLE );
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for configuring
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera %s\n", buf );
}
}
/* Demonstrate how to check the accessibility of a feature. */
demonstrateAccessibilityCheck( hDev );
puts( "" );
/* Demonstrate how to handle integer camera parameters. */
demonstrateIntFeature( hDev );
puts( "" );
demonstrateInt32Feature( hDev );
puts( "" );
/* Demonstrate how to handle floating point camera parameters. */
demonstrateFloatFeature( hDev );
puts( "" );
/* Demonstrate how to handle boolean camera parameters. */
demonstrateBooleanFeature( hDev );
puts( "" );
/* Each feature can be read as a string and also set as a string. */
demonstrateFromStringToString( hDev );
puts( "" );
/* Demonstrate how to handle enumeration camera parameters. */
demonstrateEnumFeature( hDev );
puts( "" );
/* Demonstrate how to execute actions. */
demonstrateCommandFeature( hDev );
/* Clean up. Close and release the pylon device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
/* Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
pressEnterToExit();
return EXIT_SUCCESS;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... First find out how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve the more details about the error
... First find out how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* This function demonstrates how to check the presence, readability, and writability
of a feature. */
void demonstrateAccessibilityCheck( PYLON_DEVICE_HANDLE hDev )
{
_Bool val; /* Output of the check functions */
/* Check to see if a feature is implemented at all. */
val = PylonDeviceFeatureIsImplemented( hDev, "Width" );
printf( "The 'Width' feature %s implemented\n", val ? "is" : "isn't" );
val = PylonDeviceFeatureIsImplemented( hDev, "MyCustomFeature" );
printf( "The 'MyCustomFeature' feature %s implemented\n", val ? "is" : "isn't" );
/* Although a feature is implemented by the device, it might not be available
with the device in its current state. Check to see if the feature is currently
available. The PylonDeviceFeatureIsAvailable sets val to 0 if either the feature
is not implemented or if the feature is not currently available. */
val = PylonDeviceFeatureIsAvailable( hDev, "BinningVertical" );
printf( "The 'BinningVertical' feature %s available\n", val ? "is" : "isn't" );
/* If a feature is available, it could be read-only, write-only, or both
readable and writable. Use the PylonDeviceFeatureIsReadable() and the
PylonDeviceFeatureIsWritable() functions(). It is safe to call these functions
for features that are currently not available or not implemented by the device.
A feature that is not available or not implemented is neither readable nor writable.
The readability and writability of a feature can change depending on the current
state of the device. For example, the Width parameter might not be writable when
the camera is acquiring images. */
val = PylonDeviceFeatureIsReadable( hDev, "Width" );
printf( "The 'Width' feature %s readable\n", val ? "is" : "isn't" );
val = PylonDeviceFeatureIsReadable( hDev, "MyCustomFeature" );
printf( "The 'MyCustomFeature' feature %s readable\n", val ? "is" : "isn't" );
val = PylonDeviceFeatureIsWritable( hDev, "Width" );
printf( "The 'Width' feature %s writable\n", val ? "is" : "isn't" );
printf( "\n" );
}
/* This function demonstrates how to handle integer camera parameters. */
void demonstrateIntFeature( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "Width"; /* Name of the feature used in this sample: AOI Width */
int64_t val, min, max, incr; /* Properties of the feature */
GENAPIC_RESULT res; /* Return value */
if (PylonDeviceFeatureIsReadable( hDev, featureName ))
{
/*
Query the current value, the allowed value range, and the increment of the feature.
For some integer features, you are not allowed to set every value within the
value range. For example, for some cameras the Width parameter must be a multiple
of 2. These constraints are expressed by the increment value. Valid values
follow the rule: val >= min && val <= max && val == min + n * inc. */
res = PylonDeviceGetIntegerFeatureMin( hDev, featureName, &min ); /* Get the minimum value. */
CHECK( res );
res = PylonDeviceGetIntegerFeatureMax( hDev, featureName, &max ); /* Get the maximum value. */
CHECK( res );
res = PylonDeviceGetIntegerFeatureInc( hDev, featureName, &incr ); /* Get the increment value. */
CHECK( res );
res = PylonDeviceGetIntegerFeature( hDev, featureName, &val ); /* Get the current value. */
CHECK( res );
#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)
printf( "%s: min= %lld max= %lld incr=%lld Value=%lld\n", featureName, (long long) min, (long long) max, (long long) incr, (long long) val );
#else
printf( "%s: min= %I64d max= %I64d incr=%I64d Value=%I64d\n", featureName, min, max, incr, val );
#endif
if (PylonDeviceFeatureIsWritable( hDev, featureName ))
{
/* Set the Width half-way between minimum and maximum. */
res = PylonDeviceSetIntegerFeature( hDev, featureName, min + (max - min) / incr / 2 * incr );
CHECK( res );
}
else
fprintf( stderr, "The %s feature is not writable.\n", featureName );
}
else
fprintf( stderr, "The %s feature is not readable.\n", featureName );
}
/* The integer functions illustrated above take 64 bit integers as output parameters. There are variants
of the integer functions that accept 32 bit integers instead. The Get.... functions return
an error when the value returned by the device doesn't fit into a 32 bit integer. */
void demonstrateInt32Feature( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "Height"; /* Name of the feature used in this sample: AOI height */
int32_t val, min, max, incr; /* Properties of the feature */
GENAPIC_RESULT res; /* Return value */
if (PylonDeviceFeatureIsReadable( hDev, featureName ))
{
/*
Query the current value, the allowed value range, and the increment of the feature.
For some integer features, you are not allowed to set every value within the
value range. For example, for some cameras the Width parameter must be a multiple
of 2. These constraints are expressed by the increment value. Valid values
follow the rule: val >= min && val <= max && val == min + n * inc. */
res = PylonDeviceGetIntegerFeatureMinInt32( hDev, featureName, &min ); /* Get the minimum value. */
CHECK( res );
res = PylonDeviceGetIntegerFeatureMaxInt32( hDev, featureName, &max ); /* Get the maximum value. */
CHECK( res );
res = PylonDeviceGetIntegerFeatureIncInt32( hDev, featureName, &incr ); /* Get the increment value. */
CHECK( res );
res = PylonDeviceGetIntegerFeatureInt32( hDev, featureName, &val ); /* Get the current value. */
CHECK( res );
printf( "%s: min= %d max= %d incr=%d Value=%d\n", featureName, min, max, incr, val );
if (PylonDeviceFeatureIsWritable( hDev, featureName ))
{
/* Set the value to half its maximum */
res = PylonDeviceSetIntegerFeatureInt32( hDev, featureName, min + (max - min) / incr / 2 * incr );
CHECK( res );
}
else
fprintf( stderr, "The %s feature is not writable.\n", featureName );
}
else
fprintf( stderr, "The %s feature is not readable.\n", featureName );
}
/* Some features are floating point features. This function illustrates how to set and get floating
point parameters. */
void demonstrateFloatFeature( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "Gamma"; /* The name of the feature used */
_Bool isWritable; /* Is the feature writable? */
double min, max, value; /* Value range and current value */
GENAPIC_RESULT res; /* Return value */
if (PylonDeviceFeatureIsReadable( hDev, featureName ))
{
/* Query the value range and the current value. */
res = PylonDeviceGetFloatFeatureMin( hDev, featureName, &min );
CHECK( res );
res = PylonDeviceGetFloatFeatureMax( hDev, featureName, &max );
CHECK( res );
res = PylonDeviceGetFloatFeature( hDev, featureName, &value );
CHECK( res );
printf( "%s: min = %4.2f, max = %4.2f, value = %4.2f\n", featureName, min, max, value );
/* Set the value to half its maximum. */
isWritable = PylonDeviceFeatureIsWritable( hDev, featureName );
if (isWritable)
{
value = 0.5 * (min + max);
printf( "Setting %s to %4.2f\n", featureName, value );
res = PylonDeviceSetFloatFeature( hDev, featureName, value );
CHECK( res );
}
else
fprintf( stderr, "The %s feature is not writable.\n", featureName );
}
else
fprintf( stderr, "The %s feature is not readable.\n", featureName );
}
/* Some features are boolean features that can be switched on and off.
This function illustrates how to access boolean features. */
void demonstrateBooleanFeature( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "GammaEnable"; /* The name of the feature */
_Bool isWritable; /* Is the feature writable? */
_Bool value; /* The value of the feature */
GENAPIC_RESULT res; /* Return value */
/* Check to see if the feature is writable. */
isWritable = PylonDeviceFeatureIsWritable( hDev, featureName );
if (isWritable)
{
/* Retrieve the current state of the feature. */
res = PylonDeviceGetBooleanFeature( hDev, featureName, &value );
CHECK( res );
printf( "The %s features is %s\n", featureName, value ? "on" : "off" );
/* Set a new value. */
value = (_Bool) !value; /* New value */
printf( "Switching the %s feature %s\n", featureName, value ? "on" : "off" );
res = PylonDeviceSetBooleanFeature( hDev, featureName, value );
CHECK( res );
}
else
printf( "The %s feature isn't writable\n", featureName );
}
/*
Regardless of the parameter's type, any parameter value can be retrieved as a string. Each parameter
can be set by passing in a string correspondingly. This function illustrates how to set and get the
Width parameter as string. As demonstrated above, the Width parameter is of the integer type.
*/
void demonstrateFromStringToString( PYLON_DEVICE_HANDLE hDev )
{
static const char featureName[] = "Width"; /* The name of the feature */
size_t len;
char* buf;
char smallBuf[1];
char properBuf[32];
GENAPIC_RESULT res; /* Return value */
/* Get the value of a feature as a string. Normally getting the value consits of 3 steps:
1.) Determine the required buffer size.
2.) Allocate the buffer.
3.) Retrieve the value. */
/* ... Get the required buffer size. The size is queried by
passing a NULL pointer as a pointer to the buffer. */
res = PylonDeviceFeatureToString( hDev, featureName, NULL, &len );
CHECK( res );
/* ... Len is set to the required buffer size (terminating zero included).
Allocate the memory and retrieve the string. */
buf = (char*) alloca( len );
res = PylonDeviceFeatureToString( hDev, featureName, buf, &len );
CHECK( res );
printf( "%s: %s\n", featureName, buf );
/* You are not necessarily required to query the buffer size in advance. If the buffer is
big enough, passing in a buffer and a pointer to its length will work.
When the buffer is too small, an error is returned. */
/* Passing in a buffer that is too small */
len = sizeof( smallBuf );
res = PylonDeviceFeatureToString( hDev, featureName, smallBuf, &len );
if (res == GENAPI_E_INSUFFICIENT_BUFFER)
{
/* The buffer was too small. The required size is indicated by len. */
printf( "Buffer is too small for the value of '%s'. The required buffer size is %d\n", featureName, (int) len );
}
else
CHECK( res ); /* Unexpected return value */
/* Passing in a buffer with sufficient size. */
len = sizeof( properBuf );
res = PylonDeviceFeatureToString( hDev, featureName, properBuf, &len );
CHECK( res );
/* A feature can be set as a string using the PylonDeviceFeatureFromString() function.
If the content of a string can not be converted to the type of the feature, an
error is returned. */
res = PylonDeviceFeatureFromString( hDev, featureName, "fourty-two" ); /* Can not be converted to an integer */
if (res != GENAPI_E_OK)
{
/* Print out an error message. */
size_t l;
char* msg;
GenApiGetLastErrorMessage( NULL, &l ); /* Retrieve buffer size for the error message */
msg = (char*) malloc( l ); /* Provide memory */
GenApiGetLastErrorMessage( msg, &l ); /* Retrieve the message */
printf( "%s\n", msg );
free( msg );
}
}
/* There are camera features that behave like enumerations. These features can take a value from a fixed
set of possible values. One example is the pixel format feature. This function illustrates how to deal with
enumeration features.
*/
void demonstrateEnumFeature( PYLON_DEVICE_HANDLE hDev )
{
char value[64]; /* The current value of the feature */
size_t len; /* The length of the string */
GENAPIC_RESULT res; /* Return value */
_Bool isWritable;
_Bool supportsMono8;
_Bool supportsYUV422Packed;
_Bool supportsMono16;
/* The allowed values for an enumeration feature are represented as strings. Use the
PylonDeviceFeatureFromString() and PylonDeviceFeatureToString() methods for setting and getting
the value of an enumeration feature. */
/* Get the current value of the enumeration feature. */
len = sizeof( value );
res = PylonDeviceFeatureToString( hDev, "PixelFormat", value, &len );
CHECK( res );
printf( "PixelFormat: %s\n", value );
/*
For an enumeration feature, the pylon Viewer's "Feature Documentation" window lists the the
names of the possible values. Some of the values might not be supported by the device.
To check if a certain "SomeValue" value for a "SomeFeature" feature can be set, call the
PylonDeviceFeatureIsAvailable() function with "EnumEntry_SomeFeature_SomeValue" as an argument.
*/
/* Check to see if the Mono8 pixel format can be set. */
supportsMono8 = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
printf( "Mono8 %s a supported value for the PixelFormat feature\n", supportsMono8 ? "is" : "isn't" );
/* Check to see if the YUV422Packed pixel format can be set. */
supportsYUV422Packed = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_YUV422Packed" );
printf( "YUV422Packed %s a supported value for the PixelFormat feature\n", supportsYUV422Packed ? "is" : "isn't" );
/* Check to see if the Mono16 pixel format can be set. */
supportsMono16 = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono16" );
printf( "Mono16 %s a supported value for the PixelFormat feature\n", supportsMono16 ? "is" : "isn't" );
/* Before writing a value, we recommend checking to see if the enumeration feature is
currently writable. */
isWritable = PylonDeviceFeatureIsWritable( hDev, "PixelFormat" );
if (isWritable)
{
/* The PixelFormat feature is writable, set it to one of the supported values. */
if (supportsMono16)
{
printf( "Setting PixelFormat to Mono16\n" );
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono16" );
CHECK( res );
}
else if (supportsYUV422Packed)
{
printf( "Setting PixelFormat to YUV422Packed\n" );
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "YUV422Packed" );
CHECK( res );
}
else if (supportsMono8)
{
printf( "Setting PixelFormat to Mono8\n" );
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
CHECK( res );
}
/* Reset the PixelFormat feature to its previous value. */
PylonDeviceFeatureFromString( hDev, "PixelFormat", value );
}
}
/* There are camera features, such as starting image acquisition, that represent a command.
This function that loads the factory settings, illustrates how to execute a command feature. */
void demonstrateCommandFeature( PYLON_DEVICE_HANDLE hDev )
{
GENAPIC_RESULT res; /* Return value. */
/* Before executing the user set load command, the user set selector must be
set to the default set. Since we are focusing on the command feature,
we skip the recommended steps for checking the availability of the user set
related features and values. */
/* Choose the default configuration set (with one of the factory setups chosen). */
res = PylonDeviceFeatureFromString( hDev, "UserSetSelector", "Default" );
CHECK( res );
/* Execute the user set load command. */
printf( "Loading the default set.\n" );
res = PylonDeviceExecuteCommandFeature( hDev, "UserSetLoad" );
CHECK( res );
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
SimpleGrab Sample
/*
This sample illustrates how to use the PylonDeviceGrabSingleFrame() convenience
method for grabbing images in a loop. PylonDeviceGrabSingleFrame() grabs one
single frame in single frame mode.
Grabbing in single frame acquisition mode is the easiest way to grab images. Note: in single frame
mode the maximum frame rate of the camera can't be achieved. The full frame
rate can be achieved by setting the camera to the continuous frame acquisition
mode and by grabbing in overlapped mode, i.e., image acquisition is done in parallel
with image processing. This is illustrated in the OverlappedGrab sample program.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <pylonc/PylonC.h>
/* Simple error handling. */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message
for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* Calculating the minimum and maximum gray value of an image buffer */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax );
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
const int numGrabs = 10; /* Number of images to grab. */
size_t payloadSize = 0; /* Size of an image frame in bytes. */
unsigned char* imgBuf; /* Buffer used for grabbing. */
_Bool isAvail;
int i;
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device! */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found!\n" );
/* Before exiting a program, PylonTerminate() should be called to release
all pylon related resources. */
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for configuring
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera %s\n", buf );
}
}
/* Set the pixel format to Mono8 if available, where gray values will be output as 8 bit values for each pixel. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "PixelFormat", "Mono8" );
CHECK( res );
}
/* Disable acquisition start trigger if available */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_AcquisitionStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "AcquisitionStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame burst start trigger if available. */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameBurstStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameBurstStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* Disable frame start trigger if available */
isAvail = PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_TriggerSelector_FrameStart" );
if (isAvail)
{
res = PylonDeviceFeatureFromString( hDev, "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev, "TriggerMode", "Off" );
CHECK( res );
}
/* For GigE cameras, we recommend increasing the packet size for better
performance. If the network adapter supports jumbo frames, set the packet
size to a value > 1500, e.g., to 8192. In this sample, we only set the packet size
to 1500. */
/* ... Check first to see if the GigE camera packet size parameter is supported
and if it is writable. */
isAvail = PylonDeviceFeatureIsWritable( hDev, "GevSCPSPacketSize" );
if (isAvail)
{
/* ... The device supports the packet size feature. Set a value. */
res = PylonDeviceSetIntegerFeature( hDev, "GevSCPSPacketSize", 1500 );
CHECK( res );
}
/* Determine the required size of the grab buffer. */
{
PYLON_STREAMGRABBER_HANDLE hGrabber;
/* Temporary create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev, 0, &hGrabber );
CHECK( res );
res = PylonStreamGrabberOpen( hGrabber );
CHECK( res );
res = PylonStreamGrabberGetPayloadSize( hDev, hGrabber, &payloadSize );
CHECK( res );
res = PylonStreamGrabberClose( hGrabber );
CHECK( res );
}
/* Allocate memory for grabbing. */
imgBuf = (unsigned char*) malloc( payloadSize );
if (NULL == imgBuf)
{
fprintf( stderr, "Out of memory.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Grab some images in a loop. */
for (i = 0; i < numGrabs; ++i)
{
unsigned char min, max;
PylonGrabResult_t grabResult;
_Bool bufferReady;
/* Grab one single frame from stream channel 0. The
camera is set to single frame acquisition mode.
Wait up to 500 ms for the image to be grabbed. */
res = PylonDeviceGrabSingleFrame( hDev, 0, imgBuf, payloadSize,
&grabResult, &bufferReady, 500 );
if (GENAPI_E_OK == res && !bufferReady)
{
/* Timeout occurred. */
printf( "Frame %d: timeout\n", i + 1 );
}
CHECK( res );
/* Check to see if the image was grabbed successfully. */
if (grabResult.Status == Grabbed)
{
/* Success. Perform image processing. */
getMinMax( imgBuf, grabResult.SizeX, grabResult.SizeY, &min, &max );
printf( "Grabbed frame #%2d. Min. gray value = %3u, Max. gray value = %3u\n", i + 1, min, max );
#ifdef GENAPIC_WIN_BUILD
/* Display image */
res = PylonImageWindowDisplayImageGrabResult( 0, &grabResult );
CHECK( res );
#endif
}
else if (grabResult.Status == Failed)
{
fprintf( stderr, "Frame %d wasn't grabbed successfully. Error code = 0x%08X\n",
i + 1, grabResult.ErrorCode );
}
}
/* Clean up. Close and release the pylon device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
/* Free memory for grabbing. */
free( imgBuf );
pressEnterToExit();
/* Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
return EXIT_SUCCESS;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... Find out first how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve more details about the error.
... Find out first how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Simple "image processing" function returning the minimum and maximum gray
value of an 8 bit gray value image. */
void getMinMax( const unsigned char* pImg, int32_t width, int32_t height,
unsigned char* pMin, unsigned char* pMax )
{
unsigned char min = 255;
unsigned char max = 0;
unsigned char val;
const unsigned char* p;
for (p = pImg; p < pImg + width * height; p++)
{
val = *p;
if (val > max)
max = val;
if (val < min)
min = val;
}
*pMin = min;
*pMax = max;
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
SurpriseRemoval Sample
/*
This sample program demonstrates how to be informed about the removal of a device.
Attention:
If you build this sample in debug mode and run it using a GigE camera device, pylon will set the heartbeat
timeout to 60 minutes. This is done to allow debugging and single stepping of the code without
the camera thinking we're hung because we don't send any heartbeats.
This also means that it would normally take 60 minutes for the application to notice that a GigE device
has been disconnected.
To work around this, the heartbeat timeout will be set to 1000 ms before we remove a device and wait to
notice the removal.
*/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <pylonc/PylonC.h>
#ifdef __GNUC__
# include <unistd.h>
# define Sleep(ms) usleep(ms*1000)
#endif
static int callbackCounter = 0; /* Will be incremented by the callback function. */
/* Simple error handling */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message
for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
/* This function will be registered as a callback function that is called
when the opened device has been removed. On Windows only stdcall functions can be registered. */
void GENAPIC_CC removalCallbackFunction( PYLON_DEVICE_HANDLE hDevice );
/* Sets the heartbeat timeout. */
int64_t setHeartbeatTimeout( PYLON_DEVICE_HANDLE hDevice, int64_t timeout_ms );
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t numDevices; /* Number of available devices. */
PYLON_DEVICE_HANDLE hDev; /* Handle for the pylon device. */
PYLON_DEVICECALLBACK_HANDLE hCb; /* Required for deregistering the callback. */
int loopCount; /* Counter. */
int isGigECamera; /* 1 if the device is a GigE device. */
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all camera devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevices );
CHECK( res );
if (0 == numDevices)
{
fprintf( stderr, "No devices found.\n" );
/* Before exiting a program, PylonTerminate() should be called to release
all pylon related resources. */
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Get a handle for the first device found. */
res = PylonCreateDeviceByIndex( 0, &hDev );
CHECK( res );
/* Before using the device, it must be opened. Open it for configuring
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* Print out the name of the camera we are using. */
{
char buf[256];
size_t siz = sizeof( buf );
_Bool isReadable;
isReadable = PylonDeviceFeatureIsReadable( hDev, "DeviceModelName" );
if (isReadable)
{
res = PylonDeviceFeatureToString( hDev, "DeviceModelName", buf, &siz );
CHECK( res );
printf( "Using camera %s\n", buf );
}
}
/* Register the callback function. */
res = PylonDeviceRegisterRemovalCallback( hDev, removalCallbackFunction, &hCb );
CHECK( res );
/* For GigE cameras, the application periodically sends heartbeat signals to the camera to keep the
connection to the camera alive. If the camera doesn't receive heartbeat signals within the time
period specified by the heartbeat timeout counter, the camera resets the connection.
When the application is stopped by the debugger, the application cannot create the heartbeat signals.
For that reason, the pylon runtime extends the heartbeat timeout when debugging to 5 minutes to allow
debugging. For GigE cameras, we will set the heartbeat timeout to a shorter period before testing the
callbacks.
The heartbeat mechanism is also used for detection of device removal. When the pylon runtime doesn't
receive an acknowledge for the heartbeat signal, it is assumed that the device has been removed. A
removal callback will be fired in that case.
By decreasing the heartbeat timeout, the surprise removal will be noticed earlier. */
{
/* Find out if we are using a GigE camera. */
PylonDeviceInfo_t devInfo;
res = PylonDeviceGetDeviceInfo( hDev, &devInfo );
CHECK( res );
isGigECamera = 0 == strcmp( devInfo.DeviceClass, "BaslerGigE" );
/* Adjust the heartbeat timeout. */
if (isGigECamera)
{
setHeartbeatTimeout( hDev, 1000 ); /* 1000 ms */
}
}
/* Ask the user to disconnect a device. */
loopCount = 20 * 4;
printf( "Please disconnect the device (timeout %d s) \n", loopCount / 4 );
/* Wait until the removal has been noticed and callback function has been fired. */
do
{
/* Print a . every few seconds to tell the user we're waiting for the callback. */
if (--loopCount % 4 == 0)
{
printf( "." );
fflush( stdout );
}
Sleep( 250 );
} while (callbackCounter < 1 && loopCount >= 0); /* Check loopCount so we won't wait forever. */
if (callbackCounter < 1)
printf( "\nTimeout expired. Device hasn't been removed.\n" );
/* Clean up. */
/* ... Deregister the removal callback. */
res = PylonDeviceDeregisterRemovalCallback( hDev, hCb );
CHECK( res );
/* ....Close and release the pylon device. */
res = PylonDeviceClose( hDev );
CHECK( res );
res = PylonDestroyDevice( hDev );
CHECK( res );
/* Shut down the pylon runtime system. Don't call any pylon method after
calling PylonTerminate(). */
PylonTerminate();
pressEnterToExit();
return EXIT_SUCCESS;
}
/* The function to be called when the removal of an opened device is detected. */
void GENAPIC_CC removalCallbackFunction( PYLON_DEVICE_HANDLE hDevice )
{
PylonDeviceInfo_t di;
GENAPIC_RESULT res;
/* Print out the name of the device. It is not possible to read the name
from the camera since it has been removed. Use the device's device
information instead. For accessing the device information, no reading from
the device is required. */
/* Retrieve the device information for the removed device. */
res = PylonDeviceGetDeviceInfo( hDevice, &di );
CHECK( res );
/* Print out the name. */
printf( "\nCallback function for removal of device %s (%s).\n", di.FriendlyName, di.FullName );
/* Increment the counter to indicate that the callback has been fired. */
callbackCounter++;
}
/* If the device provides a heartbeat timeout, this function will set the heartbeat timeout.
When the device provides the parameter, the old value is returned, -1 otherwise.
The heartbeat timeout is a parameter provided by the transport layer.
The transport layer parameters are exposed as a GenApi node map that
can be retrieved from the device.
*/
int64_t setHeartbeatTimeout( PYLON_DEVICE_HANDLE hDev, int64_t timeout_ms )
{
NODEMAP_HANDLE hNodemap; /* Handle to the node map */
NODE_HANDLE hNode; /* Handle to a node, i.e., a feature */
GENAPIC_RESULT res; /* Return value */
int64_t oldTimeout; /* The current timeout value */
/* Get the node map for the transport layer parameters. */
res = PylonDeviceGetTLNodeMap( hDev, &hNodemap );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNodemap)
{
/* The device doesn't provide a transport layer node map. Nothing to do. */
return -1;
}
/* Get the node for the heartbeat timeout parameter. */
res = GenApiNodeMapGetNode( hNodemap, "HeartbeatTimeout", &hNode );
CHECK( res );
if (GENAPIC_INVALID_HANDLE == hNode)
{
/* There is no heartbeat timeout parameter. Nothing to do. */
return -1;
}
/* Get the current value. */
res = GenApiIntegerGetValue( hNode, &oldTimeout );
CHECK( res );
/* Set the new value. */
res = GenApiIntegerSetValue( hNode, timeout_ms );
CHECK( res );
/* Return the old value. */
return oldTimeout;
}
/* This function demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
size_t length;
/* Retrieve the error message.
... Find out first how big the buffer must be, */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorMessage( errMsg, &length );
fprintf( stderr, "%s (%#08x).\n", errMsg, (unsigned int) errc );
free( errMsg );
/* Retrieve more details about the error.
... Find out first how big the buffer must be, */
GenApiGetLastErrorDetail( NULL, &length );
errMsg = (char*) malloc( length );
/* ... and retrieve the message. */
GenApiGetLastErrorDetail( errMsg, &length );
fprintf( stderr, "%s\n", errMsg );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}
ActionCommands Sample
/*
This sample illustrates how to grab images
using a GigE Vision action command to trigger multiple cameras.
At least 2 connected GigE cameras are required for this sample.
*/
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0400
#endif
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <time.h>
#include <pylonc/PylonC.h>
/* Limits the amount of cameras used for grabbing.
It is important to manage the available bandwidth when grabbing with multiple
cameras. This applies, for instance, if two GigE cameras are connected to the
same network adapter via a switch. To manage the bandwidth, the GevSCPD
interpacket delay parameter and the GevSCFTD transmission delay parameter can
be set for each GigE camera device. The "Controlling Packet Transmission Timing
with the Interpacket and Frame Transmission Delays on Basler GigE Vision Cameras"
Application Note (AW000649xx000) provides more information about this topic. */
#define MAX_NUM_DEVICES 4
#define GIGE_PACKET_SIZE 1500 /* Size of one Ethernet packet. */
#define GIGE_PROTOCOL_OVERHEAD 36 /* Total number of bytes of protocol overhead. */
#define CHECK( errc ) if ( GENAPI_E_OK != errc ) printErrorAndExit( errc )
const uint32_t AllGroupMask = 0xffffffff;
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void );
/* This method demonstrates how to retrieve the error message for the last failed function call. */
void printErrorAndExit( GENAPIC_RESULT errc );
int main( void )
{
GENAPIC_RESULT res; /* Return value of pylon methods. */
size_t i; /* Generic loop variable */
size_t numDevicesEnumerated; /* Number of available devices. */
size_t numDevicesToUse; /* Number of usable devices. */
_Bool isAvail; /* Used for checking feature availability. */
size_t deviceIndex; /* Index of device used in this sample. */
PYLON_WAITOBJECTS_HANDLE wos = NULL; /* Wait objects. */
uint32_t DeviceKey; /* Random device key used in this session. It will be initialized below. */
uint32_t GroupKey; /* Group key for the devices. In this sample all devices will be in the same group. */
/* These are camera-specific variables. */
PYLON_DEVICE_HANDLE hDev[MAX_NUM_DEVICES]; /* Handle for the pylon device. */
PYLON_STREAMGRABBER_HANDLE hGrabber[MAX_NUM_DEVICES]; /* Handle for the pylon stream grabber. */
unsigned char* buffers[MAX_NUM_DEVICES]; /* Buffers used for grabbing. */
PYLON_STREAMBUFFER_HANDLE bufHandles[MAX_NUM_DEVICES]; /* Handles for the buffers. */
PylonDeviceInfo_t deviceInfos[MAX_NUM_DEVICES]; /* Information about enumerated devices */
/* Seed the random number generator. */
srand( (unsigned int) time( NULL ) );
/* Get random device key used in this session. */
DeviceKey = rand();
/* In this sample all cameras will belong to the same group. */
GroupKey = 0x24;
/* Before using any pylon methods, the pylon runtime must be initialized. */
PylonInitialize();
/* Enumerate all devices. You must call
PylonEnumerateDevices() before creating a device. */
res = PylonEnumerateDevices( &numDevicesEnumerated );
CHECK( res );
if (numDevicesEnumerated == 0)
{
fprintf( stdout, "No devices found!\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Create wait objects so we can wait for the images of each camera */
res = PylonWaitObjectsCreate( &wos );
CHECK( res );
/* =======================================================================
* Open Cameras and Set Parameters.
* ======================================================================= */
for (deviceIndex = 0; deviceIndex < numDevicesEnumerated; ++deviceIndex)
{
PylonDeviceInfo_t di;
res = PylonGetDeviceInfo( deviceIndex, &di );
CHECK( res );
if (strcmp( di.DeviceClass, "BaslerGigE" ) != 0)
{
/* Action commands are only supported by GigE cameras. */
continue;
}
/* Get a handle for the device. */
res = PylonCreateDeviceByIndex( deviceIndex, &hDev[deviceIndex] );
CHECK( res );
/* Before using the device, it must be opened. Open it for setting
parameters and for grabbing images. */
res = PylonDeviceOpen( hDev[deviceIndex], PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM );
CHECK( res );
/* When the device has been opened successfully, we remember its deviceinfo
so we can print out the name device name, etc. later */
deviceInfos[deviceIndex] = di;
/* Print out the name of the camera we are using. */
printf( "Using camera '%s'\n", deviceInfos[deviceIndex].ModelName );
isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "ActionControl" );
if (!isAvail)
{
/* Action Command feature is not available. */
fprintf( stderr, "Device doesn't support the Action Control" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_SUCCESS );
}
/* Configure the first action (Action1)*/
res = PylonDeviceSetIntegerFeatureInt32( hDev[deviceIndex], "ActionSelector", 1 );
CHECK( res );
res = PylonDeviceSetIntegerFeatureInt32( hDev[deviceIndex], "ActionDeviceKey", DeviceKey );
CHECK( res );
res = PylonDeviceSetIntegerFeatureInt32( hDev[deviceIndex], "ActionGroupKey", GroupKey );
CHECK( res );
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "ActionGroupMask", AllGroupMask );
CHECK( res );
/* Set the trigger mode to FrameStart and TriggerSource to first action. */
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSelector", "FrameStart" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerMode", "On" );
CHECK( res );
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "TriggerSource", "Action1" ); /* Action1 corresponds to ActionSelector=1. */
CHECK( res );
/* Set the pixel format to Mono8, where gray values will be output as 8-bit values for each pixel. */
/* ... First check to see if the device supports the Mono8 format. */
isAvail = PylonDeviceFeatureIsAvailable( hDev[deviceIndex], "EnumEntry_PixelFormat_Mono8" );
if (!isAvail)
{
/* Feature is not available. */
fprintf( stderr, "Device doesn't support the Mono8 pixel format.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_SUCCESS );
}
/* ... Set the pixel format to Mono8. */
res = PylonDeviceFeatureFromString( hDev[deviceIndex], "PixelFormat", "Mono8" );
CHECK( res );
/* For GigE cameras, we recommend increasing the packet size for better
performance. When the network adapter supports jumbo frames, set the packet
size to a value > 1500, e.g. to 8192. In this sample, we only set the packet size
to 1500.
Also we set the Inter-Packet and the Frame Transmission delay
so that the switch can line up packets in a better way.
*/
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPSPacketSize", GIGE_PACKET_SIZE );
CHECK( res );
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCPD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * (MAX_NUM_DEVICES - 1) );
CHECK( res );
res = PylonDeviceSetIntegerFeature( hDev[deviceIndex], "GevSCFTD", (GIGE_PACKET_SIZE + GIGE_PROTOCOL_OVERHEAD) * deviceIndex );
CHECK( res );
}
/* Remember the number of devices actually created. */
numDevicesToUse = deviceIndex;
if (numDevicesToUse == 0)
{
fprintf( stderr, "No suitable cameras found!\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_SUCCESS );
}
if (numDevicesToUse < 2)
{
printf( "WARNING: This sample works best with two or more GigE cameras supporting action commands.\n" );
}
/* ======================================================================
Allocate and Register Buffers for Grab.
====================================================================== */
for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
{
PYLON_WAITOBJECT_HANDLE hWait;
size_t payloadSize;
/* Image grabbing is done using a stream grabber.
A device may be able to provide different streams. A separate stream grabber must
be used for each stream. In this sample, we create a stream grabber for the default
stream, i.e., the first stream ( index == 0 ).
*/
/* Get the number of streams supported by the device and the transport layer. */
res = PylonDeviceGetNumStreamGrabberChannels( hDev[deviceIndex], &i );
CHECK( res );
if (i < 1)
{
fprintf( stderr, "The transport layer doesn't support image streams.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* Create and open a stream grabber for the first channel. */
res = PylonDeviceGetStreamGrabber( hDev[deviceIndex], 0, &hGrabber[deviceIndex] );
CHECK( res );
res = PylonStreamGrabberOpen( hGrabber[deviceIndex] );
CHECK( res );
/* Get a handle for the stream grabber's wait object. The wait object
allows waiting for buffers to be filled with grabbed data. */
res = PylonStreamGrabberGetWaitObject( hGrabber[deviceIndex], &hWait );
CHECK( res );
/* Add the stream grabber's wait object to our wait objects.
This is needed to be able to wait until at least one camera has
grabbed an image in the grab loop below. */
res = PylonWaitObjectsAdd( wos, hWait, NULL );
CHECK( res );
/* Determine the required size of the grab buffer. */
res = PylonStreamGrabberGetPayloadSize( hDev[deviceIndex], hGrabber[deviceIndex], &payloadSize );
CHECK( res );
/* Allocate memory for grabbing. */
buffers[deviceIndex] = (unsigned char*) malloc( payloadSize );
if (NULL == buffers[deviceIndex])
{
fprintf( stderr, "Out of memory.\n" );
PylonTerminate();
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* We must tell the stream grabber the number and size of the buffers
we are using. */
/* .. We will use one buffer for grabbing. */
res = PylonStreamGrabberSetMaxNumBuffer( hGrabber[deviceIndex], 1 );
CHECK( res );
/* .. We will not use buffers bigger than payloadSize bytes. */
res = PylonStreamGrabberSetMaxBufferSize( hGrabber[deviceIndex], payloadSize );
CHECK( res );
/* Allocate the resources required for grabbing. After this, critical parameters
that impact the payload size must not be changed until FinishGrab() is called. */
res = PylonStreamGrabberPrepareGrab( hGrabber[deviceIndex] );
CHECK( res );
/* Before using the buffers for grabbing, they must be registered at
the stream grabber. For each registered buffer, a buffer handle
is returned. After registering, these handles are used instead of the
raw pointers. */
res = PylonStreamGrabberRegisterBuffer( hGrabber[deviceIndex], buffers[deviceIndex], payloadSize, &bufHandles[deviceIndex] );
CHECK( res );
/* Feed the buffers into the stream grabber's input queue. For each buffer, the API
allows passing in a pointer to additional context information. This pointer
will be returned unchanged when the grab is finished. In our example, we use the index of the
camera as context information. */
res = PylonStreamGrabberQueueBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex], (void*) deviceIndex );
CHECK( res );
}
/* The stream grabber is now prepared. As soon as the camera starts acquiring images,
the image data will be grabbed into the provided buffers. */
for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
{
/* Let the camera acquire images. */
res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStart" );
/* Do not call CHECK() here! Instead exit the loop. */
if (res != GENAPI_E_OK)
{
break;
}
}
/* ======================================================================
Issue an ActionCommand and Retrieve the Images.
====================================================================== */
/* Only start the grab loop if all cameras have been "started" */
if (res == GENAPI_E_OK)
{
char subnet[32] = { '\0' }; /* reserve space for a dotted ip address */
size_t buflen = sizeof subnet;
PYLON_DEVICE_INFO_HANDLE hDevInfo = NULL;
/* Retrieve subnet broadcast address of first device.
We must query this GigE specific value explicitly as it isn't contained
in the generic PylonDeviceInfo_t struct. */
res = PylonDeviceGetDeviceInfoHandle( hDev[0], &hDevInfo );
CHECK( res );
res = PylonDeviceInfoGetPropertyValueByName( hDevInfo, "SubnetAddress", subnet, &buflen );
CHECK( res );
/* Trigger the camera using an action command. */
res = PylonGigEIssueActionCommand( DeviceKey, GroupKey, AllGroupMask, subnet, 0, NULL, NULL );
CHECK( res );
/* Grab one image from each camera. */
for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
{
_Bool isReady;
size_t woIndex = 0;
PylonGrabResult_t grabResult;
/* Wait for the next buffer to be filled. Wait up to 5000 ms.*/
res = PylonWaitObjectsWaitForAny( wos, 5000, &woIndex, &isReady );
CHECK( res );
if (!isReady)
{ /* Timeout occurred. */
/* Grab timeout occurred. */
fprintf( stderr, "Grab timeout occurred.\n" );
break; /* Stop grabbing. */
}
/* The woIndex corresponds to the index of the camera in handle arrays. */
/* Retrieve the grab result. */
res = PylonStreamGrabberRetrieveResult( hGrabber[woIndex], &grabResult, &isReady );
CHECK( res );
if (!isReady)
{
/* Oops. No grab result available? We should never have reached this point.
Since the wait operation above returned without a timeout, a grab result
should be available. */
fprintf( stderr, "Failed to retrieve a grab result\n" );
break;
}
/* Check to see whether the image was grabbed successfully. */
if (grabResult.Status == Grabbed && grabResult.PayloadType == PayloadType_Image)
{
/* Success. Perform image processing. Since we passed more than one buffer
to the stream grabber, the remaining buffers are filled while
we are doing the image processing. The processed buffer won't be touched by
the stream grabber until we pass it back to the stream grabber. */
/* Pointer to the buffer attached to the grab result.
Get the buffer pointer from the result structure. Since we also got the buffer index,
we could alternatively use buffers[bufferIndex]. */
/* Unsigned char* buffer = (unsigned char*) grabResult.pBuffer; */
/* Perform processing. */
printf( "Grabbed frame from camera %u into buffer.\n", (unsigned int) woIndex );
#ifdef GENAPIC_WIN_BUILD
/* Display image. */
if (woIndex <= 31)
{
res = PylonImageWindowDisplayImageGrabResult( woIndex, &grabResult );
CHECK( res );
}
#endif
}
else if (grabResult.Status == Failed)
{
/* If a buffer has been incompletely grabbed, the network bandwidth is possibly insufficient for transferring
multiple images simultaneously. See note on MAX_NUM_DEVICES. */
fprintf( stderr, "Frame wasn't grabbed successfully. Error code = 0x%08X\n",
grabResult.ErrorCode );
}
} /* for */
}
/* ========================================================================
Clean up.
======================================================================== */
/* Stop the image acquisition on the cameras. */
for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
{
/* ... Stop the camera. */
res = PylonDeviceExecuteCommandFeature( hDev[deviceIndex], "AcquisitionStop" );
CHECK( res );
}
/* Remove all wait objects from waitobjects. */
res = PylonWaitObjectsRemoveAll( wos );
CHECK( res );
res = PylonWaitObjectsDestroy( wos );
CHECK( res );
/* Do the cleanup for each camera we've set up. */
for (deviceIndex = 0; deviceIndex < numDevicesToUse; ++deviceIndex)
{
_Bool rdy;
PylonGrabResult_t grabResult;
/* Issue a flush call to ensure that all queued buffers are put into the
stream grabber's output queue. */
res = PylonStreamGrabberFlushBuffersToOutput( hGrabber[deviceIndex] );
CHECK( res );
/* The buffers can now be retrieved from the stream grabbers output queue. */
do
{
res = PylonStreamGrabberRetrieveResult( hGrabber[deviceIndex], &grabResult, &rdy );
CHECK( res );
} while (rdy);
/* After all buffers have been retrieved from the stream grabber, they can be deregistered.
After deregistering the buffers, it is safe to free the memory. */
res = PylonStreamGrabberDeregisterBuffer( hGrabber[deviceIndex], bufHandles[deviceIndex] );
CHECK( res );
free( buffers[deviceIndex] );
/* Release grabbing related resources. */
res = PylonStreamGrabberFinishGrab( hGrabber[deviceIndex] );
CHECK( res );
/* When PylonStreamGrabberFinishGrab() has been called, parameters that impact the payload size (e.g.,
the AOI width and height parameters) are unlocked and can be modified again. */
/* Close the stream grabber. */
res = PylonStreamGrabberClose( hGrabber[deviceIndex] );
CHECK( res );
/* Close and release the pylon device. The stream grabber becomes invalid
after closing the pylon device. Don't call stream grabber related methods after
closing or releasing the device. */
res = PylonDeviceClose( hDev[deviceIndex] );
CHECK( res );
res = PylonDestroyDevice( hDev[deviceIndex] );
CHECK( res );
}
pressEnterToExit();
/* ... Shut down the pylon runtime system. Don't call any pylon function after
calling PylonTerminate(). */
PylonTerminate();
return EXIT_SUCCESS;
}
/* This method demonstrates how to retrieve the error message for the last failed
function call. */
void printErrorAndExit( GENAPIC_RESULT errc )
{
char* errMsg;
char* errDetail;
size_t length = 0;
/* Retrieve the error message using double calling. */
GenApiGetLastErrorMessage( NULL, &length );
errMsg = (char*) malloc( length );
GenApiGetLastErrorMessage( errMsg, &length );
/* Get the detailed error message. */
length = 0;
GenApiGetLastErrorDetail( NULL, &length );
errDetail = (char*) malloc( length );
GenApiGetLastErrorDetail( errDetail, &length );
fprintf( stderr, "%s (%#08x).\n%s\n", errMsg, (unsigned int) errc, errDetail );
free( errDetail );
free( errMsg );
PylonTerminate(); /* Releases all pylon resources. */
pressEnterToExit();
exit( EXIT_FAILURE );
}
/* This function can be used to wait for user input at the end of the sample program. */
void pressEnterToExit( void )
{
fprintf( stderr, "\nPress enter to exit.\n" );
while (getchar() != '\n');
}