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.1.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.1-py3-none-any.whl (29.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: msw_flir_bonsai-0.4.1.tar.gz
  • Upload date:
  • Size: 28.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","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.1.tar.gz
Algorithm Hash digest
SHA256 ee9b305fc33fbafef5da34856f8d4cb6141724443448e0d166bfd61eada7836a
MD5 80efa952cd1a34e4c8f50d7915f01d6a
BLAKE2b-256 6712ad04ff3f78909aa683c392aa09e447f17b07a917cf6cb66248476ab58b00

See more details on using hashes here.

File details

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

File metadata

  • Download URL: msw_flir_bonsai-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 29.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 de37bc854c445c3c40667fee1546892d354bb0b14f016e9c81258413db152257
MD5 1cf212c54006cabfe895e13b701b17cd
BLAKE2b-256 ee15b9793310455b9ed8d92a99fee6e4d127087613c9cbe96ce26a40348ac183

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