Useful bidsmap features

The dictionary values in a bidsmap are not simple strings but have some special features that make BIDScoin powerful and flexible.

Dynamic values

Dictionary values in the bidsmap can be static, in which case the value is just a normal string, or dynamic, when the string is enclosed with single <> or double <<>> pointy brackets. The enclosed string is an attribute or property that will be extracted from the data source to supplant the dynamic value. In case of single pointy brackets the bids value will be supplanted during bidsmapper, bidseditor and bidscoiner runtime by the value of the source attribute or property of the data sample at hand. Single brackets are typically used in template bidsmaps, meaning that you will not see them at all in the bidseditor (or anywhere after that), but instead you will see the actual values from the data. If the values are enclosed with double pointy brackets, then they won’t be supplanted until bidscoiner runtime. This means that they will be moved over to the study bidsmap unmodified, and that you can see, edit or add them yourself anywhere in the bidseditor. In the final BIDS output data, they will be supplanted, just like the single bracket values. The rationale for using double bracket values is that certain properties or attributes vary from subject to subject, or even from acquisition to acquisition, and therefore they cannot directly be represented in the study bidsmap, i.e. their extraction needs to be postponed until bidscoiner runtime. For instance, suppose you want to include the scan operator’s comments or the PET dose in the BIDS sidecar files, then you could add <<ImageComments>> and <<RadionuclideTotalDose>> as metadata values in your bidsmap.

It is useful that dynamic values can extract source properties and attributes, but sometimes you may want to extract just a part of the value. That is where regular expressions come in. You can simply append a semi-colon to the property or attribute, followed by a findall regex pattern, which is then applied to the extracted value. For instance, say you want to extract the subject and session label from the filepath of your source data in /data/raw/sub-003/ses-01. In your bidsmap you could then use {subject: <<filepath:/sub-(.*?)/>>} to evaluate re.findall('/sub-(.*?)/', '/data/raw/sub-003/ses-01') under the hood, and get {subject: 003} (i.e. the shortest string between /sub- and /) during bidscoiner runtime. Alternatively, if the subject label is encoded in the DICOM PatientName attribute as e.g. ID_003_anon, then {subject: <<PatientName:ID_(.*?)_>>} in your bidsmap would likewise evaluate re.findall('ID_(.*?)_', 'ID_003_anon') and give you {subject: 003} at bidscoiner runtime.

As may have become clear from the above, dynamic values are BIDScoin’s hidden powerhouse. But you can take it even one step further and make combinations of static and dynamic values. For instance the static and dynamic values in <MRAcquisitionType>Demo<SeriesDescription:t1_(.*?)_sag> will result in 3DDemoMPRAGE if the ‘MRAcquisitionType’ of the data sample is ‘3D’ and ‘SeriesDescription’ is ‘t1_MPRAGE_sag_p2_iso_1.0’ (hint: the second dynamic value will evaluates re.findall('t1_(.*?)_sag', 't1_mprage_sag_p2_iso_1.0') to give MPRAGE).

Tip

Dynamic values with (or without) regular expressions can be hard to grasp and predict their outcome. To easily test out their working, you can just enter dynamic values in the bidseditor (in any value field) using single brackets and instantly obtain their resulting value

Run-index

If the run index is encoded in the header or filename, then the index number can be normally extracted using dynamic values. For instance, using {run: <<ProtocolName:run_nr-(.*?)_>>} in the bids output dictionary will give {run: 3} if the DICOM ProtocolName is t1_mprage_sag_run_nr-3_iso_1.0. Yet, if the index information is not available in the header or filename, then it needs to be determined from the presence of other files in the output directory. To handle that, the run-index value can be a dynamic number (similar to a dynamic value). This dynamic number (e.g. {run: <<1>>}) will be incremented during bidscoiner runtime if an output file with the same name already exists (so .._run-1_.. will become .._run-2_..). If the dynamic number if left empty ({run: <<>>}), then the run-index is omitted from the output filename if there are no other files with the same name (i.e. if only a single run was acquired). If that’s not the case and multiple runs were acquired, then {run: <<>>} will behave the same as {run: <<1>>}, i.e. then .._run-1_.., .._run-2_.., etc will be included in the output filenames.

