Skip to content

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:

C:\ProgramData\Spinnaker\XML     (Windows)
/var/tmp/Spinnaker/XML           (Linux)

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.


PySpin supports two styles for configuring camera features:

  • QuickSpin API — recommended for common parameters; fewer lines, IDE auto-complete, statically typed properties on the Camera object.
  • 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:

cam.TriggerMode.SetValue(PySpin.TriggerMode_On)
cam.TriggerSource.SetValue(PySpin.TriggerSource_Line0)
cam.TriggerSelector.SetValue(PySpin.TriggerSelector_FrameStart)
cam.TriggerActivation.SetValue(PySpin.TriggerActivation_RisingEdge)
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.

cam.BlackLevelSelector.SetValue(PySpin.BlackLevelSelector_All)
cam.BlackLevel.SetValue(1.5)  # 1.5%
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.

cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Off)
cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed)
cam.ExposureTime.SetValue(20000.0)  # 20 ms
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

cam.GainAuto.SetValue(PySpin.GainAuto_Off)
cam.Gain.SetValue(10.5)  # dB
nodemap = cam.GetNodeMap()

node_gain_auto = PySpin.CEnumerationPtr(nodemap.GetNode("GainAuto"))
node_gain_auto.SetIntValue(
    node_gain_auto.GetEntryByName("Off").GetValue())

gain = PySpin.CFloatPtr(nodemap.GetNode("Gain"))
gain.SetValue(10.5)

Setting Gamma

cam.GammaEnable.SetValue(True)
cam.Gamma.SetValue(1.5)
nodemap = cam.GetNodeMap()

node_gamma_enable = PySpin.CBooleanPtr(nodemap.GetNode("GammaEnable"))
node_gamma_enable.SetValue(True)

gamma = PySpin.CFloatPtr(nodemap.GetNode("Gamma"))
gamma.SetValue(1.5)

Setting White Balance

cam.BalanceWhiteAuto.SetValue(PySpin.BalanceWhiteAuto_Off)

# Blue channel
cam.BalanceRatioSelector.SetValue(PySpin.BalanceRatioSelector_Blue)
cam.BalanceRatio.SetValue(2.0)

# Red channel
cam.BalanceRatioSelector.SetValue(PySpin.BalanceRatioSelector_Red)
cam.BalanceRatio.SetValue(2.0)
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.

try:
    cam.Init()
except PySpin.SpinnakerException as ex:
    print(f"Error: {ex}")

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:

Leader | Image Data | Chunk Information | Trailer
# 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