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
- Work on a
feature/orfix/branch, committing withcz commit - Open a PR — CI (lint + tests + secrets scan) must pass before merge
- Merge to main → version bump and tag are created automatically
- 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04b9de1bc2a41cb47d5611094e6ca30e11d6b355fb7d03ec7f295e9925f42215
|
|
| MD5 |
a79653300e478496fb830c15008da3b7
|
|
| BLAKE2b-256 |
90d0b24836cb6d022331a441538125d4f0d389b6bfb7d77b3855f0ebdba34a46
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2530cd25c92c3ca786be6fc0a6a8316264c14a3c2fd975944a8c0333d17cc891
|
|
| MD5 |
6097570349589dd608371358f32daf52
|
|
| BLAKE2b-256 |
d56e0de67be8b93326d3c45408960f3f7688bbf56332294e2daeea4d7d3ff9b3
|