Field maps

Field maps are MRI scans that are acquired and stored in various (sequences and manufacturer dependent) ways and may require some special treatment. For instance, it could be that you have magnitude1 and magnitude2 data in one series-folder (which is what Siemens can do). In that case, when running the bidseditor, you should select the magnitude1 suffix and let bidscoiner automatically pick up the magnitude2 during runtime (or vice versa). The same holds for phase1 and phase2 data.

Field maps are typically acquired to be applied to other scans from the same session. As described in the following sections, there are two BIDS metadata specifications to indicate the associations with the target scans. Except for resting-state scans, it is important specify such metadata, or BIDS-apps may not use your acquired field map scans at all.

Using B0FieldIdentifier metadata

In BIDS version 1.7 and later, field maps and their target scans can be grouped with B0FieldIdentifier metadata tags. The fieldmap is computed from all scans with an identical B0FieldIdentifier tag, and applied to all scans with the same identical B0FieldSource tag. That means that, for instance, in the Metadata table you could use a {B0FieldIdentifier: sbref_fmap} tag in your AP and PA PE-polar sbref field map images, and a {B0FieldSource: sbref_fmap} tag in your AP PE-polar bold image that you like to correct.

The B0FieldIdentifier tags group all scans in the whole subject directory. For single session datasets this works well, but it will cause issues if you have multiple sessions with field maps that were scanned with the same protocol, as BIDScoin will tag them identically for both scan sessions. To uniquely tag these scans you can add a special <<session>> dynamic value – which will be supplanted with the parent session label during bidscoiner runtime. In the above example you could hence use {B0FieldIdentifier: sbref_fmap<<session>>}, which would result during bidscoiner runtime in e.g. {B0FieldIdentifier: sbref_fmap<<ses01>>} for scans acquired in the first session and in {B0FieldIdentifier: sbref_fmap<<ses02>>} for the second session.

The problem of tagging duplicate field maps is not limited to multiple sessions, but it can also occur within a single session. For instance, when the quality of one of your scans was insufficient or when your session got briefly interrupted, you may have re-acquired them in the same session. You would then like to use the B0FieldIdentifier tags only for scans that were acquired just before or just after the particular field map. To achieve this, you can append a colon-separated “bounding” term to the dynamic value. This bounding term will be supplanted by the field map run-index for each field map and its adjacently acquired target scans. This adjacency can be specified as a [start:stop] inclusion range, where negative/positive digits indicate the number of scans before/after the acquisition of the fieldmap that are considered. That means that if you specify the adjacency range as [-4:0] (i.e. from -4 to 0) you will tag only target scans that are acquired within four runs before the field map. Similarly, using [0:3] will only tag scans acquired within three runs after the field map, using [-2:3] will tag two target scans before up to three scans after the field map, and using [0:] (i.e. from zero to infinity) will tag all target scans after the field map.

Here is a more extensive example that brings it all together. In the left column of the file tree below you can see the sidecar filenames of an anatomical scan, a single field map and three subsequent functional scans that was interrupted after two scans, after which the field map and all three functional scans were re-acquired. The middle column shows its B0FieldIdentifier metadata, and the far right column shows its DICOM SeriesNumber, i.e. the acquisition order of the source data. Here, the bounded B0FieldIdentifier/Source metadata entered in the bidseditor was mytag<<session:[0:3]>>, i.e. a custom tag appended with a session tag that is applied only to the fieldmap + three succeeding functional scans. As you can see in the middle column, in the final output the session substring was supplanted by the session-label and :[0:3] was supplanted by the field map run-index:

