Seek ThermalThermal sensing for mission critical applications
Guide

Examples

Real-world code examples


Overview

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.


Prerequisites

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)

Installation

The example scripts ship with the repository and do not require a separate installation step. To get them:

  1. Clone the repository (if you have not already):

    git clone https://github.com/seekthermal/seekcamera-python.git
    cd seekcamera-python
    
  2. Install the package and its core dependency:

    pip install seekcamera
    
  3. Install optional dependencies for the OpenCV-based examples:

    pip install numpy opencv-python Pillow
    
  4. Run an example from the repository root:

    python examples/seekcamera-simple.py
    python examples/seekcamera-opencv.py
    python examples/seekcamera-opencv_record.py
    

    Each script is self-contained and can be executed directly.


Configuration

The examples expose several runtime behaviors you can adjust by editing the script directly before running.

SettingWhere to changeDefaultEffect
IO typeSeekCameraManager(SeekCameraIOType.USB)USBChange to a bitwise OR of SeekCameraIOType values (e.g., USB | SPI) to manage cameras on multiple transports simultaneously.
Frame formatcamera.capture_session_start(SeekCameraFrameFormat.*)Varies by exampleSelects 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 palettecamera.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 modecamera.shutter_mode = SeekCameraShutterMode.*AUTOThe 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>.csvOne file per connected camera, named with the unique chip ID. Change the prefix string as needed.
Output filename (video)cv2.VideoWriter("myVideo.avi", ...)myVideo.aviThe AVI file produced at the end of a recording session.
Frame wait timeoutrenderer.frame_condition.wait(150.0 / 1000.0)150 msMaximum 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.

Usage

All three examples share the same fundamental callback-driven pattern:

  1. Create a SeekCameraManager as a context manager.
  2. Register an event callback (on_event) that fires when a camera connects, disconnects, or encounters an error.
  3. Inside the CONNECT branch of the event callback, register a frame callback (on_frame) and call capture_session_start.
  4. In the frame callback, access the frame data through the camera_frame object using the format you requested (e.g., camera_frame.thermography_float or camera_frame.color_argb8888).
  5. 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.


Examples

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:

KeyAction
cSave the current frame as a JPEG (image<counter>.jpg)
rStart recording (press again to stop and write myVideo.avi)
qQuit 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

Troubleshooting

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) or PATH (Windows) includes its directory. Reconnect the USB cable and check lsusb / Device Manager.

OSError: Failed to open file (CSV example)

  • Symptom: The on_event callback prints Failed 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.wait is 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 that capture_session_start is called with SeekCameraFrameFormat.COLOR_ARGB8888 to match the color_argb8888 attribute accessed in on_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 r to 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.