feat(data): add BIDS discovery and reshaping - #382
Open
dhritimandas wants to merge 7 commits into
Open
Conversation
Add versioned dataset contract with DataLad-aware preflight validation: - DataSpec dataclass with JSON serialization - validate() with symlink-aware file checks, spacing/orientation/label validation - inspect_entry() for NIfTI/Zarr metadata inspection - CLI commands: validate, inspect, --skip-validate on predict/convert-to-zarr - 33 unit tests covering all branches
DataSpec (added in neuronets#376) could only be built from a hand-written JSON manifest, so every BIDS dataset -- what OpenNeuro ships and what most collaborators already have -- had to be converted by hand first. The reverse was also missing: nobrainer's own outputs and ad-hoc file collections couldn't be published as BIDS. - nobrainer/data/bids.py: scan_bids() discovers image/label pairs from a BIDS(-Derivatives) tree; to_bids() reshapes arbitrary entries into a conformant tree. Entity grammar, directory rules, and derivative naming are pinned to BIDS 1.11.1. - DataSpec.from_bids() classmethod, mirroring from_json(). - CLI: `nobrainer data from-bids` / `nobrainer data to-bids`. - pyproject: new [bids] extra (pybids) -- optional, the internal walker needs no new dependencies and is what CI actually exercises (the workflow does not install pybids). Design notes: - Two discovery backends, one output schema. The zero-dependency walker is the default fallback and is fully tested on its own; pybids is a convenience layer with three documented traps guarded explicitly (it drops `subject` for a bare filename, coerces `run` to int destroying zero-padding, and uses different entity names than the BIDS schema). - from_bids() never crashes on a single bad file -- every unusable file is recorded with a reason and the scan continues. It only raises if the root doesn't exist or nothing at all was found. - Image/label entries are never a partial mix: either every returned entry has a label or none do, matching how the parallel image/label lists are built downstream. - from_bids() does no file-presence I/O itself; it reuses nobrainer.data.spec.validate() for that, so git-annex/DataLad symlink checks apply to BIDS-sourced specs exactly as they do to JSON-manifest ones, with no duplicated logic. - to_bids() writes symlinks only, never copies. Labels are written to a derivatives/ subtree, since dseg/probseg/mask are not valid raw BIDS suffixes.
dhritimandas
force-pushed
the
feat/bids-support
branch
from
August 12, 2026 14:26
1f58ea3 to
12806b3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
What: We add BIDS support to
nobrainer.data.DataSpec.from_bids()reads a BIDS dataset.to_bids()writes one.Why: OpenNeuro ships BIDS datasets. Users had to convert them to manifests by hand. No reverse path existed.
This PR depends on #376 and is stacked on it until it merges.
Discovery (
scan_bids/DataSpec.from_bids)pybidspybidstraps guarded: bare filenames dropsubject,runcoerced toint(kills zero-padding), long entity names != schema keysrequire_labels), protecting the parallel-list construction inget_dataset()nobrainer.data.spec.validate(), so git-annex/DataLad checks apply unchangedReshaper (
to_bids)derivatives/nobrainer/as_dseg—dseg/probseg/maskare not valid raw-BIDS suffixesdataset_description.jsonat both levels;dseg.tsvlookup table when labels namedCLI + deps
datagroup:nobrainer data from-bids/to-bidsbids = ["pybids >= 0.19"]— only forbackend="pybids", core paths need nothingTest plan
from_bids()output feedsget_dataset()unmodified (realDataLoaderbatch)validate()with the existingdatalad getmessage