Examples
Real-world code examples
This page walks you through the bundled example scripts included with seekcamera-python. Each example demonstrates a distinct integration pattern — from writing raw thermography data to CSV files, to rendering live color video with OpenCV, to capturing still images and recording video clips. Studying these examples is the fastest way to understand how the callback-driven API works and to adapt the patterns to your own application.
Before running any example, make sure you have the following in place:
- seekcamera-python installed in your Python environment (see the Installation guide)
- Seek Thermal SDK shared library installed and accessible on your system's library path
- A supported Seek Thermal OEM camera core connected via USB
- Python 3.6+
- NumPy — required by the CSV example (
pip install numpy) - OpenCV (
cv2) — required by the OpenCV examples (pip install opencv-python) - Pillow (
PIL) — required by the record example (pip install Pillow) - Read/write permission to the working directory (the CSV and image/video examples write files to disk)
The example scripts ship with the repository and do not require a separate installation step. To get them:
-
Clone the repository (if you have not already):
git clone https://github.com/seekthermal/seekcamera-python.git cd seekcamera-python -
Install the package and its core dependency:
pip install seekcamera -
Install optional dependencies for the OpenCV-based examples:
pip install numpy opencv-python Pillow -
Run an example from the repository root:
python examples/seekcamera-simple.py python examples/seekcamera-opencv.py python examples/seekcamera-opencv_record.pyEach script is self-contained and can be executed directly.
The examples expose several runtime behaviors you can adjust by editing the script directly before running.
| Setting | Where to change | Default | Effect |
|---|---|---|---|
| IO type | SeekCameraManager(SeekCameraIOType.USB) | USB | Change to a bitwise OR of SeekCameraIOType values (e.g., USB | SPI) to manage cameras on multiple transports simultaneously. |
| Frame format | camera.capture_session_start(SeekCameraFrameFormat.*) | Varies by example | Selects the pixel format delivered to your frame callback. Use THERMOGRAPHY_FLOAT for raw temperature values, COLOR_ARGB8888 for color-mapped images ready to display. |
| Color palette | camera.color_palette = SeekCameraColorPalette.* | TYRIAN (OpenCV examples) | Controls the color-to-temperature mapping applied to COLOR_ARGB8888 frames. Set this property before calling capture_session_start. |
| Shutter mode | camera.shutter_mode = SeekCameraShutterMode.* | AUTO | The record example switches to MANUAL shutter while recording to prevent NUC events from interrupting the video, then restores AUTO when recording stops. |
| Output filename (CSV) | open("thermography-" + camera.chipid + ".csv", "w") | thermography-<chipid>.csv | One file per connected camera, named with the unique chip ID. Change the prefix string as needed. |
| Output filename (video) | cv2.VideoWriter("myVideo.avi", ...) | myVideo.avi | The AVI file produced at the end of a recording session. |
| Frame wait timeout | renderer.frame_condition.wait(150.0 / 1000.0) | 150 ms | Maximum time the main thread blocks waiting for a new frame before looping. Increase if you see missed frames; decrease for a more responsive UI loop. |
All three examples share the same fundamental callback-driven pattern:
- Create a
SeekCameraManageras a context manager. - Register an event callback (
on_event) that fires when a camera connects, disconnects, or encounters an error. - Inside the
CONNECTbranch of the event callback, register a frame callback (on_frame) and callcapture_session_start. - In the frame callback, access the frame data through the
camera_frameobject using the format you requested (e.g.,camera_frame.thermography_floatorcamera_frame.color_argb8888). - When the camera disconnects, call
capture_session_stop.
Because frame callbacks run on a background thread, any data shared with the main thread (such as the Renderer object in the OpenCV examples) must be protected with a synchronization primitive — the examples use threading.Condition for this purpose.
Passing context to callbacks:
Both register_event_callback and register_frame_available_callback accept an optional user_data argument that is forwarded to your callback as its last parameter. This is how the CSV example passes an open file handle and the OpenCV examples pass the Renderer object — no global state required.
Supporting multiple cameras:
The SeekCameraManager automatically tracks every camera attached to the system. The OpenCV examples demonstrate a renderer.busy guard so that only one camera is rendered at a time; remove this guard and extend the pattern if you need to process frames from several cameras simultaneously.
1 — CSV Thermography Export (seekcamera-simple.py)
Captures raw floating-point temperature frames and appends each one to a CSV file named after the camera's chip ID. This is the simplest end-to-end integration and a good starting point for custom data-logging pipelines.
from time import sleep
import numpy as np
from seekcamera import (
SeekCameraIOType,
SeekCameraManager,
SeekCameraManagerEvent,
SeekCameraFrameFormat,
)
def on_frame(camera, camera_frame, file):
frame = camera_frame.thermography_float
print("frame available: {cid} (size: {w}x{h})".format(
cid=camera.chipid, w=frame.width, h=frame.height
))
np.savetxt(file, frame.data, fmt="%.1f")
def on_event(camera, event_type, event_status, _user_data):
print("{}: {}".format(str(event_type), camera.chipid))
if event_type == SeekCameraManagerEvent.CONNECT:
try:
file = open("thermography-" + camera.chipid + ".csv", "w")
except OSError as e:
print("Failed to open file: %s" % str(e))
return
camera.register_frame_available_callback(on_frame, file)
camera.capture_session_start(SeekCameraFrameFormat.THERMOGRAPHY_FLOAT)
elif event_type == SeekCameraManagerEvent.DISCONNECT:
camera.capture_session_stop()
elif event_type == SeekCameraManagerEvent.ERROR:
print("{}: {}".format(str(event_status), camera.chipid))
with SeekCameraManager(SeekCameraIOType.USB) as manager:
manager.register_event_callback(on_event)
while True:
sleep(1.0)
Expected output (printed to stdout while running):
SeekCameraManagerEvent.CONNECT: AB1234CD
frame available: AB1234CD (size: 320x240)
frame available: AB1234CD (size: 320x240)
...
A file named thermography-AB1234CD.csv is created (or appended to) in the working directory, containing one row of space-separated temperature values per pixel row per frame.
2 — Live Color Video with OpenCV (seekcamera-opencv.py)
Displays a real-time color-mapped thermal video stream in an OpenCV window. Uses a threading.Condition to safely hand frames from the background callback thread to the main rendering thread.
from threading import Condition
import cv2
from seekcamera import (
SeekCameraIOType, SeekCameraColorPalette,
SeekCameraManager, SeekCameraManagerEvent,
SeekCameraFrameFormat, SeekCamera, SeekFrame,
)
class Renderer:
def __init__(self):
self.busy = False
self.frame = SeekFrame()
self.camera = SeekCamera()
self.frame_condition = Condition()
self.first_frame = True
def on_frame(_camera, camera_frame, renderer):
with renderer.frame_condition:
renderer.frame = camera_frame.color_argb8888
renderer.frame_condition.notify()
def on_event(camera, event_type, event_status, renderer):
print("{}: {}".format(str(event_type), camera.chipid))
if event_type == SeekCameraManagerEvent.CONNECT:
if renderer.busy:
return
renderer.busy = True
renderer.camera = camera
renderer.first_frame = True
camera.color_palette = SeekCameraColorPalette.TYRIAN
camera.register_frame_available_callback(on_frame, renderer)
camera.capture_session_start(SeekCameraFrameFormat.COLOR_ARGB8888)
elif event_type == SeekCameraManagerEvent.DISCONNECT:
if renderer.camera == camera:
camera.capture_session_stop()
renderer.camera = None
renderer.frame = None
renderer.busy = False
elif event_type == SeekCameraManagerEvent.ERROR:
print("{}: {}".format(str(event_status), camera.chipid))
window_name = "Seek Thermal - Python OpenCV Sample"
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
with SeekCameraManager(SeekCameraIOType.USB) as manager:
renderer = Renderer()
manager.register_event_callback(on_event, renderer)
while True:
with renderer.frame_condition:
if renderer.frame_condition.wait(150.0 / 1000.0):
img = renderer.frame.data
if renderer.first_frame:
(height, width, _) = img.shape
cv2.resizeWindow(window_name, width * 2, height * 2)
renderer.first_frame = False
cv2.imshow(window_name, img)
key = cv2.waitKey(1)
if key == ord("q"):
break
if not cv2.getWindowProperty(window_name, cv2.WND_PROP_VISIBLE):
break
cv2.destroyWindow(window_name)
Expected output:
- An OpenCV window opens displaying a live Tyrian-palette thermal image.
- The terminal prints connect/disconnect events.
- Press q or close the window to exit.
3 — Capture Stills and Record Video (seekcamera-opencv_record.py)
Extends the OpenCV viewer with keyboard-driven still capture and video recording. Frames are saved as numbered JPEG files; at the end of a recording session they are compiled into an AVI file. The shutter is switched to manual mode during recording to avoid NUC interruptions.
# (Full Renderer / on_frame / on_event setup identical to Example 2 — see the file)
# Key bindings added in the main loop:
key = cv2.waitKey(1)
if key == ord("q"): # quit
break
if key == ord("c"): # capture one JPEG
capture = True
if key == ord("r"): # toggle recording on/off
if not record:
record = True
renderer.camera.shutter_mode = SeekCameraShutterMode.MANUAL
else:
record = False
renderer.camera.shutter_mode = SeekCameraShutterMode.AUTO
# ... compile image*.jpg → myVideo.avi ...
Keyboard controls at runtime:
| Key | Action |
|---|---|
c | Save the current frame as a JPEG (image<counter>.jpg) |
r | Start recording (press again to stop and write myVideo.avi) |
q | Quit the application |
Expected output:
user controls:
c: capture
r: record
q: quit
SeekCameraManagerEvent.CONNECT: AB1234CD
Recording! Press 'r' to stop recording
Note: shutter is disabled while recording...so keep the videos relatively short
Recording stopped and video is in myVideo.avi
No camera detected / manager fires no CONNECT event
- Symptom: The script runs silently; no event output appears.
- Likely cause: The Seek Thermal SDK shared library is not on the system library path, or the camera is not connected.
- Fix: Verify the SDK is installed and that
LD_LIBRARY_PATH(Linux/macOS) orPATH(Windows) includes its directory. Reconnect the USB cable and checklsusb/ Device Manager.
OSError: Failed to open file (CSV example)
- Symptom: The
on_eventcallback printsFailed to open file: [Errno 13] Permission denied. - Likely cause: The working directory is read-only, or a previous run left the file locked.
- Fix: Run the script from a directory you own with write permission, or change the output path in the
open(...)call.
OpenCV window appears black / no image rendered
- Symptom: The window opens but stays blank; no frame data is displayed.
- Likely cause: The
frame_condition.waitis timing out before any frames arrive, often because the camera is still initializing or the frame format is mismatched. - Fix: Increase the wait timeout (
150.0 / 1000.0) temporarily to rule out a timing issue. Confirm thatcapture_session_startis called withSeekCameraFrameFormat.COLOR_ARGB8888to match thecolor_argb8888attribute accessed inon_frame.
ImportError: No module named 'cv2' (OpenCV examples)
- Symptom: Script exits immediately with an import error.
- Likely cause: OpenCV is not installed in the active Python environment.
- Fix:
pip install opencv-python
ImportError: No module named 'PIL' (record example)
- Symptom: Script exits immediately with an import error.
- Likely cause: Pillow is not installed.
- Fix:
pip install Pillow
Video (myVideo.avi) is missing frames or has wrong frame rate
- Symptom: The recorded video plays too fast, too slow, or has gaps.
- Likely cause: The shutter fired during recording (adding blank NUC frames), or the elapsed time calculation is skewed by a very short recording.
- Fix: Keep recordings short as advised by the console message. Ensure you press
rto stop before unplugging the camera. The frame rate is computed from actual timestamps (ts_last - ts_first), so extremely brief recordings may produce an inaccurate FPS.
SeekCameraManagerEvent.ERROR printed repeatedly
- Symptom: The terminal floods with error events and the chip ID.
- Likely cause: A USB bandwidth issue, a faulty cable, or an incompatible SDK version.
- Fix: Try a different USB port (prefer USB 3.0 or directly on the host controller rather than a hub). Confirm the installed SDK version matches the version expected by
seekcamera-python.