Python (PySpin) Programmer's Guide¶
This guide covers Python-specific usage patterns for the Spinnaker SDK. For language-agnostic architecture concepts (GenICam nodes, transport layer, QuickSpin overview), see the Programmer's Guide.
Examples¶
The SDK ships with ready-to-run example scripts in src/PySpin/Examples/Python3/.
See the Python Examples reference for full source listings.
| Example | Description |
|---|---|
AcquireAndDisplay.py |
Acquire images and display them in a GUI window |
Acquisition.py |
Enumerate cameras, start acquisition, and grab images |
AcquisitionGenDC.py |
Acquire multi-component images using the GenDC format |
AcquisitionMultipleCamera.py |
Capture images from multiple cameras simultaneously |
BufferHandling.py |
Explore the four buffer handling modes (NewestFirst, NewestOnly, OldestFirst, OldestFirstOverwrite) |
ChunkData.py |
Retrieve chunk data from images (frame counter, exposure, etc.) |
CounterAndTimer.py |
Configure a PWM signal on a GPIO line using the counter and timer |
Enumeration.py |
Enumerate interfaces and cameras |
Enumeration_QuickSpin.py |
Enumerate interfaces and cameras using the QuickSpin API |
EnumerationEvents.py |
Explore arrival and removal events on interfaces |
Exposure_QuickSpin.py |
Configure a custom exposure time using the QuickSpin API |
FileAccess_QuickSpin.py |
Read and write images using the camera File Access feature |
ImageChannelStatistics.py |
Retrieve per-channel image statistics and optionally save or display them |
ImageEvents.py |
Acquire images using the image event handler |
ImageFormatControl.py |
Configure custom image size and pixel format |
ImageFormatControl_QuickSpin.py |
Configure image size and pixel format using the QuickSpin API |
Inference.py |
Run on-camera inference (classification / detection) and retrieve results |
Logging.py |
Create a logging event handler |
LookupTable.py |
Configure lookup tables for per-pixel intensity customization |
NodeMapCallback.py |
Create, register, use, and unregister node map callbacks |
NodeMapInfo.py |
Retrieve node map information |
NodeMapInfo_QuickSpin.py |
Interact with nodes using the QuickSpin API |
SaveToVideo.py |
Save images to video (uncompressed, MJPG, H.264 AVI, H.264 MP4) |
Sequencer.py |
Capture multiple images with different parameters in a sequence |
SpinUpdate.py |
Update camera firmware |
StereoAcquisition.py |
Acquire image sets from a stereo camera |
StereoGPIO.py |
Configure the GPIO of a stereo camera |
Trigger.py |
Configure hardware or software triggering |
Trigger_QuickSpin.py |
Configure triggering using the QuickSpin API |
Camera XML¶
Every GenICam-compliant camera ships with an XML description file that defines features, register mappings, and dependencies between features. Spinnaker caches this XML in binary format for faster startup. Cached XML files are located at:
Instantiating Cameras¶
The System singleton is the entry point for all camera access. Always release
the system at program exit.
import PySpin
system = PySpin.System.GetInstance()
cam_list = system.GetCameras()
num_cameras = cam_list.GetSize()
for i, cam in enumerate(cam_list):
cam.Init()
# ... use cam ...
cam.DeInit()
del cam
cam_list.Clear()
system.ReleaseInstance()
Release order matters
Always clear the camera list and delete all camera references before calling
system.ReleaseInstance(). Releasing the system while cameras are still
referenced will raise a SpinnakerException.
Popular Features¶
PySpin supports two styles for configuring camera features:
- QuickSpin API — recommended for common parameters; fewer lines, IDE
auto-complete, statically typed properties on the
Cameraobject. - GenAPI — required for advanced or camera-specific nodes not exposed by
QuickSpin; access via
cam.GetNodeMap().
Enumeration¶
system = PySpin.System.GetInstance()
cam_list = system.GetCameras()
for cam_idx in range(cam_list.GetSize()):
cam = cam_list.GetByIndex(cam_idx)
cam.Init()
# ... process cam ...
cam.DeInit()
del cam
cam_list.Clear()
system.ReleaseInstance()
Asynchronous Hardware Triggering¶
Configure GPIO Line 0 as the trigger source with rising-edge activation:
node_map = cam.GetNodeMap()
node_trigger_mode = PySpin.CEnumerationPtr(node_map.GetNode("TriggerMode"))
node_trigger_mode.SetIntValue(
node_trigger_mode.GetEntryByName("On").GetValue())
node_trigger_source = PySpin.CEnumerationPtr(node_map.GetNode("TriggerSource"))
node_trigger_source.SetIntValue(
node_trigger_source.GetEntryByName("Line0").GetValue())
node_trigger_selector = PySpin.CEnumerationPtr(node_map.GetNode("TriggerSelector"))
node_trigger_selector.SetIntValue(
node_trigger_selector.GetEntryByName("FrameStart").GetValue())
node_trigger_activation = PySpin.CEnumerationPtr(
node_map.GetNode("TriggerActivation"))
node_trigger_activation.SetIntValue(
node_trigger_activation.GetEntryByName("RisingEdge").GetValue())
Setting Black Level¶
Black level is the DC offset applied to the video signal, expressed as a percentage of the full-scale range.
node_map = cam.GetNodeMap()
node_black_level_selector = PySpin.CEnumerationPtr(
node_map.GetNode("BlackLevelSelector"))
node_black_level_selector.SetIntValue(
node_black_level_selector.GetEntryByName("All").GetValue())
node_black_level = PySpin.CFloatPtr(node_map.GetNode("BlackLevel"))
node_black_level.SetValue(1.5)
Setting Exposure Time¶
Exposure time is specified in microseconds. Auto exposure must be disabled before setting a manual value.
nodemap = cam.GetNodeMap()
node_exposure_auto = PySpin.CEnumerationPtr(nodemap.GetNode("ExposureAuto"))
node_exposure_auto.SetIntValue(
node_exposure_auto.GetEntryByName("Off").GetValue())
node_exposure_mode = PySpin.CEnumerationPtr(nodemap.GetNode("ExposureMode"))
node_exposure_mode.SetIntValue(
node_exposure_mode.GetEntryByName("Timed").GetValue())
exposure_time = PySpin.CFloatPtr(nodemap.GetNode("ExposureTime"))
exposure_time.SetValue(20000.0)
Setting Gain¶
Setting Gamma¶
Setting White Balance¶
node_map = cam.GetNodeMap()
node_balance_white_auto = PySpin.CEnumerationPtr(
node_map.GetNode("BalanceWhiteAuto"))
node_balance_white_auto.SetIntValue(
node_balance_white_auto.GetEntryByName("Off").GetValue())
node_balance_ratio_selector = PySpin.CEnumerationPtr(
node_map.GetNode("BalanceRatioSelector"))
node_balance_ratio = PySpin.CFloatPtr(node_map.GetNode("BalanceRatio"))
node_balance_ratio_selector.SetIntValue(
node_balance_ratio_selector.GetEntryByName("Blue").GetValue())
node_balance_ratio.SetValue(2.0)
node_balance_ratio_selector.SetIntValue(
node_balance_ratio_selector.GetEntryByName("Red").GetValue())
node_balance_ratio.SetValue(2.0)
Accessing Raw Bayer Data¶
Raw image data is accessible as a NumPy array via GetNDArray(). For Bayer
formats the array is the raw mosaic — each element maps to one pixel location.
result_image = cam.GetNextImage()
data = result_image.GetNDArray()
# For BayerRG8 on a 640x480 camera (RGGB tile):
# data[0, 0] -> Row 0, Col 0 -> Red (R)
# data[0, 1] -> Row 0, Col 1 -> Green (G)
# data[1, 0] -> Row 1, Col 0 -> Green (G)
# data[1, 1] -> Row 1, Col 1 -> Blue (B)
Identifying the Bayer pattern
The PixelColorFilter node reports the Bayer tile mapping for your camera
(e.g. BayerRGGB for BayerRG8). Use ImageProcessor.Convert() to
demosaic to RGB8 or BGR8 before passing to OpenCV or other libraries.
Setting Number of Image Buffers¶
The default buffer count is 10. Increase it for burst or high-frame-rate acquisitions to reduce dropped frames.
s_node_map = cam.GetTLStreamNodeMap()
node_buffer_count_mode = PySpin.CEnumerationPtr(
s_node_map.GetNode("StreamBufferCountMode"))
node_buffer_count_mode.SetIntValue(
node_buffer_count_mode.GetEntryByName("Manual").GetValue())
node_buffer_count = PySpin.CIntegerPtr(
s_node_map.GetNode("StreamBufferCountManual"))
node_buffer_count.SetValue(11)
Basic Features¶
Event Handling¶
Spinnaker provides two event classes for reacting to connectivity changes and camera-generated signals.
Interface Events¶
Interface events fire when cameras are connected or disconnected on a USB or GigE interface.
class InterfaceEventHandler(PySpin.InterfaceEventHandler):
def OnDeviceArrival(self, camera):
node_map_tl = camera.GetTLDeviceNodeMap()
serial = PySpin.CStringPtr(
node_map_tl.GetNode("DeviceSerialNumber")).GetValue()
print(f"Camera arrived — serial: {serial}")
def OnDeviceRemoval(self, camera):
node_map_tl = camera.GetTLDeviceNodeMap()
serial = PySpin.CStringPtr(
node_map_tl.GetNode("DeviceSerialNumber")).GetValue()
print(f"Camera removed — serial: {serial}")
handler = InterfaceEventHandler()
interface_list = system.GetInterfaces()
interface = interface_list.GetByIndex(0)
interface.RegisterEventHandler(handler)
Device Events¶
Device events fire on camera-generated signals such as start or end of exposure.
Enable the desired event via EventSelector and EventNotification before
registering the handler.
node_map = cam.GetNodeMap()
# Select the ExposureEnd event
node_event_selector = PySpin.CEnumerationPtr(node_map.GetNode("EventSelector"))
node_event_selector.SetIntValue(
node_event_selector.GetEntryByName("ExposureEnd").GetValue())
# Enable notification for the selected event
node_event_notification = PySpin.CEnumerationPtr(
node_map.GetNode("EventNotification"))
node_event_notification.SetIntValue(
node_event_notification.GetEntryByName("On").GetValue())
class DeviceEventHandler(PySpin.DeviceEventHandler):
def OnDeviceEvent(self, event_name):
print(f"Device event: {event_name} ID: {self.GetDeviceEventId()}")
device_event_handler = DeviceEventHandler()
cam.RegisterEventHandler(device_event_handler)
Grabbing Images¶
Use GetNextImage() to retrieve frames. Always check the image status and
release the image when done. ImagePtr is a smart pointer — the underlying
buffer is returned to the driver's pool on Release().
cam.BeginAcquisition()
result_image = cam.GetNextImage(1000) # timeout in ms
if result_image.GetImageStatus() != PySpin.IMAGE_NO_ERROR:
print(f"Image error: {result_image.GetImageStatus()}")
else:
# ... process image ...
pass
result_image.Release()
Image Status Values¶
| Status constant | Description |
|---|---|
IMAGE_NO_ERROR |
Image returned without errors |
IMAGE_CRC_CHECK_FAILED |
Image failed CRC check |
IMAGE_INSUFFICIENT_SIZE |
Image size is smaller than expected |
IMAGE_MISSING_PACKETS |
Image has missing packets |
IMAGE_LEADER_BUFFER_SIZE_INCONSISTENT |
Image leader is incomplete |
IMAGE_TRAILER_BUFFER_SIZE_INCONSISTENT |
Image trailer is incomplete |
IMAGE_PACKETID_INCONSISTENT |
Image has an inconsistent packet ID |
IMAGE_DATA_INCOMPLETE |
Image data is incomplete |
IMAGE_UNKNOWN_ERROR |
Image has an unknown error |
Image Pointer¶
ImagePtr is a smart pointer to an Image object. Multiple pointers can
reference the same object simultaneously. Always obtain image pointers from
cam.GetNextImage() or PySpin.Image.Create() — never instantiate ImagePtr
directly and then call Create() on it.
# Correct: obtain pointer from camera
result_image = cam.GetNextImage()
# Correct: duplicate — both variables reference the same image object
duplicate = result_image
# Correct: create an offline image from a raw buffer
good_image = PySpin.Image.Create(
width, height, offset_x, offset_y,
PySpin.PixelFormat_BayerRG8, data)
# INCORRECT: do not instantiate ImagePtr directly and call Create on it
illegal = PySpin.ImagePtr # raw uninitialised pointer
illegal.Create(...) # invalid — will raise or crash
Error Handling¶
All SDK failures raise PySpin.SpinnakerException.
Loading and Saving Images¶
Load raw image bytes from disk, wrap them in a PySpin Image, convert, and
save in any supported format (jpg, png, bmp, tiff, ppm, pgm, raw).
import numpy as np
import PySpin
offline_image_width = 1280
offline_image_height = 1024
offline_offset_x = 0
offline_offset_y = 0
# Load raw bytes from disk into a NumPy buffer
offline_data = np.fromfile("capture.raw", dtype=np.ubyte)
# Wrap the buffer in a PySpin Image
load_image = PySpin.Image.Create(
offline_image_width, offline_image_height,
offline_offset_x, offline_offset_y,
PySpin.PixelFormat_BayerRG8,
offline_data,
)
# Convert to Mono8 and save as JPEG
processor = PySpin.ImageProcessor()
result_image = processor.Convert(load_image, PySpin.PixelFormat_Mono8)
result_image.Save("offline.jpg")
Advanced Features¶
Chunk Data¶
Chunk data appends per-frame metadata directly to the image stream. The exact chunks available depend on the camera model; consult the camera's Technical Reference Manual. A chunk-enabled image is structured as:
# Enable ExposureTime chunk (QuickSpin)
cam.ChunkSelector.SetValue(PySpin.ChunkSelector_ExposureTime)
cam.ChunkEnable.SetValue(True)
# Activate chunk mode
cam.ChunkModeActive.SetValue(True)
# Retrieve chunk data from an acquired image
chunk_data = result_image.GetChunkData()
current_exposure = chunk_data.GetExposureTime()
Sequencer¶
The sequencer lets you cycle through a pre-defined set of acquisition parameter states automatically. It acts as a state machine: each state specifies camera settings (exposure, gain, trigger, etc.) and an event that triggers the transition to the next state.
Configure the sequencer from SpinView's Sequencer tab, or use the
Sequencer.py example included with the SDK. For full documentation see the
camera's Technical Reference under Using the Sequencer Feature.
Logic Block¶
A Logic Block is a collection of combinatorial logic and flip-flops inside the camera that generates custom internal signals without host-side intervention. Each Logic Block contains two 3-input LUTs (Value LUT and Enable LUT) whose 8-bit truth tables are fully programmable.
Logic Blocks can be used to build custom trigger gating, exposure masking, or signal routing entirely within the camera firmware. For full documentation see the camera's Technical Reference under Using Logic Blocks.
Logging¶
Spinnaker supports five logging levels (most to least verbose):
| Level constant | Description |
|---|---|
SPINNAKER_LOG_LEVEL_DEBUG |
Low-level diagnostic information |
SPINNAKER_LOG_LEVEL_INFO |
Recurring events generated per image |
SPINNAKER_LOG_LEVEL_NOTICE |
Camera arrival, initialization, start/stop, feature changes |
SPINNAKER_LOG_LEVEL_WARN |
Recoverable failures |
SPINNAKER_LOG_LEVEL_ERROR |
Non-recoverable failures (default) |
Levels are inclusive — enabling DEBUG also captures all higher-priority levels.
SpinView saves log output to C:\ProgramData\Spinnaker\Logs by default.
import PySpin
class LoggingEventHandler(PySpin.LoggingEventHandler):
def OnLogEvent(self, logging_event_data):
print(f"[{logging_event_data.GetCategoryName()}] "
f"{logging_event_data.GetLogMessage()}")
system = PySpin.System.GetInstance()
logging_event_handler = LoggingEventHandler()
system.RegisterLoggingEventHandler(logging_event_handler)
system.SetLoggingEventPriorityLevel(PySpin.SPINNAKER_LOG_LEVEL_ERROR)
Further Reading¶
- Getting Started — Python — Quick-start workflow and basic acquisition
- Python API Reference — Full class and method documentation
- Programmer's Guide — GenICam architecture, QuickSpin, sequencer, and events (all languages)
- Python API Reference — Full class and method documentation (Sphinx-generated)