Plugin for the d2b package to handle ASL data
pip install d2b-aslThis package adds support for the aslContext field in the description objects located in the d2b config files. This field should be an array of strings, where each string is a volume type (as defined in the BIDS specification here). Specifically, this array should have the same number of entries as the described ASL acquisition has volumes (if there are 109 volumes in the acquisition which this description is describing, then aslContext should be an array of length 109).
For example, a config file describing an ASL acquisition might looks something like:
{
"descriptions": [
{
"dataType": "perf",
"modalityLabel": "asl",
"criteria": {
"ProtocolName": "ep2d_pasl",
"MagneticFieldStrength": 3,
"MRAcquisitionType": "2D",
"ImageType": [
"ORIGINAL",
"PRIMARY",
"ASL",
"NONE",
"ND",
"NORM",
"FILTERED",
"MOSAIC"
]
},
"sidecarChanges": {
"ArterialSpinLabelingType": "PASL",
"PostLabelingDelay": 1.8,
"BackgroundSuppression": false,
"M0Type": "Included",
"TotalAcquiredPairs": 54,
"AcquisitionVoxelSize": [3.5, 3.5, 4.5],
"BolusCutOffFlag": true,
"BolusCutOffDelayTime": 0.7,
"BolusCutOffTechnique": "Q2TIPS",
"PASLType": "PICORE"
},
"aslContext": [
"m0scan",
"control",
"label",
"control",
"label",
"control",
"label",
// full array omitted for readability ...
"control",
"label"
]
}
]
}There are two spots where you might be specifying arrays as field values in your d2b config files.
-
aslContext- This array should ALWAYS have the same length as the number of volumes in your ASL data. -
fields in
SidecarChanges- These are fields likeRepetitionTimePreparation,PostLabelingDelayetc. which may or may not need to be arrays. If they are arrays, then these arrays should have length equal to the number of volumes in the files output by d2b.
Why are the lengths of the fields in item 2 potentially different from the lengths of aslContext in item 1? In the event that aslContext has "discard" values in its array, the number of volumes in the output ASL data will be different than the number of volumes in the input data.
The lengths of any of the arrays in SidecarChanges should take these discarded volumes into account. For example, suppose we have:
{
"aslContext": ["m0scan", "discard", "label", "control"]
}then the PostLabelingDelay field would look like:
{
"SidecarChanges": {
"PostLabelingDelay": [0, 1.8, 1.8]
},
"aslContext": ["m0scan", "discard", "label", "control"]
}In general, the following relationship should hold:
len_sidecar_changes_field = len(config.SidecarChanges["<array-field>"])
len_aslcontext = len(config.aslContext)
len_aslcontext_discard = len(list(filter(lambda v: v == "discard", config.aslContext)))
len_sidecar_changes_field == len_aslcontext - len_aslcontext_discardSo you a have ASL data and need to format it, great!
Here we try to unpack the most confusing parts of translating acquisition metadata into BIDS sidecar fields. We do this by considering 4 major classes of "acquisition configuration":
- PCASL + Separate M0
- PCASL + Included M0
- PASL + Separate M0
- PASL + Included M0
In each of the 4 scenarios we'll touch on:
- How to determine the following parameters:
RepetitionTimePreparationPostLabelingDelayLabelingDurationInversionTime
- What the implications are with regard to array-like sidecar fields for ASL data.
As a historical/contextual note, it seems like the people who were/are involved in the creation of the ASL-specific portions of the BIDS specification have close ties (or are the same people) who work on the ASL tools included in FSL, and who created https://asl-docs.readthedocs.io. As such, we'll use the same language found there (FSL + asl-docs), because many of the concepts intentions in those locations seem to have made their way into the BIDS spec.
IMPORTANT: Any of the fields discussed here that can/should be of type
number[], should be arrays of length equal to the number of volumes in the associated acquisition, unless explicitly indicated otherwise.
If this is how you acquired your ASL data then congratulations, because this is probably the least confusing of the 4 scenarios.
-
RepetitionTimePreparationtl;dr: Probably
number(same asRepetitionTime)Unless you know that the TR varies (and how it varies) during the course of the ASL acquisition (in which case this field should be an array, see the note above) then this number can most likely be of type
number(i.e. a single (scalar) value), and should probably match (be equal to) whatever is listed as theRepetitionTime -
PostLabelingDelaytl;dr:
number(if single delay) ornumber[](if multi-delay)If you have single-delay data, then this field can simply be a scalar (single value), namely the single post-label delay with which your data was acquired. This will likely (or at least hopefully likely) be recorded in the MRI protocol sheet somewhere, worst case: someone in your lab (the lab that collected the data) will know what it is/was.
If you have multi-delay data, then this field should be of type
number[]i.e. an array of numbers equal in length to the number of volumes in the ASL acquisition. Each number in the array should be the post-label delay used to acquire the volume at the corresponding index. (i.e.sidecar.PostLabelingDelay[0]is the delay used for the first volume,sidecar.PostLabelingDelay[12]is the delay used for the 13th volume, etc.) -
LabelingDurationtl;dr: Probably
numberSimilar to
RepetitionTimePreparation, this is (very) probably a single number unless your know and/or have good reason to be believe that it should be an array (in which case this field should be an array, see the note above). This value will most likely be listed in the MRI protocol sheet as:label durationorbolus duration. -
InversionTimetl;dr:
numberThis should be whatever is listed in the DICOM header (or protocol sheet).
-
RepetitionTimePreparationtl;dr: Probably
number(but should benumber[]- an array)First, we'll talk about what probably should be provided for this field, then we'll talk about you'll likely be able to provide.
Because of how the M0 images are acquired it's possible that the TR for the M0 volume(s) is(are) different (likely longer) than the TR for the rest of the "regular" ASL volumes. If this is the case and you actually know what the different TRs are, then this field should be an array (where length == num vols, yada yada ..., see the note above about arrays).
It's possible that either: A) the TR for the M0 volume(s) is(are) known to be the same as for the rest of the volumes, or B) that you suspect that the TRs are different, but you don't know what they are. In this case, either because it's right (A), or because it practical (B), this number will be of type
number(i.e. a single scalar value) which equals (is the same as) theRepetitionTimefield listed for this acquisition. -
PostLabelingDelaytl;dr:
number[](MUST be an array)In the previous section this field could have been a scalar or an array. In this case, it's no longer situation-dependent, this field must be an array of
number[].This is because, for the M0 volumes, (which are mixed in with the "regular" ASL volumes) the post-label delay is 0, so effectively we have a multi-delay timeseries with at least 2 delays:
- the M0 delay (0), and
- the delay(s) that you collected the "regular" ASL volumes (one (single-delay) or more (multi-delay) values > 0)
-
LabelingDurationtl;dr:
number[](MUST be an array)The same argument that applied to
PostLabelingDelay(in this section, above) applies here, the only difference is that, realisitically, this will (in most cases) be an array of exactly two values:0and[actual-label-duration-from-protocol-sheet](<- which is probably a single value) -
InversionTimetl;dr:
numberThis should be whatever is listed in the DICOM header (or protocol sheet).
-
RepetitionTimePreparationtl;dr: Probably
number(but should benumber[]- an array)See the discussion for
RepetitionTimePreparationin the section PCASL + Separate M0 -
PostLabelingDelaytl;dr:
number(if single delay) ornumber[](if multi-delay)This is where things start to get more complicated. Already, there is a potentially confusing mixture of terms (described here), specifically, (only for PASL):
t_inversion (TI) = t_inflow (also, TI) = t_pld (as specified by BIDS)In the context of a (P)CASL (continuous) ASL acquisition, we talk about
label durationandpost-label delay(the sum of the two asl-docs calls theinflow time).t_inflow = t_label + t_pldIn the context of a PASL (pulsed) ASL acqusition, we talk about the
inversion time (TI), typically, for PASL, there aren't two "parts" to the inflow time, but instead we just have the inversion time:t_inflow = t_inversionHowever, Since "inflow time" isn't an available BIDS sidecar field, the next best option might be
InversionTime(an actual BIDS sidecar field), but there are two problems with using this field:- this field already has a different non-ASL meaning/role, and
- this field cannot be an array (
number[]), which is a requirement of whatever field we use in order to accommodate multi-inversion time (multi-delay) PASL fields
Thus, to avoid adding yet another field the to the BIDS spec, the
PostLabelingDelayfield is used. As such, in the case of PASL:t_inflow = t_inversion = t_pldSo the "inversion time" (delay) used to collect your PASL data should be placed as the
PostLabelingDelayfield value, which anumber(scalar) for single-inversion (single-delay) data, and anumber[](array) for "multi-inversion" (multi-delay) data. -
LabelingDurationtl;dr: Should NOT be present ... or
numberStrictly speaking, the
LabelingDurationfield is an invalid field for data withArterialSpinLabelingType == "PASL", as such, this field should not appear in the associated sidecar.However, if you are hoping to make use of the ASLPrep BIDS app, this tool assumes the presence of a
LabelingDurationfield (until issue 167 is resolved). In this case one can get around this issue by setting:{ // other fields ... "LabelingDuration": 0 // ... }in the sidecar and specifying
--skip-bids-validationas a flag. -
InversionTimetl;dr:
numberThis should be whatever is listed in the DICOM header (or protocol sheet).
-
RepetitionTimePreparationtl;dr: Probably
number(but should benumber[]- an array)See the discussion for
RepetitionTimePreparationin the section PCASL + Separate M0 -
PostLabelingDelaytl;dr:
number[]For the same reasons that
PostLabelingDelayMUST be an array (outline in this section), this field must also be an arary.Regarding the interpretation/where to find these values see the discussion on
PostLabelingDelayfield in the previous section -
LabelingDurationtl;dr: Should NOT be present ... or
numberSame reasoning as the previous section with the same caveat/remark/hack regarding ASLPrep.
-
InversionTimetl;dr:
numberThis should be whatever is listed in the DICOM header (or protocol sheet).