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.3.1.tar.gz (25.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.3.1-py3-none-any.whl (26.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: msw_flir_bonsai-0.3.1.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","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.3.1.tar.gz
Algorithm Hash digest
SHA256 04b9de1bc2a41cb47d5611094e6ca30e11d6b355fb7d03ec7f295e9925f42215
MD5 a79653300e478496fb830c15008da3b7
BLAKE2b-256 90d0b24836cb6d022331a441538125d4f0d389b6bfb7d77b3855f0ebdba34a46

See more details on using hashes here.

File details

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

File metadata

  • Download URL: msw_flir_bonsai-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 26.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","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.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2530cd25c92c3ca786be6fc0a6a8316264c14a3c2fd975944a8c0333d17cc891
MD5 6097570349589dd608371358f32daf52
BLAKE2b-256 d56e0de67be8b93326d3c45408960f3f7688bbf56332294e2daeea4d7d3ff9b3

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