From f6e3f721bfdde953d26d713e1513a81ee9907bca Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 21 May 2024 17:04:00 -0400 Subject: [PATCH 1/7] SCHEMA: Add nifti_header.mrs to context --- src/schema/meta/context.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/schema/meta/context.yaml b/src/schema/meta/context.yaml index 7f9ca1259c..1c82dc13c0 100644 --- a/src/schema/meta/context.yaml +++ b/src/schema/meta/context.yaml @@ -329,6 +329,10 @@ context: name: 'sform code' description: 'Use of the affine fields.' type: integer + mrs: + name: 'NIfTI-MRS extension' + description: 'NIfTI-MRS JSON fields' + type: object ome: name: 'Open Microscopy Environment fields' description: 'Parsed contents of OME-XML header, which may be found in OME-TIFF or OME-ZARR files' From 899e6ec5f5f80a7d5f6eeb71d7dafcac5486d806 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 21 May 2024 17:51:20 -0400 Subject: [PATCH 2/7] SCHEMA: Add check for consistency between MRS header and sidecar --- src/schema/rules/checks/mrs.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/schema/rules/checks/mrs.yaml diff --git a/src/schema/rules/checks/mrs.yaml b/src/schema/rules/checks/mrs.yaml new file mode 100644 index 0000000000..a38a3007c6 --- /dev/null +++ b/src/schema/rules/checks/mrs.yaml @@ -0,0 +1,14 @@ +--- +MRSNiftiConsistency: + issue: + code: MRS_NIFTI_CONSISTENCY + message: | + ResonantNucleus and/or SpectrometerFrequency fields are inconsistent + between the NIfTI-MRS header extension and the BIDS sidecar. + level: error + selectors: + - datatype == "mrs" + - nifti_header.mrs + checks: + - sidecar.ResonantNucleus == nifti_header.mrs.ResonantNucleus + - sidecar.SpectrometerFrequency == nifti_header.mrs.SpectrometerFrequency From 9dabf8e2e4ffe78e4971e38015cc2649ee48726d Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Wed, 29 May 2024 11:30:46 -0400 Subject: [PATCH 3/7] Split conditional matrices in schema, combine in doc This implements the following agreed upon suggestions: * Set InversionTime to recommended if `inv` entity is present. (`entities.inversion`) * Move to RepetitionTime and VolumeTiming, rather than RepetitionTime as an array in the variable-timing case. * ReferenceSignal should be uniformly recommended. --- .../magnetic-resonance-spectroscopy.md | 37 +++-------- src/schema/objects/metadata.yaml | 24 +------ src/schema/rules/sidecars/func.yaml | 4 ++ src/schema/rules/sidecars/mrs.yaml | 64 ++++++++++++------- 4 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/modality-specific-files/magnetic-resonance-spectroscopy.md b/src/modality-specific-files/magnetic-resonance-spectroscopy.md index 55088a5f85..910d4b70a3 100644 --- a/src/modality-specific-files/magnetic-resonance-spectroscopy.md +++ b/src/modality-specific-files/magnetic-resonance-spectroscopy.md @@ -179,19 +179,7 @@ A guide for using macros can be found at ### MRS-relevant fields -Metadata fields that MUST be present: - - -{{ MACROS___make_sidecar_table("mrs.MRSRequiredFields") }} - -SHOULD be present: +Metadata fields that MUST, SHOULD or MAY be present: {{ MACROS___make_sidecar_table([ - "mrs.MRSRecommendedFieldsNumTransients", - "mrs.MRSRecommendedFields", + "mrs.MRSRelevantFields", + "mrs.MRSRepetitionTime", + "mrs.MRSVolumeTiming", + "mrs.MRSConditionalNumTransients", "mrs.MRSIRecommendedFields", - "mrs.MRSRecommendedFieldsRefSignal", - "mrs.MRSRecommendedFieldsAnatomicalImage" + "mrs.MRSConditionalInversionTime", + "mrs.MRSConditionalAnatomicalImage", + "mrs.MRSOptionalFields", ]) }} -MAY be present: - - -{{ MACROS___make_sidecar_table("mrs.MRSOptionalFields") }} - ### Example `*_svs.json` ```JSON diff --git a/src/schema/objects/metadata.yaml b/src/schema/objects/metadata.yaml index c4b5350e86..7f7e69b338 100644 --- a/src/schema/objects/metadata.yaml +++ b/src/schema/objects/metadata.yaml @@ -2865,22 +2865,6 @@ RepetitionTime: type: number exclusiveMinimum: 0 unit: s -RepetitionTime__mrs: - name: RepetitionTime - display_name: Repetition Time - description: | - The time between repetitions (TR), specified in seconds. - For dynamic acquisitions, such as cardiac-triggered acquisitions where the TR - may differ over the scan, an array of numbers can be used. - anyOf: - - type: number - exclusiveMinimum: 0 - unit: s - - type: array - items: - type: number - exclusiveMinimum: 0 - unit: s RepetitionTimeExcitation: name: RepetitionTimeExcitation display_name: Repetition Time Excitation @@ -3851,12 +3835,10 @@ VolumeTiming: description: | The time at which each volume was acquired during the acquisition. It is described using a list of times referring to the onset of each volume - in the BOLD series. - The list must have the same length as the BOLD series, + in the series. + The list must have the same length as the series, and the values must be non-negative and monotonically increasing. - This field is mutually exclusive with `"RepetitionTime"` and `"DelayTime"`. - If defined, this requires acquisition time (TA) be defined via either - `"SliceTiming"` or `"AcquisitionDuration"` be defined. + This field is mutually exclusive with `"RepetitionTime"`. type: array minItems: 1 items: diff --git a/src/schema/rules/sidecars/func.yaml b/src/schema/rules/sidecars/func.yaml index f3b7e70b69..475bc23207 100644 --- a/src/schema/rules/sidecars/func.yaml +++ b/src/schema/rules/sidecars/func.yaml @@ -41,6 +41,10 @@ MRIFuncVolumeTiming: VolumeTiming: level: required level_addendum: mutually exclusive with `RepetitionTime` + description_addendum: | + This field is mutually exclusive with `"DelayTime"`. + If defined, this requires acquisition time (TA) be defined via either + `"SliceTiming"` or `"AcquisitionDuration"` be defined. # Timing Parameters MRIFuncTimingParameters: diff --git a/src/schema/rules/sidecars/mrs.yaml b/src/schema/rules/sidecars/mrs.yaml index 7524053ec7..d4be8d58d0 100644 --- a/src/schema/rules/sidecars/mrs.yaml +++ b/src/schema/rules/sidecars/mrs.yaml @@ -51,7 +51,7 @@ MRSSequenceSpecifics: B0ShimmingTechnique: optional B1ShimmingTechnique: optional -MRSRequiredFields: +MRSRelevantFields: selectors: - modality == "mrs" fields: @@ -59,45 +59,56 @@ MRSRequiredFields: SpectrometerFrequency: required SpectralWidth: required EchoTime: required - -MRSRecommendedFields: - selectors: - - modality == "mrs" - fields: NumberOfSpectralPoints: recommended - RepetitionTime__mrs: recommended MixingTime: recommended - InversionTime: recommended for inversion recovery data FlipAngle: recommended AcquisitionVoxelSize: recommended - MatrixSize: recommended for MRSI - VolumeAffineMatrix: recommended for MRSI - EncodingTechnique: recommended for MRSI + ReferenceSignal: recommended -MRSRecommendedFieldsAnatomicalImage: +MRSRepetitionTime: selectors: - modality == "mrs" - - intersects(dataset.datatypes, ["anat"]) + - '!("VolumeTiming" in sidecar)' fields: - AnatomicalImage: + RepetitionTime: level: recommended - level_addendum: if anatomical MRI data are present + level_addendum: mutually exclusive with `VolumeTiming` -MRSRecommendedFieldsNumTransients: +MRSVolumeTiming: selectors: - modality == "mrs" - - intersects([suffix], ["svs", "unloc"]) + - '!("RepetitionTime" in sidecar)' fields: - NumberOfTransients: recommended + VolumeTiming: + level: recommended + level_addendum: mutually exclusive with `RepetitionTime` -MRSRecommendedFieldsRefSignal: +MRSConditionalInversionTime: selectors: - modality == "mrs" - - intersects([suffix], ["mrsref"]) + - entities.inversion fields: - ReferenceSignal: + InversionTime: level: recommended - level_addendum: if MRS reference data are present + level_addendum: if `inv` entity is present + +MRSConditionalAnatomicalImage: + selectors: + - modality == "mrs" + - intersects(dataset.datatypes, ["anat"]) + fields: + AnatomicalImage: + level: recommended + level_addendum: if anatomical MRI data are present + +MRSConditionalNumTransients: + selectors: + - modality == "mrs" + - intersects([suffix], ["svs", "unloc"]) + fields: + NumberOfTransients: + level: recommended + level_addendum: for SVS and unlocalized acquisitions MRSIRecommendedFields: selectors: @@ -107,6 +118,15 @@ MRSIRecommendedFields: MRAcquisitionType: level: recommended level_addendum: for MRSI + MatrixSize: + level: recommended + level_addendum: for MRSI + VolumeAffineMatrix: + level: recommended + level_addendum: for MRSI + EncodingTechnique: + level: recommended + level_addendum: for MRSI MRSOptionalFields: selectors: From 07d2ec8ebd45aec15f04f322fbc52d2c0d55ed13 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Wed, 29 May 2024 11:32:53 -0400 Subject: [PATCH 4/7] Make BodyPart(Details) required if voi entity is present --- src/schema/rules/sidecars/mrs.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/schema/rules/sidecars/mrs.yaml b/src/schema/rules/sidecars/mrs.yaml index d4be8d58d0..1149a1ae79 100644 --- a/src/schema/rules/sidecars/mrs.yaml +++ b/src/schema/rules/sidecars/mrs.yaml @@ -30,10 +30,21 @@ MRSSample: fields: BodyPart: level: optional + level_addendum: required if `voi` entity is present description_addendum: Corresponds to DICOM Tag 0018, 0015 `Body Part Examined`. - BodyPartDetails: optional + BodyPartDetails: + level: optional + level_addendum: required if `voi` entity is present BodyPartDetailsOntology: optional +MRSSampleVOI: + selectors: + - modality == "mrs" + - '"volume" in entities' + fields: + BodyPart: required + BodyPartDetails: required + MRSSequenceSpecifics: selectors: - modality == "mrs" From 0181150f68c08cd7afa942ae67c35c22cd7bdc74 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Wed, 29 May 2024 11:36:55 -0400 Subject: [PATCH 5/7] feat(checks): Add check to ensure MatrixSize and nifti_header.dim are consistent --- src/schema/rules/checks/mrs.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/schema/rules/checks/mrs.yaml b/src/schema/rules/checks/mrs.yaml index a38a3007c6..41417feb93 100644 --- a/src/schema/rules/checks/mrs.yaml +++ b/src/schema/rules/checks/mrs.yaml @@ -12,3 +12,18 @@ MRSNiftiConsistency: checks: - sidecar.ResonantNucleus == nifti_header.mrs.ResonantNucleus - sidecar.SpectrometerFrequency == nifti_header.mrs.SpectrometerFrequency + +MRSMatrixSize: + issue: + code: MRS_MATRIX_SIZE + message: | + MatrixSize metadata must match NIfTI header field `dim[1:4]`. + level: error + selectors: + - datatype == "mrs" + - nifti_header + - type(sidecar.MatrixSize) != "null" + checks: + - sidecar.MatrixSize[0] == nifti_header.dim[1] + - sidecar.MatrixSize[1] == nifti_header.dim[2] + - sidecar.MatrixSize[2] == nifti_header.dim[3] From 83c6f3ea10a0a87a4baf7c98a5e4e953135e35f5 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Thu, 30 May 2024 14:29:17 -0400 Subject: [PATCH 6/7] fix(checks): Use non-trivial expressions for selectors --- src/schema/rules/checks/mrs.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/schema/rules/checks/mrs.yaml b/src/schema/rules/checks/mrs.yaml index 41417feb93..598a59c94f 100644 --- a/src/schema/rules/checks/mrs.yaml +++ b/src/schema/rules/checks/mrs.yaml @@ -8,7 +8,7 @@ MRSNiftiConsistency: level: error selectors: - datatype == "mrs" - - nifti_header.mrs + - type(nifti_header.mrs) != "null" checks: - sidecar.ResonantNucleus == nifti_header.mrs.ResonantNucleus - sidecar.SpectrometerFrequency == nifti_header.mrs.SpectrometerFrequency @@ -21,7 +21,7 @@ MRSMatrixSize: level: error selectors: - datatype == "mrs" - - nifti_header + - type(nifti_header) != "null" - type(sidecar.MatrixSize) != "null" checks: - sidecar.MatrixSize[0] == nifti_header.dim[1] From 1d5e089e75c011bcd9e55486df673c563ef923d6 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 31 May 2024 20:37:39 -0400 Subject: [PATCH 7/7] Uncombine required/recommended/optional fields --- .../magnetic-resonance-spectroscopy.md | 33 +++++++++++++++++-- src/schema/rules/sidecars/mrs.yaml | 7 +++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/modality-specific-files/magnetic-resonance-spectroscopy.md b/src/modality-specific-files/magnetic-resonance-spectroscopy.md index 910d4b70a3..d5a0d992ac 100644 --- a/src/modality-specific-files/magnetic-resonance-spectroscopy.md +++ b/src/modality-specific-files/magnetic-resonance-spectroscopy.md @@ -179,7 +179,7 @@ A guide for using macros can be found at ### MRS-relevant fields -Metadata fields that MUST, SHOULD or MAY be present: +Metadata fields that MUST be present: {{ MACROS___make_sidecar_table([ - "mrs.MRSRelevantFields", + "mrs.MRSRequiredFields", + ]) +}} + +Metadata fields that SHOULD be present: + + +{{ MACROS___make_sidecar_table([ + "mrs.MRSRecommendedFields", "mrs.MRSRepetitionTime", "mrs.MRSVolumeTiming", "mrs.MRSConditionalNumTransients", "mrs.MRSIRecommendedFields", "mrs.MRSConditionalInversionTime", "mrs.MRSConditionalAnatomicalImage", + ]) +}} + +Metadata fields that MAY be present: + + +{{ MACROS___make_sidecar_table([ "mrs.MRSOptionalFields", ]) }} diff --git a/src/schema/rules/sidecars/mrs.yaml b/src/schema/rules/sidecars/mrs.yaml index 1149a1ae79..75fe94bd2b 100644 --- a/src/schema/rules/sidecars/mrs.yaml +++ b/src/schema/rules/sidecars/mrs.yaml @@ -62,7 +62,7 @@ MRSSequenceSpecifics: B0ShimmingTechnique: optional B1ShimmingTechnique: optional -MRSRelevantFields: +MRSRequiredFields: selectors: - modality == "mrs" fields: @@ -70,6 +70,11 @@ MRSRelevantFields: SpectrometerFrequency: required SpectralWidth: required EchoTime: required + +MRSRecommendedFields: + selectors: + - modality == "mrs" + fields: NumberOfSpectralPoints: recommended MixingTime: recommended FlipAngle: recommended