Skip to main content

FLIR camera control via Bonsai for murine shift work experiments

Project description

msw-flir-bonsai

FLIR camera acquisition for MurineShiftWork via Bonsai subprocesses.

Each camera runs in an isolated Bonsai subprocess — a crash in one camera does not affect others or the main behaviour task. Workflow XMLs for FlyCapture (PointGrey2) and Spinnaker cameras are shipped as package data.


Installation

pip install msw-flir-bonsai

Or with uv:

uv add msw-flir-bonsai

Runtime dependencies: numpy, pandas. No FLIR Python SDK is required on the acquisition machine — Bonsai handles all camera communication.


Requirements

Bonsai (Windows only)

Install Bonsai and the following NuGet packages via the Bonsai package manager (Tools → Manage Packages):

Package Purpose Tested version
Bonsai.Core Core reactive framework 2.8.5
Bonsai.Design Editor UI 2.8.5
Bonsai.Editor Editor shell 2.8.5
Bonsai.System I/O operators (CsvWriter, etc.) 2.8.5
Bonsai.Vision VideoWriter, image processing 2.8.5
Bonsai.Vision.Design Vision editor support 2.8.5
Bonsai.Scripting.IronPython IronPython inline scripts 2.8.5
Bonsai.Scripting.IronPython.Design IronPython editor 2.8.5
Bonsai.PointGrey2 FlyCapture2 camera driver 0.3.0
Bonsai.Spinnaker Spinnaker camera driver 0.4.0

Camera drivers (Windows)

Driver Version Notes
FlyCapture2 SDK 2.13.3 Required for PointGrey / FLIR Grasshopper cameras
Spinnaker SDK 3.x Required for FLIR Blackfly S / BFS cameras

Note: FlyCapture2 and Spinnaker are mutually exclusive on the same machine in some versions. If using Spinnaker cameras, install only the Spinnaker SDK.

Bonsai executable path

Set the BONSAI_EXE environment variable on the acquisition machine:

setx BONSAI_EXE "C:\Users\<user>\AppData\Local\Bonsai\Bonsai.exe"

Or pass bonsai_exe= directly to BonsaiCameraRunner.


Quick start

from msw_flir_bonsai.runner import BonsaiCameraRunner

runner = BonsaiCameraRunner(
    workflow="run-flir-flycap-1cam",   # or "run-flir-spinnaker-1cam"
    output_dir=r"D:\DATA\video",
    session="mouse001__20260518_120000",
    cam_index=0,
    fps=60,
    driver="flycap",
)
runner.start()

# ... main task runs on Linux, camera runs on Windows acquisition machine ...

runner.stop()
runner.wait(timeout=10)

Multiple cameras

from msw_flir_bonsai.runner import MultiCameraRunner

cameras = MultiCameraRunner.from_config(
    n_cameras=2,
    driver="flycap",
    output_dir=r"D:\DATA\video",
    session="mouse001__20260518_120000",
    fps=60,
)
cameras.start()
# each camera is an independent subprocess — one crash does not stop the other
cameras.stop()

CLI

msw-flir find-bonsai                         # locate Bonsai.exe
msw-flir list-cameras --driver flycap        # enumerate connected cameras
msw-flir test-record --cam-index 0 --fps 30  # 5-second test recording
msw-flir run D:\DATA\video mouse001 --n-cameras 2 --fps 60

Output files

Each Bonsai workflow creates a session directory and writes:

<output_dir>/<session>/<session>__<datetime>/
    <session>__<datetime>__cam1.avi     # video
    <session>__<datetime>__cam1.csv     # per-frame metadata

CSV columns (FlyCapture):

Column Description
frame_counter Hardware frame counter (rolls over at 32-bit)
timestamp_raw Embedded hardware timestamp (seconds, cycles every 128 s)
gpio_state GPIO input state (0/1) — records TTL barcode and trial pulses

Timestamp preprocessing

from msw_flir_bonsai.timestamps import preprocess_camera_csv, detect_dropped_frames

df = preprocess_camera_csv(
    "cam1.csv",
    ts_cycle_s=128.0,       # FlyCapture rollover period; np.inf for Spinnaker
    session_start_s=None,   # set to subtract session t0 if known
)
# df["timestamp_s"]    — unwrapped monotonic timestamps
# df["frame_counter"]  — unwrapped frame counter
# df["gpio_state"]     — TTL/barcode input

drops = detect_dropped_frames(df, expected_fps=60.0)

Alignment

Two channels link camera frames to Bpod behaviour timestamps:

1. TTL barcodes

Periodic binary barcode pulses recorded on both the camera GPIO and the Bpod BNC output. Partial barcodes are handled via Hamming-distance matching (up to 2 bit errors tolerated).

from msw_flir_bonsai.alignment import extract_camera_barcodes, align_barcodes

cam_barcodes = extract_camera_barcodes(df)          # [(time_s, value), ...]
bpod_barcodes = [...]                               # from Bpod session data
offset_s = align_barcodes(bpod_barcodes, cam_barcodes)
df["timestamp_bpod"] = df["timestamp_s"] + offset_s

2. Trial TTL edges (fallback)

The sequence task and others pulse a Bpod BNC line at trial start/end. The camera GPIO records these transitions at frame resolution, providing per-frame trial alignment even when barcodes are absent or incomplete.

from msw_flir_bonsai.alignment import align_ttl_edges

bpod_trial_starts = [...]    # trial-start times from Bpod session YAML
offset_s = align_ttl_edges(df, bpod_trial_starts)

Development setup

git clone https://github.com/murineshiftwork/msw-flir-bonsai.git
cd msw-flir-bonsai
uv sync --extra dev
uv run pre-commit install --hook-type pre-commit --hook-type commit-msg

Running tests

uv run pytest

Integration tests against a real Bonsai installation (Windows only):

set BONSAI_EXE=C:\Users\<user>\AppData\Local\Bonsai\Bonsai.exe
uv run pytest tests/integration/ -v

Release workflow

  1. Work on a feature/ or fix/ branch, committing with cz commit
  2. Open a PR — CI (lint + tests + secrets scan) must pass before merge
  3. Merge to main → version bump and tag are created automatically
  4. Tag triggers release: GitHub release + PyPI publish

License

See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

msw_flir_bonsai-0.4.0.tar.gz (28.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

msw_flir_bonsai-0.4.0-py3-none-any.whl (29.7 kB view details)

Uploaded Python 3

File details

Details for the file msw_flir_bonsai-0.4.0.tar.gz.

File metadata

  • Download URL: msw_flir_bonsai-0.4.0.tar.gz
  • Upload date:
  • Size: 28.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for msw_flir_bonsai-0.4.0.tar.gz
Algorithm Hash digest
SHA256 29b32f8c2ef0d206b78ebc4c640217cea2a62f2b68bbcdee12ba81d172130b70
MD5 5f0c265f6bed532d41c3a5ba820d1e25
BLAKE2b-256 ac9ae54692dc5f01359c068eb52bcb669e42f4f912486e83143ffb655af05df8

See more details on using hashes here.

File details

Details for the file msw_flir_bonsai-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: msw_flir_bonsai-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 29.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for msw_flir_bonsai-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3cf4805eebc30f69a2a2da3eafe8694869e0c7dbc5907d882cb636eefa7ed2f2
MD5 d4a923ebaa2270fd0b99bb5f57bea690
BLAKE2b-256 4fb1a6edb3975d7f36058dff537fa17026016a68a81cc16be5451a8a8efb1e59

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page