|-- anat
|   |-- sub-001_ses-01_UNIT1.json                                                           [05]
|   |-- sub-001_ses-01_inv-1_MP2RAGE.json                                                   [06]
|   `-- sub-001_ses-01_inv-2_MP2RAGE.json                                                   [07]
|
|-- fmap
|   |-- sub-001_ses-01_run-1_magnitude1.json      <- {B0FieldIdentifier: mytag<<ses01_1>>}  [01]
|   |-- sub-001_ses-01_run-1_magnitude2.json      <- {B0FieldIdentifier: mytag<<ses01_1>>}  [01]
|   |-- sub-001_ses-01_run-1_phasediff.json       <- {B0FieldIdentifier: mytag<<ses01_1>>}  [02]
|   |-- sub-001_ses-01_run-2_magnitude1.json      <- {B0FieldIdentifier: mytag<<ses01_2>>}  [08]
|   |-- sub-001_ses-01_run-2_magnitude2.json      <- {B0FieldIdentifier: mytag<<ses01_2>>}  [08]
|   `-- sub-001_ses-01_run-2_phasediff.json       <- {B0FieldIdentifier: mytag<<ses01_2>>}  [09]
|
`-- func
    |-- sub-001_ses-01_task-rest_run-1_bold.json  <- {B0FieldSource: mytag<<ses01_1>>}      [03]
    |-- sub-001_ses-01_task-rest_run-2_bold.json  <- {B0FieldSource: mytag<<ses01_1>>}      [04]
    |-- sub-001_ses-01_task-rest_run-3_bold.json  <- {B0FieldSource: mytag<<ses01_2>>}      [10]
    |-- sub-001_ses-01_task-rest_run-4_bold.json  <- {B0FieldSource: mytag<<ses01_2>>}      [11]
    `-- sub-001_ses-01_task-rest_run-5_bold.json  <- {B0FieldSource: mytag<<ses01_2>>}      [12]

Note

Using a bounding term is supported when the B0FieldIdentifier is a single string (as in the example above), but not when it is a list of strings (which is uncommon but allowed in BIDS). Also, using bounded B0FieldIdentifiers outside the fmap folder does not work (yet).

Using IntendedFor metadata

IntendedFor is a legacy metadata specification that explicitly specifies a list of relative pathnames of the target images to which the field map should be applied. Unfortunately, this makes it suitable for handling basic use cases only, as it leaves it implicit from which images the field map should be computed (contrary to the B0FieldIdentifier tags described above). In BIDScoin, in the Metadata table, you can enter a dynamic IntendedFor search string (Unix wildcard style) to automatically fill out the target pathnames during bidscoiner runtime. For example you can use a {IntendedFor: task-Stop*_bold} search pattern to specify all functional runs in the BIDS subject[/session] subfolder that have task-Stop and _bold as part of their filename (in the glob code implementation a * wildcard is automatically added to the start and end of the search pattern). Another more sophisticated example would be to use {IntendedFor: <<func/*Stop*Go_bold><func/*Reward*_bold>>} pattern to select e.g. all ‘Stop1Go’-, ‘Stop2Go’- and ‘Reward’ bold-runs as targets.

In some cases, e.g. when a scan failed or a session was interrupted, you may have acquired multiple field maps and/or target scans with the same protocol, resulting in IntendedFor values in all field maps pointing to all targets. This is typically undesirable, and to avoid this you can limit the IntendedFor search by appending a colon-separated “bounding” term to the pattern to include only scans that were acquired adjacent to the fieldmap. The adjacency range of the bounding term can be specified with the same [start:stop] format as for the B0FieldIdentifier described above. For instance {IntendedFor: <<task:[-3:0]>>} will limit the bounded search to maximally three functional runs preceding the field map. Similarly, {IntendedFor: <<func/*Stop:[-2:2]>>} will limit the bounded search to maximally two preceding and two subsequent functional runs that have a “Stop” substring in the filename. For more explanation and examples see the B0FieldIdentifier section above.