Eye voxel extraction pipeline using templated eye masks.
Project description
MReyeXtract: eye-voxel extraction for fMRI
MReyeXtract extracts the eyeballs from 4D BOLD images so they can be fed to DeepMReye or other gaze-decoding models. Each run is registered to a DeepMReye eye template with ANTsPy, cropped to the eye masks, and saved alongside an interactive HTML quality-control report. It runs on BIDS datasets out of the box, and on arbitrary directory trees via a glob pattern.
If you have questions or comments, please reach out (see Correspondence).
Installation
MReyeXtract requires Python 3.11.
Option 1: Pip install
Pip installation
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .
For development (tests, linting, type checking):
pip install -e ".[dev]"
Anaconda / Miniconda installation
conda create --name mreyextract python=3.11
conda activate mreyextract
pip install -e .
If ANTsPy does not resolve a wheel for your platform, install it manually first (see the ANTsPy installation guide) and then re-run the install above.
Usage
Installing the package exposes the mreyextract command-line tool. Run
mreyextract --help for the full list of options.
BIDS datasets (default)
mreyextract --root /path/to/bids_dataset
To extract from a derivatives pipeline (e.g. fMRIPrep outputs):
mreyextract --root /path/to/bids_dataset --derivatives-dir fmriprep
Restrict which BOLD files are processed with BIDS entities. Each accepts one or
more values; '*' matches any value and 'none'/'null' matches files where
the entity is absent:
mreyextract --root /path/to/bids_dataset \
--subject 01 02 --task rest --run '*'
Filters can also be supplied as a JSON file via --bids-filter-file (the
BIDS-App convention). Precedence, lowest to highest, is: YAML config (see later) →
JSON BIDS filter file → explicit CLI entity flags, so the command line always
wins.
Non-BIDS directories
Point --no-bids-compatible at any tree and provide a glob pattern:
mreyextract --root /path/to/data --no-bids-compatible \
--glob-pattern 'sub-*/**/func/*_bold.nii*'
Options
| Option | Description |
|---|---|
--root |
Root directory to search for BOLD files (required). |
--bids-compatible / --no-bids-compatible |
Treat --root as a BIDS dataset. Default: BIDS. |
--derivatives-dir |
Relative derivatives directory to extract from (e.g. fmriprep). |
--glob-pattern |
Glob for non-BIDS mode. Default: sub-*/**/func/*_bold.nii*. |
--force |
Overwrite existing outputs instead of skipping them. |
--as-pickle |
Save the masked eye voxels as a pickled array instead of NIfTI. |
--n-jobs |
Number of runs to process in parallel. 1 (default) is serial; -1 uses all cores. |
--threads-per-job |
ITK/ANTs threads per parallel job. Default: cores // n_jobs. |
--log-level |
Logging verbosity: DEBUG, INFO, WARNING, ERROR. Default: INFO. |
--bids-filter-file |
Path to a JSON file of BIDS entity filters. |
--config |
Path to a YAML config file that seeds the options above. |
Config file
Rather than passing many flags, the run can be described in a YAML file and
loaded with --config. Keys under extract mirror the CLI options (with
underscores); explicit flags on the command line override the file:
# run.yaml
extract:
root: /abs/path/to/bids_dataset
derivatives_dir: fmriprep
n_jobs: 4
threads_per_job: 2
filters:
task: [rest] # "*" -> any, "none"/"null" -> absent
mreyextract --config run.yaml # everything from the file
mreyextract --config run.yaml --force # override a single option
Parallel processing
Runs are independent, so they can be processed in parallel across a loky process pool:
mreyextract --root /path/to/bids_dataset --n-jobs 8
Registration (ANTsPy/ITK) is itself multithreaded, so the tool splits the
available cores between across-run parallelism (--n-jobs) and each run's own
threads (--threads-per-job) to avoid oversubscription. By default
threads-per-job is set to cores // n_jobs, where cores respects the CPU
allocation (SLURM/cgroup affinity), not just the physical node — so the default
is safe when running interactively inside an allocation. Tune both together on a
shared server, and keep an eye on memory — each concurrent run holds a full 4D
BOLD volume in RAM.
Python API
The extraction entry point can also be called directly:
from mreyextract.extract import extract_eyeball_voxels
extract_eyeball_voxels(
root="/path/to/bids_dataset",
glob_pattern="sub-*/**/func/*_bold.nii*",
bids_compatible=True,
filters={"task": "rest"},
)
Data formats
Inputs are 4D BOLD images in NIfTI format (.nii / .nii.gz).
Outputs are written to a BIDS-style derivatives folder under the dataset root:
<root>/derivatives/mreyextract/
dataset_description.json
sub-01/func/
sub-01_task-rest_run-1_desc-eye_bold.nii.gz # masked eye voxels
sub-01_task-rest_run-1_desc-eye_report.html # QC report
With --as-pickle, the eye voxels are saved as a pickled NumPy array
(*_desc-eye_timeseries.p) instead of NIfTI. Existing outputs are skipped
unless --force is passed.
Hardware requirements
Registration is CPU-based and runs per BOLD run. A standard workstation is sufficient; no GPU is required. Memory scales with image size — 4D BOLD runs are held in memory during registration, so allow several GB of free RAM for high-resolution or long acquisitions.
Software requirements
MReyeXtract is developed and tested on Python 3.11. Core dependencies (installed automatically):
numpy (<2.0.0)
nibabel (>=5.3.2)
antspyx (>=0.6.1)
scipy (>=1.15.1)
plotly (>=6.5.0)
pybids (>=0.22.0)
joblib (>=1.3)
pyyaml (>=6.0)
Running on a cluster (SLURM)
The slurm/ directory contains a ready-to-adapt job-array template
(submit.sbatch) and an example config.yaml. The pattern is one array task
per subject: SLURM provides the across-subject parallelism, and each task lets
ANTs use all of its allocated cores.
mkdir -p logs
sbatch slurm/submit.sbatch slurm/config.yaml
The template reads the subject list from the config's slurm.subjects, injects
the right subject per array index, and caps ITK/OpenMP threads to
--cpus-per-task. Edit the #SBATCH resource directives (and the --array
range to match the number of subjects), the module load line (must be a
Python 3.11 build), and the virtual-environment path before submitting.
Tests
pytest
Development
MReyeXtract is maintained as part of the OpenMReye ecosystem and is a dependency of other packages, so releases follow an automated, convention-based pipeline. Please read this section before contributing.
Local setup
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Run the full check suite (mirrors CI) before pushing:
./format_and_test.sh # black, mypy, pylint, tests
Branch and commit workflow
main is protected: no direct pushes. All changes land through pull requests
that are squash-merged, so the PR title becomes the commit message on
main. That title must be a valid
Conventional Commit, because it is what
drives the next version number:
| PR title prefix | Example | Release effect |
|---|---|---|
fix: |
fix: correct mask resampling origin |
patch (0.1.0 → 0.1.1) |
feat: |
feat: add --as-pickle output |
minor (0.1.0 → 0.2.0) |
feat!: / BREAKING CHANGE: |
feat!: drop Python 3.10 support |
major bump* |
docs: / chore: / ci: / test: / refactor: |
docs: clarify SLURM template |
no release |
*While the package is in 0.x (allow_zero_version, major_on_zero = false), a breaking change bumps the minor version rather than jumping to
1.0.0. Graduating to 1.0.0 is a deliberate decision to make once the API is
stable, since downstream packages pin against these numbers.
The PR title is checked automatically (pr-title.yml); a malformed title blocks
the merge.
How a release happens
The pipeline is fully automated and stores no secrets — you never bump a version or publish by hand:
ci.ymlruns the check suite on every PR. These are required status checks, somainis always green.- On merge to
main,release.ymlruns. Its first job uses python-semantic-release: it reads the Conventional Commits since the last release, and if there is something to release, creates thevX.Y.Zgit tag and a GitHub Release (whose notes are the changelog). It is configured not to commit or push tomain, so the branch is never touched and the defaultGITHUB_TOKENsuffices. The version lives only in git tags and is read at build time byhatch-vcs. - The workflow's second job then builds the sdist/wheel from that tag and
uploads to PyPI via Trusted Publishing
(OIDC — no PyPI token anywhere). It runs in the
pypideployment environment, so the upload waits on manual approval.
Pure docs:/chore: merges produce no release, so main does not spam PyPI.
BIDS app
MReyeXtract reads and writes BIDS-compatible layouts: it queries BOLD files with
PyBIDS, honours BIDS entity filters,
and emits a derivatives/mreyextract/ folder with a dataset_description.json.
Correspondence
If you have questions, comments or inquiries, please reach out to us: zachnudels[at]gmail.com
Project details
Release history Release notifications | RSS feed
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 mreyextract-0.1.0.tar.gz.
File metadata
- Download URL: mreyextract-0.1.0.tar.gz
- Upload date:
- Size: 2.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f6fb7d566deb4cfe370f34a86d49335579c0ace959d7d2411edf993774534cd
|
|
| MD5 |
c8334b4ede6428dcd31f1a0cfaa20e7e
|
|
| BLAKE2b-256 |
821bab314b9d9283a693f8bde074e2569302b89e19b3ca034c43b19ea1338d61
|
Provenance
The following attestation bundles were made for mreyextract-0.1.0.tar.gz:
Publisher:
release.yml on DeepMReye/MReyeXtract
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mreyextract-0.1.0.tar.gz -
Subject digest:
7f6fb7d566deb4cfe370f34a86d49335579c0ace959d7d2411edf993774534cd - Sigstore transparency entry: 2173158360
- Sigstore integration time:
-
Permalink:
DeepMReye/MReyeXtract@40d857ce9c8d5b32e78faf070959f89b1bed45e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/DeepMReye
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@40d857ce9c8d5b32e78faf070959f89b1bed45e6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mreyextract-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mreyextract-0.1.0-py3-none-any.whl
- Upload date:
- Size: 2.9 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63bdb8cfeb5801bf31f1e9ed9d8596bf17defd95ffc7ffd8a7460c5d069680e9
|
|
| MD5 |
5bd0b63fa2afc7a969dcde3bac31c720
|
|
| BLAKE2b-256 |
481e07171664fe0ec582be5eacff8bc98388caff50b93d613cb519110da36e2e
|
Provenance
The following attestation bundles were made for mreyextract-0.1.0-py3-none-any.whl:
Publisher:
release.yml on DeepMReye/MReyeXtract
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mreyextract-0.1.0-py3-none-any.whl -
Subject digest:
63bdb8cfeb5801bf31f1e9ed9d8596bf17defd95ffc7ffd8a7460c5d069680e9 - Sigstore transparency entry: 2173158368
- Sigstore integration time:
-
Permalink:
DeepMReye/MReyeXtract@40d857ce9c8d5b32e78faf070959f89b1bed45e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/DeepMReye
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@40d857ce9c8d5b32e78faf070959f89b1bed45e6 -
Trigger Event:
push
-
Statement type: