Script to Export Freesurfer-based Parcellation/Segmentation Stats and Provenance as JSON-LD and NIDM
This project ultimately aims to facilitate both query and analysis of parcellation/segmentation based regional statistics across popular softwares such as Freesurfer, FSL, and ANTS. Currently each software produces its own output format and brain region labels are specific to the atlas used in generating the regional statistics. This makes life difficult when trying to search for "nucleus accumbens" volume, for example, across the different software products. Further, knowing which version of the software tool used and what atlas and version of the atlas in a structured representation facilitating query is lacking. To this end we propose augmenting the various segmentation tools with scripts that will: (1) map atlas-specific anatomical nomeclature to anatomical concepts hosted in terminology resources (e.g. InterLex); (2) capture better structured provenance about the input image(s) and the atlases used for the segmentation; (3) export the segmentation results and the provenance as either JSON-LD, NIDM which can then link the derived data to broader records of the original project metadata, or as an additional component of a BIDS derivative.
We aim to tackle this problem in steps. For this hackathon project we'll be focusing on conversion from Freesurfer's mri_segstats program output along with some additional parsing/conversion of Freesurfer log files. The conversion is driven by a function which queries InterLex and develops a JSON structure which defines the atlas terminology and the measures being output.
Python and structural neuroimaging experience. If one has experience with rdflib or PROV that would also be helpful.
- David Keator
- Adina Wagner
- Jeffrey Grethe
- Satra Ghosh
- David Kennedy
- JB Poline
This project will need expertise in programming, structural neuroimaging, and anatomy. To make this project sucessful we need individuals who have skills in any of these domains to help with: (1) understand Freesurfer's segmentation results format and log files; (2) programming up a script in Python; (3) understand anatomy well enough to select the proper anatomical concept that maps to a specific atlas designation of a region and can define new anatomy terms where needed, linking them to broader concepts to facilitate segmentation results queries across softwares.
- Freesurfer
- mri_segstats
- rdflib
- PyNIDM
- InterLex Term Search
- Anatomical Term Mappings via InterLex
- Examples done pseudo-manually of Freesurfer, FSL, and ANTS segmentation data added to NIDM documents for ABIDE and ADHD200 datasets
$ conda create -n segstats_jsonld python=3
$ source activate segstats_jsonld
$ git clone https://github.com/ReproNim/segstats_jsonld.git
$ cd segstats_jsonld
$ pip install -e .
The program is installed as segstats2nidm; python -m segstats_jsonld.fs_to_nidm runs the same
program. Give exactly one of -o (write new NIDM files) and -n (add the stats to an existing NIDM
file). With -s, -o is a directory that receives one file per stats file; with -f or -csv it is
the output file. The text below is the program's --help.
-csv requires PyNIDM 5. Under PyNIDM 4.5 every -csv run fails inside PyNIDM: its DD_to_nidm
adds an rdflib.Namespace as a triple's object, which the rdflib 6 that PyNIDM 4.5 requires refuses
("must be an rdflib term"). -s and -f work with PyNIDM 4.5 and 5.
usage: fs_to_nidm.py [-h] (-s SUBJECT_DIR | -f SEGFILE | -csv CSVFILE) [-subjid SUBJID]
(-o OUTPUT | -n NIDM_FILE) [-j] [-add_de] [-forcenidm] [-json_map JSON_MAP]
[-acq_filename ACQ_FILENAME [ACQ_FILENAME ...]]
This program reads FreeSurfer stats files (e.g. aseg.stats), maps the FreeSurfer regions and
measures to common data elements, and writes the statistics as NIDM (Turtle or JSON-LD), either as
new files (-o) or added to an existing NIDM file (-n).
options:
-h, --help show this help message and exit
-s SUBJECT_DIR, --subject_dir SUBJECT_DIR
Path to Freesurfer subject directory
-f SEGFILE, --seg_file SEGFILE
Path or URL to a specific Freesurfer stats file. Check supported_files for
list of supported stats files.
-csv CSVFILE, --csv_file CSVFILE
Path to CSV file which includes a header row with 1 column containing
subject IDs and the other columns are variables indicating the Freesurfer-
derived region measure (e.g. volume, surface area, etc. If you use this
mode of running this software you will be asked to match your region
variables to standard Freesurfer region / measure labels presented to you
(so this program will end up being interactive. In future WIP we will add
the ability to export a json sidecar file with those mappings for
automated runs of future CSV files with the same set of variables.
-subjid SUBJID, --subjid SUBJID
Subject identifier to be used in the NIDM files. Required with
-f/--seg_file. With -s/--subject_dir it defaults to the name of the
subject directory, and when given it overrides the subject directory name
(set it when that directory is not named after the subject, e.g.
sub-01/freesurfer). Earlier releases ignored -subjid with -s and always
used the directory name. Ignored with -csv, where the ID column names each
subject
-o OUTPUT, --output OUTPUT
Where to write a new NIDM file. With -s, the directory that gets one
<stats>_nidm.ttl (or .jsonld) per stats file; with -f or -csv, the output
file. Exactly one of -o/--output and -n/--nidm is required
-n NIDM_FILE, --nidm NIDM_FILE
Existing NIDM file to add the stats to; it is rewritten in place (with -j
the result goes to <NIDM_FILE>.json instead). Exactly one of -o/--output
and -n/--nidm is required
-j, --jsonld If flag set then NIDM file will be written as JSONLD instead of TURTLE.
With -n the JSON-LD goes to <NIDM_FILE>.json. Not allowed with -s and -n
together
-add_de, --add_de If flag set then the data element dictionary is added to the NIDM file,
else it is written to a separate file, fs_cde.ttl, in the output directory
(or the directory of the -n NIDM file)
-forcenidm, --forcenidm
With -n: if the NIDM file has no subjects at all, add a new subject agent
with the subject ID and attach the stats to it. If the file has subjects
and none matches the subject ID (compared without a leading "sub-" and
leading zeros), the run fails with or without this option
-json_map JSON_MAP, --json_map JSON_MAP
If storing freesurfer segmentation data stored in a CSV file and you've
previously stored the variable to Freesurfer CDE mappings, this can be
reused. Otherwise this argument is ignored
-acq_filename ACQ_FILENAME [ACQ_FILENAME ...], --acq_filename ACQ_FILENAME [ACQ_FILENAME ...]
With -s or -f and -n: file names (or paths) of the anatomical images the
stats were computed from (several for repeated runs). The stats are linked
to the input NIDM acquisitions with those file names if each name matches
exactly one of the subject's anatomical acquisitions, and left unlinked
otherwise. Without it the stats are linked only when the subject has a
single anatomical acquisition
-subjidwith-snow sets the subject ID, overriding the subject directory name. Earlier releases ignored-subjidwith-sand always used the directory name, so a caller that passes both now gets the-subjidvalue asndar:src_subject_id(for exampleds000001/sub-01instead ofsub-01). Drop-subjidto keep the directory name.- With
-n, the subject is found by comparing IDs without a leadingsub-and leading zeros on both sides. If the NIDM file has subjects and none matches, the run fails (exit 1) and writes nothing, with or without--forcenidm; earlier releases attached the stats to a new subject agent under--forcenidm. If several match and all store the same ID, the one that owns an anatomical MRI acquisition is used (the run prints which); if none or more than one of them does, the run fails and writes nothing. If several match but store different IDs (sub-01and001), the run fails and writes nothing.--forcenidmnow adds a subject only to a NIDM file that has none, and without it such a file is an error. A subject is any node withndar:src_subject_id(under thehttps://or thehttp://NDAR IRI), whatever itsrdf:type. The lookup issegstats_jsonld.fs_to_nidm.find_subject(graph, subject_id, forceagent=False, created_agents=()), which returns the subject node (orNonefor a file without subjects underforceagent), raisesSubjectLookupErrorotherwise, and never changes the graph;ants_seg_to_nidmhas the same function with the same rules. -swith no supported stats file in itsstats/directory fails (exit 1) with a message naming the directory and the files looked for; earlier releases exited 0 without writing anything.-jwith both-sand-nis refused with a message naming the alternatives (earlier releases failed on it with an rdflib plugin error).
This section describes how one can update the common data elements from FreeSurfer
as they update the names they are using for their elements. Run it from the repository
root with the package installed in editable mode (pip install -e .), so the files under
segstats_jsonld/mapping_data/ are the ones written.
from segstats_jsonld.fsutils import collate_fs_items, create_fs_mapper, create_cde_graph
collate_fs_items('/path/to/statsdir', error_on_new_key=False)
create_fs_mapper()This adds new structures to freesurfer-cdes.json and freesurfermap.json. You can edit
freesurfermap.json to improve connections to ontologies (e.g., by editing the isAbout) and
run create_fs_mapper() again to carry the edits into freesurfer-cdes.json.
Then regenerate fs_cde.ttl, the file PyNIDM reads, from freesurfer-cdes.json:
create_cde_graph().serialize(destination="segstats_jsonld/mapping_data/fs_cde.ttl", format="turtle")tests/test_fs_cde_ttl.py fails while fs_cde.ttl and freesurfer-cdes.json disagree.
PyNIDM (4.x and 5.x) caches the data elements it loads in
<temp dir>/cde_graph.<hash>.pickle (<temp dir> is Python's tempfile.gettempdir(), usually
$TMPDIR or /tmp) and reuses that file without checking whether the .ttl files changed.
After fs_cde.ttl changes, delete cde_graph.*.pickle there, or PyNIDM keeps resolving
against the old data elements.