Getting Started with Python¶
PySpin provides Python bindings for the Spinnaker SDK, enabling rapid prototyping, data science workflows, and scripting-based camera control.
Installation & Compatibility¶
| Platform | Python | Architecture | Notes |
|---|---|---|---|
| Windows 10/11 | 3.10, 3.12 | x64 | Full support |
| Ubuntu 20.04 | 3.8 | x64, ARM64, ARMHF | Full support |
| Ubuntu 22.04 | 3.10 | x64, ARM64, ARMHF | Full support |
| Ubuntu 24.04 | 3.12 | x64, ARM64, ARMHF | Full support |
| macOS 14 (Sonoma)+ | 3.10, 3.12 | x64, ARM64 | Full support |
# Install from SDK wheel (bundled with SDK installer)
pip install spinnaker_python-4.4.0.246-cp312-cp312-win_amd64.whl
# Optional but recommended
pip install numpy opencv-python
Version Pinning
The PySpin wheel is tightly coupled to the installed Spinnaker runtime
version. Mismatched versions will raise ImportError or cause runtime
crashes. Always install the wheel that ships with your SDK version.
Prerequisites¶
- Spinnaker SDK installed (download)
- Python 3.10 or later
- PySpin package installed (included with the SDK installer or via
pip install spinnaker-python) - A supported Teledyne camera connected via USB3 or GigE
Basic Acquisition Example¶
The canonical example is located at src/PySpin/Examples/Python3/Acquisition.py in the SDK.
Below is the essential workflow:
import PySpin
def main():
# Get the system singleton
system = PySpin.System.GetInstance()
# Retrieve camera list
cam_list = system.GetCameras()
# Get the first camera
cam = cam_list.GetByIndex(0)
# Initialize
cam.Init()
# Configure acquisition mode
cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_Continuous)
# Begin acquisition
cam.BeginAcquisition()
# Retrieve images
for i in range(10):
image = cam.GetNextImage(1000)
if not image.IsIncomplete():
print(f"Image {i}: {image.GetWidth()} x {image.GetHeight()}")
# Release image
image.Release()
# Clean up
cam.EndAcquisition()
cam.DeInit()
del cam
cam_list.Clear()
system.ReleaseInstance()
if __name__ == "__main__":
main()
Key Concepts¶
QuickSpin Properties¶
PySpin supports QuickSpin for direct property access:
GenICam Node Access¶
For advanced features, access the GenICam nodemap:
nodemap = cam.GetNodeMap()
node_acquisition_mode = PySpin.CEnumerationPtr(
nodemap.GetNode("AcquisitionMode"))
NumPy Integration¶
Convert images to NumPy arrays for processing:
Feature Snippets¶
Hardware Triggering¶
Configure the camera for asynchronous hardware triggering on GPIO Line 0 (rising edge):
import PySpin
nodemap = cam.GetNodeMap()
# Disable trigger mode first to configure source
node_trigger_mode = PySpin.CEnumerationPtr(nodemap.GetNode('TriggerMode'))
node_trigger_mode_off = node_trigger_mode.GetEntryByName('Off')
node_trigger_mode.SetIntValue(node_trigger_mode_off.GetValue())
# Set trigger selector to FrameStart
node_trigger_selector = PySpin.CEnumerationPtr(nodemap.GetNode('TriggerSelector'))
node_trigger_selector_framestart = node_trigger_selector.GetEntryByName('FrameStart')
node_trigger_selector.SetIntValue(node_trigger_selector_framestart.GetValue())
# Set trigger source to Line0 (hardware)
node_trigger_source = PySpin.CEnumerationPtr(nodemap.GetNode('TriggerSource'))
node_trigger_source_line0 = node_trigger_source.GetEntryByName('Line0')
node_trigger_source.SetIntValue(node_trigger_source_line0.GetValue())
# Enable trigger mode
node_trigger_mode_on = node_trigger_mode.GetEntryByName('On')
node_trigger_mode.SetIntValue(node_trigger_mode_on.GetValue())
Setting Exposure Time¶
Set exposure to 20 milliseconds (20000 microseconds) using the QuickSpin API:
# Turn off auto exposure
cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Off)
# Set exposure time in microseconds
cam.ExposureTime.SetValue(20000.0)
Setting Gain¶
Adjust analog gain using the QuickSpin API:
# Turn off auto gain
cam.GainAuto.SetValue(PySpin.GainAuto_Off)
# Set gain to 10.5 dB
cam.Gain.SetValue(10.5)
Event Handling¶
Handle device arrival and removal events using event handler classes:
import PySpin
class InterfaceEventHandler(PySpin.InterfaceEventHandler):
def OnDeviceArrival(self, camera):
print('Device arrived: serial %s' % camera.TLDevice.DeviceSerialNumber.GetValue())
def OnDeviceRemoval(self, camera):
print('Device removed')
# Register the handler on a system interface
handler = InterfaceEventHandler()
interface = system.GetInterfaces()[0]
interface.RegisterEventHandler(handler)
Chunk Data¶
Chunk data appends metadata such as frame counter, exposure time, and image dimensions to each acquired image:
nodemap = cam.GetNodeMap()
# Activate chunk mode
chunk_mode_active = PySpin.CBooleanPtr(nodemap.GetNode('ChunkModeActive'))
if PySpin.IsWritable(chunk_mode_active):
chunk_mode_active.SetValue(True)
# Enable a specific chunk (e.g., ExposureTime)
chunk_selector = PySpin.CEnumerationPtr(nodemap.GetNode('ChunkSelector'))
entry = chunk_selector.GetEntryByName('ExposureTime')
chunk_selector.SetIntValue(entry.GetValue())
chunk_enable = PySpin.CBooleanPtr(nodemap.GetNode('ChunkEnable'))
if PySpin.IsWritable(chunk_enable):
chunk_enable.SetValue(True)
# Retrieve chunk data from a grabbed image
chunk_data = image.GetChunkData()
exposure = chunk_data.GetExposureTime()
Error Handling¶
Use PySpin.SpinnakerException to catch SDK errors:
Design Conventions¶
Context Manager Pattern¶
Always use with blocks or explicit cleanup — PySpin wraps native C++ handles
and the GC cannot guarantee timely release:
import PySpin
system = PySpin.System.GetInstance()
cam_list = system.GetCameras()
try:
for cam in cam_list:
cam.Init()
try:
# ... configure and acquire ...
pass
finally:
cam.DeInit()
del cam
finally:
cam_list.Clear()
system.ReleaseInstance()
Do not rely on garbage collection
PySpin objects wrap native handles. Always call .DeInit(), .Clear(),
and .ReleaseInstance() explicitly. Relying on del or GC to clean up
camera handles will cause resource leaks and may hang subsequent sessions.
QuickSpin Property Access¶
QuickSpin exposes camera settings as typed Python properties — prefer this over raw node map access for common parameters:
# Raw node map access (verbose)
node_map = cam.GetNodeMap()
node = PySpin.CEnumerationPtr(node_map.GetNode("ExposureMode"))
node.SetIntValue(node.GetEntryByName("Timed").GetValue())
# QuickSpin equivalent (preferred)
cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed)
cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Off)
cam.ExposureTime.SetValue(5000.0) # microseconds
NumPy Integration¶
PySpin.Image provides direct NumPy array access without copying:
image_result = cam.GetNextImage(1000)
if not image_result.IsIncomplete():
# Convert and get as NumPy array
converted = image_result.Convert(
PySpin.PixelFormat_Mono8,
PySpin.HQ_LINEAR
)
arr = converted.GetNDArray() # shape: (H, W) or (H, W, C)
# Optional: use with OpenCV
import cv2
cv2.imwrite("frame.png", arr)
image_result.Release()
Pixel Format Compatibility
NumPy access via GetNDArray() requires conversion to an unpacked
format first (Mono8, RGB8, BGR8). Packed formats like BayerRG12p
will raise a SpinnakerException.
Error Handling¶
PySpin raises PySpin.SpinnakerException on failure — wrap acquisition
loops accordingly:
try:
cam.BeginAcquisition()
except PySpin.SpinnakerException as ex:
print(f"Acquisition failed: {ex}")
raise
Quick Reference¶
System & Discovery¶
| Class / Function | Description |
|---|---|
PySpin.System.GetInstance() |
Obtain the system singleton |
PySpin.System.ReleaseInstance() |
Release the system singleton |
PySpin.System.GetCameras() |
Enumerate connected cameras |
PySpin.System.GetLibraryVersion() |
Query SDK version at runtime |
Camera Lifecycle¶
| Method | Description |
|---|---|
Camera.Init() |
Initialize the camera |
Camera.DeInit() |
De-initialize and release the camera |
Camera.BeginAcquisition() |
Start image acquisition |
Camera.EndAcquisition() |
Stop image acquisition |
Camera.GetNextImage(timeout) |
Retrieve next image with timeout (ms) |
Image Handling¶
| Method | Description |
|---|---|
Image.IsIncomplete() |
Check for incomplete transfer |
Image.GetNDArray() |
Get image data as NumPy array |
Image.GetData() |
Get raw byte buffer |
Image.GetWidth() |
Image width in pixels |
Image.GetHeight() |
Image height in pixels |
Image.GetPixelFormat() |
Current pixel format enum |
Image.Convert(format, algorithm) |
Convert to a different pixel format |
Image.Release() |
Return image to acquisition buffer pool |
Node Map & Configuration¶
| Method | Description |
|---|---|
Camera.GetNodeMap() |
Access GenICam node map |
Camera.GetTLDeviceNodeMap() |
Access transport layer device node map |
Camera.GetTLStreamNodeMap() |
Access transport layer stream node map |
INodeMap.GetNode(name) |
Retrieve a node by name |
CIntegerPtr.GetValue() |
Read an integer node |
CIntegerPtr.SetValue(val) |
Write an integer node |
CEnumerationPtr.SetIntValue(val) |
Set an enumeration node |
Events & Callbacks¶
| Class | Description |
|---|---|
ImageEventHandler |
Base class for image arrival callbacks |
DeviceEventHandler |
Base class for device events (e.g. exposure end) |
LoggingEventHandler |
Base class for SDK log message callbacks |
Camera.RegisterEventHandler() |
Attach an event handler to a camera |
Camera.UnregisterEventHandler() |
Detach an event handler |
class MyImageHandler(PySpin.ImageEventHandler):
def OnImageEvent(self, image):
arr = image.GetNDArray()
print(f"Frame received: {arr.shape}")
handler = MyImageHandler()
cam.RegisterEventHandler(handler)
cam.BeginAcquisition()
# ... handler fires on each frame arrival ...
cam.EndAcquisition()
cam.UnregisterEventHandler(handler)
Exception Reference¶
PySpin raises PySpin.SpinnakerException on failure. The exception exposes
the same SDK error details as the underlying C++ layer:
| Attribute | Description |
|---|---|
str(ex) |
Human-readable error message |
errorcode |
Numeric SDK error code (spinError) |
filename |
Source file where the error originated |
functionname |
Function name where the error originated |
linenumber |
Line number where the error originated |
fullmessage |
Concatenated full error details |
try:
cam.BeginAcquisition()
except PySpin.SpinnakerException as ex:
print(f"Error {ex.errorcode}: {ex}")
Common error codes are listed in the C API Error Code Reference.
Known Issues & Limitations¶
SWIG Wrapper Overhead
PySpin is a SWIG-generated wrapper. High-frequency calls into the SDK (e.g. reading node values in a tight acquisition loop) incur per-call marshalling overhead. Move node lookups outside acquisition loops and cache node pointers where possible.
# Bad — node lookup on every frame
while acquiring:
gain = cam.GetNodeMap().GetNode("Gain")
# Good — cache outside the loop
gain_node = PySpin.CFloatPtr(cam.GetNodeMap().GetNode("Gain"))
while acquiring:
val = gain_node.GetValue()
NumPy Array Lifetime
Arrays returned by GetNDArray() reference SDK-managed memory.
If you need to retain the data after calling Image.Release(),
copy the array explicitly:
arr = image.GetNDArray().copy() # detach from SDK buffer
image.Release()
# arr is now safe to use after release
Further Reading¶
- Python API Reference — Full module and class documentation
- Python Programmer's Guide — Black level, gamma, white balance, logging, sequencer, Logic Blocks, and more
- Programmer's Guide — GenICam nodes, QuickSpin, sequencer, and more (all languages)
- Streaming Drivers — GigE driver configuration
- SDK Example: Acquisition.py — Full acquisition example
- SDK Example: Trigger.py — Hardware and software triggering
- SDK Example: Exposure_QuickSpin.py — Exposure control
- SDK Example: ChunkData.py — Chunk data