diff --git a/README.md b/README.md index 0827ed8e07..e0b608b0e2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ BIDS currently supports the following data modalities with more to come in the f - physiological - PET - microscopy +- fNIRS # Formatting your data with BIDS diff --git a/mkdocs.yml b/mkdocs.yml index 1c2690b88b..63273f3c28 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,7 @@ nav: - Genetic Descriptor: 04-modality-specific-files/08-genetic-descriptor.md - Positron Emission Tomography: 04-modality-specific-files/09-positron-emission-tomography.md - Microscopy: 04-modality-specific-files/10-microscopy.md + - Functional Near-Infrared Spectroscopy: 04-modality-specific-files/11-functional-near-infrared-spectroscopy.md - Derivatives: - BIDS Derivatives: 05-derivatives/01-introduction.md - Common data types and metadata: 05-derivatives/02-common-data-types.md diff --git a/src/01-introduction.md b/src/01-introduction.md index e5f86ee927..9cfd3fe5ef 100644 --- a/src/01-introduction.md +++ b/src/01-introduction.md @@ -156,6 +156,10 @@ For example: **qMRI-BIDS: An extension to the brain imaging data structure for quantitative magnetic resonance imaging data.** Scientific Data 9, 517 (2022). [doi:10.1038/s41597-022-01571-4](https://doi.org/10.1038/s41597-022-01571-4) +#### NIRS + +- (publication forthcoming) + ### Research Resource Identifier (RRID) BIDS has also a diff --git a/src/04-modality-specific-files/11-functional-near-infrared-spectroscopy.md b/src/04-modality-specific-files/11-functional-near-infrared-spectroscopy.md new file mode 100644 index 0000000000..62d50a59cf --- /dev/null +++ b/src/04-modality-specific-files/11-functional-near-infrared-spectroscopy.md @@ -0,0 +1,297 @@ +# Functional Near-Infrared Spectroscopy + +Support for functional Near-Infrared Spectroscopy (fNIRS) was developed as a +[BIDS Extension Proposal](../07-extensions.md#bids-extension-proposals). +Please see [Citing BIDS](../01-introduction.md#citing-bids) +on how to appropriately credit this extension when referring to it in the +context of the academic literature. + +Several [example fNIRS datasets](https://github.com/bids-standard/bids-examples#fnirs-datasets) +have been formatted using this specification and can be used for practical guidance when curating a new dataset. + +## fNIRS recording data + +{{ MACROS___make_filename_template(datatypes=["nirs"], suffixes=["nirs", "events", "channels", "optodes", "coordsystem"]) }} + +Only the *Shared Near Infrared Spectroscopy Format* ([SNIRF](https://github.com/fNIRS/snirf)) +file specification is supported in BIDS. The SNIRF +specification supports one or more fNIRS datasets to be stored in a single +`.snirf` file. However, to be BIDS compatible, each SNIRF file MUST contain +only a single run. A limited set of fields from the SNIRF specification are +replicated in the BIDS specification. This redundancy allows the data to be +easily parsed by humans and machines that do not have a SNIRF reader at hand, +which improves findability and tooling development. + +Raw fNIRS data in the native format, if different from SNIRF, can also +be stored in the [`/sourcedata`](../02-common-principles.md#source-vs-raw-vs-derived-data) +directory along with code to convert the data to +SNIRF in the [`/code`](../02-common-principles.md#storage-of-derived-datasets) directory. +The unprocessed raw data should be stored in +the manufacturer's format before any additional processing or conversion is applied. +Retaining the native file format is especially valuable in a case when conversion elicits the +loss of crucial metadata unique to specific manufacturers and fNIRS systems. + +### Terminology + +For proper documentation of fNIRS recording metadata, it is important +to understand the difference between a Source, Detector, and Channel as these +are defined differently to other modalities, such as EEG. The following definitions +apply in this document: + +- Source - A light emitting device, sometimes called a transmitter. + +- Detector - A photoelectric transducer, sometimes called a receiver. + +- Optode - Refers to either a source or detector. + +- Channel - A paired coupling of a source and a detector with one specific wavelength of light. + It is common for a single Source-Detector pair to result in two or more channels + with different wavelengths. + +### Sidecar JSON (`*_nirs.json`) + +It is common within the fNIRS community for researchers to build their own caps +and optode holders to position their sources and detectors, or for optodes to +be directly attached to the scalp with adhesive. To facilitate description of +the wide variety of possible configurations, several fields are RECOMMENDED within +the `*_nirs.json` file. +Additionally, in certain situations, reserved keywords MUST be used. +When custom modifications are made to a commercially available cap or a custom cap is used, +then the reserved keyword `custom` MUST be used for the `CapManufacturersModelName` field. +When a custom made cap is used, that is, no (modified) commercially available cap, +the reserved keyword `custom` MUST be used in the `CapManufacturer` field. +If no cap is used, the reserved keyword `none` MUST be used in the `CapManufacturer` +and `CapManufacturersModelName` field. +The use of `NIRSPlacementScheme` is RECOMMENDED when no cap or a customized cap is used, +and describes the positioning of the optodes. +This field may also contain a reference to a file providing a graphical depiction of the cap, +for example a PDF file, a photo, or a bitmap drawing. +If the referred file is not specified in BIDS, it MAY be placed in the +[`/sourcedata`](../02-common-principles.md#source-vs-raw-vs-derived-data) directory. +To clarify the usage and interaction of these fields, the following examples are provided: + +- If a commercial cap such as EasyCap actiCAP 64 Ch Standard-2 was used: + ```JSON + "CapManufacturer": "EasyCap", + "CapManufacturersModelName": "actiCAP 64 Ch Standard-2", + "NIRSPlacementScheme": "10-20" + ``` + +- If an Artinis Medical Systems cap with custom positions, +as may be done by cutting custom holes in the cap, +was used: + ```JSON + "CapManufacturer": "Artinis Medical Systems", + "CapManufacturersModelName": "headcap with print, size L, it was modified by adding holes for the optodes according to the NIRSPlacementScheme and optode_layout.pdf", + "NIRSPlacementScheme": "see optode_layout.pdf: 2 groups over the left and right dlPFC, 2 groups over the left and right PPC, 1 group over the left M1 and PMC" + ``` + +- If a completely custom cap was knitted: + ```JSON + "CapManufacturer": "custom", + "CapManufacturersModelName": "custom knitted cap with holes for optodes according to the NIRSPlacementScheme and optode_knitted_layout.jpg", + "NIRSPlacementScheme": "see optode_knitted_layout.jpg: 2 groups over the left and right dlPFC, 2 groups over the left and right PPC." + ``` + +- If no cap was used and optodes were taped to the scalp + at positions Cz, C1 and C2: + ```JSON + "CapManufacturer": "none", + "CapManufacturersModelName": "none", + "NIRSPlacementScheme": ["Cz", "C1", "C2"], + ``` + In these cases additional information regarding channels and optodes SHOULD be placed in `*_channels.tsv` and `*_optodes.tsv` files. + +Closely spaced or short-separation source-detector pairs are often included in fNIRS measurements to +obtain a measure of systemic, rather than neural, activity. These source-detector +pairs are referred to as *short channels*. There is variation in how manufacturers +implement these short channels, some use specialised sources or detectors, +and the placement mechanisms vary. +It is beyond the scope of the BIDS specification to define what constitutes a short channel, +and detailed characteristics of channels may be stored within the SNIRF file +(for example, in the `sourcePower` field). +However, to improve searchability and ease of access for users, it is useful to +know if short channels were included in the fNIRS measurements; the presence of short channels is +is stored in the field `ShortChannelCount`. +If the field `ShortChannelCount` is populated, then the optional column `short_channel` +may be used in `*_channels.tsv` to describe which channels were specified as short. + +Generic fields: For consistency between studies and institutions, +we encourage users to extract the values of these fields from the actual raw data. +Whenever possible, please avoid using ad hoc wording. + +{{ MACROS___make_sidecar_table("nirs.NirsBase") }} + +Specific fNIRS fields that are REQUIRED or may be REQUIRED depending on other metadata values: + +{{ MACROS___make_sidecar_table("nirs.NirsRequired") }} + +Specific fNIRS fields that SHOULD be present: + +{{ MACROS___make_sidecar_table("nirs.NirsRecommend") }} + +Example: + +```JSON +{ + "TaskName": "visual", + "InstitutionName": "Macquarie University. Australian Hearing Hub", + "InstitutionAddress": "6 University Ave, Macquarie University NSW 2109 Australia", + "Manufacturer": "NIRx", + "ManufacturersModelName": "NIRScout", + "TaskDescription": "visual gratings and noise patterns", + "Instructions": "look at the dot in the center of the screen and press the button when it changes color", + "SamplingFrequency": 3.7, + "NIRSChannelCount": 56, + "NIRSSourceOptodeCount": 16, + "NIRSDetectorOptodeCount": 16, + "ACCELChannelCount": 0, + "SoftwareFilters": "n/a", + "RecordingDuration": 233.639, + "HardwareFilters": {"Highpass RC filter": {"Half amplitude cutoff (Hz)": 0.0159, "Roll-off": "6dBOctave"}}, + "CapManafacturer": "NIRx", + "CapManufacturersModelName": "Headband with print (S-M)", + "NIRSPlacementScheme": "n/a", +} +``` + +## Channels description (`*_channels.tsv`) + +{{ MACROS___make_filename_template(datatypes=["nirs"], suffixes=["channels"]) }} + +This file is RECOMMENDED as it provides easily searchable information across BIDS datasets. +Channels are a pairing of source and detector optodes with a specific wavelength of light. +Unlike in other modalities, not all pairings of optodes correspond to meaningful data +and not all pairs have to be recorded or represented in the data. Note that the source +and detector names used in the channel specifications are specified in the `*_optodes.tsv` +file below. If a `*_channels.tsv` file is specified, an `*_optodes.tsv` file MUST be specified as well. +The required columns in the `*_channels.tsv` file MUST be ordered as listed below. + +The BIDS specification supports several types of fNIRS devices which output raw data in +different forms. The type of measurement is specified in the `type` column. For example, +when measurements are taken with a continuous wave (CW) device that saves the data +as optical density, the `type` should be `NIRSCWOPTICALDENSITY` and the `units` should be `unitless`, +this is equivalent to SNIRF data type `dOD`. + +The columns of the channels description table stored in `*_channels.tsv` are: + + + +{{ MACROS___make_columns_table("nirs.nirsChannels") }} + +### Restricted keyword list for the channel types + +All fNIRS channels types MUST correspond to a [valid SNIRF data type](https://github.com/fNIRS/snirf/blob/master/snirf_specification.md#appendix). +Additional channels that are recorded simultaneously with the fNIRS +device and stored in the same data file SHOULD be included as well. +However, additional channels that are simultaneously recorded with a different device +SHOULD be stored according to their appropriate modality specification. +For example, motion data that was simultaneously recorded with a different device should be specified +according to BEP029 and not according to the fNIRS data type. +Whereas, if the motion data was acquired in with the fNIRS device itself, it should be included here with the fNIRS data. +Any of the channel types defined in other BIDS specification MAY be used here as well such as `ACCEL` or `MAGN`. +As several of these data types are commonly acquired using fNIRS devices they are included as an example at the base of the table. +Note that upper-case is REQUIRED. + +| **Keyword** | **Description** | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| NIRSCWAMPLITUDE | Continuous wave amplitude measurements. Equivalent to dataType 001 in SNIRF. | +| NIRSCWFLUORESCENSEAMPLITUDE | Continuous wave fluorescence amplitude measurements. Equivalent to dataType 051 in SNIRF. | +| NIRSCWOPTICALDENSITY | Continuous wave change in optical density measurements. Equivalent to dataTypeLabel dOD in SNIRF. | +| NIRSCWHBO | Continuous wave oxygenated hemoglobin (oxyhemoglobin) concentration measurements. Equivalent to dataTypeLabel HbO in SNIRF. | +| NIRSCWHBR | Continuous wave deoxygenated hemoglobin (deoxyhemoglobin) concentration measurements. Equivalent to dataTypeLabel HbR in SNIRF. | +| NIRSCWMUA | Continuous wave optical absorption measurements. Equivalent to dataTypeLabel mua in SNIRF. | +| ACCEL | Accelerometer channel, one channel for each orientation. An extra column `component` for the axis of the orientation MUST be added to the `*_channels.tsv` file (x, y or z). | +| GYRO | Gyrometer channel, one channel for each orientation. An extra column `component` for the axis of the orientation MUST be added to the `*_channels.tsv` file (x, y or z). | +| MAGN | Magnetomenter channel, one channel for each orientation. An extra column `component` for the axis of the orientation MUST be added to the `*_channels.tsv` file (x, y or z). | +| MISC | Miscellaneous | + +Example: + +```Text +Name type source detector wavelength_nominal units +S1-D1 NIRSCWAMPLITUDE A1 Fz 760 V +S1-D1 NIRSCWAMPLITUDE A1 Fz 850 V +S1-D2 NIRSCWAMPLITUDE A1 Cz 760 V +S2-D1 NIRSCWAMPLITUDE A2 Fz 760 V +S3-D4 NIRSCWAMPLITUDE VisS2 VisD4 760 V +``` + +## Optode description (`*_optodes.tsv`) + +{{ MACROS___make_filename_template(datatypes=["nirs"], suffixes=["optodes"]) }} + +File that provides the location and type of optodes. Note that coordinates MUST be +expressed in Cartesian coordinates according to the NIRSCoordinateSystem and +NIRSCoordinateSystemUnits fields in `*_coordsystem.json`. If an `*_optodes.tsv` +file is specified, a `*_coordsystem.json` file MUST be specified as well. +The order of the required columns in the `*_optodes.tsv` file MUST be as listed below. + +The x, y, and z positions are for measured locations, for example, with a polhemus +digitizer. If you also have idealized positions, where you wish the optodes to be +placed, these can be listed in the template values +(for example for "template positions" computed on a sphere). +SNIRF contains arrays for both +the 3D and 2D locations of data. In BIDS the `*_optodes.tsv` file MUST contain the 3D locations. Only in case 3D positions are unavailable the 2D locations should be used, setting the z field to an `n/a` value. + +The columns of the optodes description table stored in `*_optodes.tsv` are: + +{{ MACROS___make_columns_table("nirs.nirsOptodes") }} + +Example: +```Text +name type x y z template_x template_y template_z +A1 source -0.0707 0.0000 -0.0707 -0.07 0.00 0.07 +Fz detector 0.0000 0.0714 0.0699 0.0 0.07 0.07 +S1 source -0.2707 0.0200 -0.1707 -0.03 0.02 -0.2 +D2 detector 0.0022 0.1214 0.0299 0.0 0.12 0.03 +VisS2 source -0.1707 0.1200 -0.3707 -0.1 0.1 -0.4 +VisD4 detector 0.0322 0.2214 0.2299 0.02 0.22 0.23 +``` + +## Coordinate System JSON (`*_coordsystem.json`) + +{{ MACROS___make_filename_template(datatypes=["nirs"], suffixes=["coordsystem"]) }} + +A `*_coordsystem.json` file is used to specify the fiducials, the location of anatomical landmarks, +and the coordinate system and units in which the position of optodes and landmarks is expressed. +Fiducials are objects with a well-defined location used to facilitate the localization of sensors +and co-registration, anatomical landmarks are locations on a research subject such as the nasion +(for a detailed definition see [coordinate system appendix](../appendices/coordinate-systems.md)). +The `*_coordsystem.json` is REQUIRED if the optional `*_optodes.tsv` is present. If a corresponding +anatomical MRI is available, the locations of anatomical landmarks in that scan should also be stored +in the `*_T1w.json` file which goes alongside the fNIRS data. + +Not all fNIRS systems provide 3D coordinate information or digitization capabilities. +In this case, only x and y are specified and z is `"n/a"`. + +General fields: + +{{ MACROS___make_sidecar_table("nirs.CoordsystemGeneral") }} + +Fields relating to the fNIRS optode positions: + +{{ MACROS___make_sidecar_table(["nirs.CoordinateSystem", "nirs.CoordinateSystemDescriptionRec"]) }} + +Fields relating to the position of fiducials measured during an fNIRS session/run: + +{{ MACROS___make_sidecar_table(["nirs.Fiducials", "nirs.FiducialsCoordinateSystemDescriptionRec"]) }} + +Fields relating to the position of anatomical landmarks measured during an fNIRS session/run: + +{{ MACROS___make_sidecar_table(["nirs.AnatomicalLandmark", "nirs.AnatomicalLandmarkCoordinateSystemDescriptionRec"]) }} + +Example: +```text +{ + "NIRSCoordinateSystem": "Other", + "NIRSCoordinateUnits": "mm", + "NIRSCoordinateSystemDescription": "RAS orientation: Origin halfway between LPA and RPA, positive x-axis towards RPA, positive y-axis orthogonal to x-axis through Nasion, z-axis orthogonal to xy-plane, pointing in superior direction.", + "FiducialsDescription": "Optodes and fiducials were digitized with Polhemus, fiducials were recorded as the centre of vitamin E capsules sticked on the left/right pre-auricular and on the nasion, these are also visible on the T1w MRI" +} +``` diff --git a/src/schema/meta/context.yaml b/src/schema/meta/context.yaml index 3bdefaf873..0cc8c26d61 100644 --- a/src/schema/meta/context.yaml +++ b/src/schema/meta/context.yaml @@ -187,7 +187,25 @@ context: n_cols: description: 'Number of columns in bvec file' type: integer - + channels: + description: 'Channels file' + type: object + properties: + path: + description: 'Path to associated channels file' + type: string + type: + description: 'Contents of the type column' + type: array + items: + type: string + coordsystem: + description: 'Coordinate system file' + type: object + properties: + path: + description: 'Path to associated coordsystem file' + type: string # The following properties are populated if the current file is of an appropriate type columns: description: 'TSV columns, indexed by column header, values are arrays with column contents' diff --git a/src/schema/objects/columns.yaml b/src/schema/objects/columns.yaml index 52e7f26fab..a229997d59 100644 --- a/src/schema/objects/columns.yaml +++ b/src/schema/objects/columns.yaml @@ -52,6 +52,24 @@ color: Hexadecimal. Label color for visualization. type: string unit: hexadecimal +detector__channels: + name: detector + display_name: Detector Name + description: | + Name of the detector as specified in the `*_optodes.tsv` file. + `n/a` for channels that do not contain fNIRS signals (for example, acceleration). + anyOf: + - type: string + - type: string + enum: + - n/a +detector_type: + name: detector_type + display_name: Detector Type + description: | + The type of detector. Only to be used if the field `DetectorType` in `*_nirs.json` is set to `mixed`. + anyOf: + - type: string derived_from: name: derived_from display_name: Derived from @@ -66,6 +84,12 @@ description: description: | Brief free-text description of the channel, or other information of interest. type: string +description__optode: + name: description + display_name: Description + description: | + Free-form text description of the optode, or other information of interest. + type: string dimension: name: dimension display_name: Dimension @@ -240,6 +264,12 @@ name__electrodes: description: | Name of the electrode contact point. type: string +name__optodes: + name: name + display_name: Optode name + description: | + Name of the optode, must be unique. + type: string # name column for dseg.tsv files name__segmentations: name: name @@ -278,6 +308,16 @@ onset: acquired data point. type: number unit: s +orientation_component: + name: orientation_component + display_name: Orientation Component + description: | + Description of the orientation of the channel. + type: string + enum: + - x + - y + - z pathology: name: pathology display_name: Pathology @@ -420,6 +460,14 @@ sex: - OTHER - Other - n/a +short_channel: + name: short_channel + display_name: Short Channel + description: | + Is the channel designated as short. + The total number of channels listed as short channels + SHOULD be stored in `ShortChannelCount` in `*_nirs.json`. + type: boolean size: name: size display_name: Electrode size @@ -440,6 +488,24 @@ software_filters: - type: string enum: - n/a +source__channels: + name: source + display_name: Source name + description: | + Name of the source as specified in the `*_optodes.tsv` file. + `n/a` for channels that do not contain fNIRS signals (for example, acceleration). + anyOf: + - type: string + - type: string + enum: + - n/a +source__optodes: + name: source_type + display_name: Source type + description: | + The type of source. Only to be used if the field `SourceType` in `*_nirs.json` is set to `mixed`. + anyOf: + - type: string species: name: species display_name: Species @@ -614,6 +680,52 @@ type__ieeg_channels: - DAC - REF - OTHER +type__nirs_channels: + name: type + display_name: Channel type + description: | + Type of channel; MUST use the channel types listed below. + Note that the type MUST be in upper-case. + type: string + enum: + - NIRSCWAMPLITUDE + - NIRSCWFLUORESCENSEAMPLITUDE + - NIRSCWOPTICALDENSITY + - NIRSCWHBO + - NIRSCWHBR + - NIRSCWMUA + - MEGMAG + - MEGGRADAXIAL + - MEGGRADPLANAR + - MEGREFMAG + - MEGREFGRADAXIAL + - MEGREFGRADPLANAR + - MEGOTHER + - EEG + - ECOG + - SEEG + - DBS + - VEOG + - HEOG + - EOG + - ECG + - EMG + - TRIG + - AUDIO + - PD + - EYEGAZE + - PUPIL + - MISC + - SYSCLOCK + - ADC + - DAC + - HLU + - FITERR + - ACCEL + - GYRO + - MAGN + - MISC + - OTHER # type column for electrodes.tsv files type__electrodes: name: type @@ -621,6 +733,16 @@ type__electrodes: description: | Type of the electrode (for example, cup, ring, clip-on, wire, needle). type: string +type__optodes: + name: type + display_name: Type + description: | + The type of the optode. + type: string + enum: + - source + - detector + - n/a units: name: units display_name: Units @@ -630,6 +752,17 @@ units: (see [Units](SPEC_ROOT/02-common-principles.md#units)). type: string format: unit +units__nirs: + name: units + display_name: Units + description: | + Physical unit of the value represented in this channel, + specified according to the SI unit symbol and possibly prefix symbol, + or as a derived SI unit (for example, `V`, or unitless for changes in optical densities). + For guidelines about units see the [Appendix](SPEC_ROOT/appendices/units.md) + and [Common Principles](SPEC_ROOT/02-common-principles.md#units) pages. + type: string + format: unit value: name: value display_name: Marker value @@ -652,6 +785,34 @@ volume_type: - m0scan - deltam - cbf +wavelength_nominal: + name: wavelength_nominal + display_name: Wavelength nominal + description: | + Specified wavelength of light in nm. + `n/a` for channels that do not contain raw fNIRS signals (for example, acceleration). + This field is equivalent to `/nirs(i)/probe/wavelengths` in the SNIRF specification. + anyOf: + - type: number + - type: string + enum: + - n/a +wavelength_actual: + name: wavelength_actual + display_name: Wavelength actual + description: | + Measured wavelength of light in nm. + `n/a` for channels that do not contain raw NIRS signals (acceleration). + This field is equivalent to `measurementList.wavelengthActual` in the SNIRF specification. + type: number +wavelength_emission_actual: + name: wavelength_emission_actual + display_name: Wavelength emission actual + description: | + Measured emission wavelength of light in nm. + `n/a` for channels that do not contain raw NIRS signals (acceleration). + This field is equivalent to `measurementList.wavelengthEmissionActual` in the SNIRF specification. + type: number whole_blood_radioactivity: name: whole_blood_radioactivity display_name: Whole blood radioactivity @@ -681,3 +842,66 @@ z: - type: string enum: - n/a +x__optodes: + name: x + display_name: X position + description: | + Recorded position along the x-axis. + `"n/a"` if not available. + anyOf: + - type: number + - type: string + enum: + - n/a +y__optodes: + name: y + display_name: Y position + description: | + Recorded position along the y-axis. + `"n/a"` if not available. + anyOf: + - type: number + - type: string + enum: + - n/a +z__optodes: + name: z + display_name: Z position + description: | + Recorded position along the z-axis. + `"n/a"` if not available. + anyOf: + - type: number + - type: string + enum: + - n/a +template_x: + name: template_x + display_name: X template position + description: | + Assumed or ideal position along the x axis. + anyOf: + - type: number + - type: string + enum: + - n/a +template_y: + name: template_y + display_name: Y template position + description: | + Assumed or ideal position along the y axis. + anyOf: + - type: number + - type: string + enum: + - n/a +template_z: + name: template_z + display_name: Z template position + description: | + Assumed or ideal position along the z axis. + anyOf: + - type: number + - type: string + enum: + - n/a diff --git a/src/schema/objects/common_principles.yaml b/src/schema/objects/common_principles.yaml index 2a779b6c1d..a418b74004 100644 --- a/src/schema/objects/common_principles.yaml +++ b/src/schema/objects/common_principles.yaml @@ -39,6 +39,8 @@ data_type: 10. `pet` (positron emission tomography) 11. `micr` (microscopy) + + 12. `nirs` (near infrared spectroscopy) dataset: name: Dataset display_name: Dataset diff --git a/src/schema/objects/datatypes.yaml b/src/schema/objects/datatypes.yaml index 13d5907d74..7b1608d081 100644 --- a/src/schema/objects/datatypes.yaml +++ b/src/schema/objects/datatypes.yaml @@ -53,3 +53,8 @@ pet: display_name: Positron Emission Tomography description: | Positron emission tomography data +nirs: + value: nirs + display_name: Functional Near-Infrared Spectroscopy + description: + Near-Infrared data organized around the SNIRF format diff --git a/src/schema/objects/extensions.yaml b/src/schema/objects/extensions.yaml index 90aaf84586..6bc111a99f 100644 --- a/src/schema/objects/extensions.yaml +++ b/src/schema/objects/extensions.yaml @@ -237,6 +237,11 @@ set: The format used by the MATLAB toolbox [EEGLAB](https://sccn.ucsd.edu/eeglab). Each recording consists of a `.set` file with an optional `.fdt` file. +snirf: + value: .snirf + display_name: Shared Near Infrared Spectroscopy Format + description: | + HDF5 file organized according to the [SNIRF specification](https://github.com/fNIRS/snirf) sqd: value: .sqd display_name: SQD diff --git a/src/schema/objects/metadata.yaml b/src/schema/objects/metadata.yaml index f7615ee7fc..6011ac012c 100644 --- a/src/schema/objects/metadata.yaml +++ b/src/schema/objects/metadata.yaml @@ -2,6 +2,13 @@ # This file defines valid BIDS metadata fields. # These definitions include the field names, their descriptions, and valid values. # This file **does not** define how and when metadata fields can be used with a given file. +ACCELChannelCount: + name: ACCELChannelCount + display_name: Accelerometer channel count + description: | + Number of accelerometer channels. + type: integer + minimum: 0 Acknowledgements: name: Acknowledgements display_name: Acknowledgements @@ -368,7 +375,7 @@ CapManufacturersModelName: name: CapManufacturersModelName display_name: Cap Manufacturers Model Name description: | - Manufacturer's designation of the EEG cap model + Manufacturer's designation of the cap model (for example, `"actiCAP 64 Ch Standard-2"`). type: string CellType: @@ -586,6 +593,21 @@ Description: description: | Free-form natural language description. type: string +DetectorType: + name: DetectorType + display_name: Detector Type + description: | + Type of detector. This is a free form description with the following suggested terms: + `"SiPD"`, `"APD"`. Preferably a specific model/part number is supplied. + If individual channels have different `DetectorType`, + then the field here should be specified as `"mixed"` + and this column should be included in `optodes.tsv`. + anyOf: + - type: string + format: unit + - type: string + enum: + - mixed DeviceSerialNumber: name: DeviceSerialNumber display_name: Device Serial Number @@ -1127,6 +1149,13 @@ GradientSetType: insert set, then the specifications of the actual gradient coil should be reported independently. type: string +GYROChannelCount: + name: GYROChannelCount + display_name: Gyrometer Channel Count + description: | + Number of gyrometer channels. + type: integer + minimum: 0 HED: name: HED display_name: HED @@ -1677,6 +1706,13 @@ M0Type: - Included - Estimate - Absent +MAGNChannelCount: + name: MAGNChannelCount + display_name: Magnetometer Channel Count + description: | + Number of magnetometer channels. + type: integer + minimum: 0 MEGChannelCount: name: MEGChannelCount display_name: MEG Channel Count @@ -1963,6 +1999,87 @@ NegativeContrast: usage of a contrast agent in conjunction with a T2\* weighted acquisition protocol. type: boolean +NIRSChannelCount: + name: NIRSChannelCount + display_name: NIRS Channel Count + description: | + Total number of NIRS channels, including short channels. + Corresponds to the number of rows in `channels.tsv` with any NIRS type. + type: integer + minimum: 0 +NIRSSourceOptodeCount: + name: NIRSSourceOptodeCount + display_name: NIRS Source Optode Count + description: | + Number of fNIRS sources. + Corresponds to the number of rows in `optodes.tsv` with type `"source"`. + type: integer + minimum: 1 +NIRSDetectorOptodeCount: + name: NIRSDetectorOptodeCount + display_name: NIRS Detector Optode Channel Count + description: | + Number of fNIRS detectors. + Corresponds to the number of rows in `optodes.tsv` with type `"detector"`. + type: integer + minimum: 1 +NIRSPlacementScheme: + name: NIRSPlacementScheme + display_name: NIRS Placement Scheme + description: | + Placement scheme of NIRS optodes. + Either the name of a standardized placement system (for example, `"10-20"`) + or an array of standardized position names (for example, `["Cz", "Pz"]`). + This field should only be used if a cap was not used. + If a standard cap was used, then it should be specified in `CapManufacturer` + and `CapManufacturersModelName` and this field should be set to `"n/a"` + anyOf: + - type: string + - type: array + items: + type: string +NIRSCoordinateSystem: + name: NIRSCoordinateSystem + display_name: NIRS Coordinate System + description: | + Defines the coordinate system in which the optode positions are expressed. + + See + [Appendix VIII](SPEC_ROOT/appendices/coordinate-systems.md) + for a list of restricted keywords for coordinate systems. + If `"Other"`, a definition of the coordinate system MUST be + provided in `NIRSCoordinateSystemDescription`. + anyOf: + - $ref: objects.metadata._MEGCoordSys + - $ref: objects.metadata._EEGCoordSys + - $ref: objects.metadata._StandardTemplateCoordSys + - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys +NIRSCoordinateSystemDescription: + name: NIRSCoordinateSystemDescription + display_name: NIRS Coordinate System Description + description: | + Free-form text description of the coordinate system. + May also include a link to a documentation page or paper describing the + system in greater detail. + type: string +NIRSCoordinateUnits: + name: NIRSCoordinateUnits + display_name: NIRS Coordinate Units + description: | + Units of the coordinates of `NIRSCoordinateSystem`. + type: string + enum: + - m + - mm + - cm + - n/a +NIRSCoordinateProcessingDescription: + name: NIRSCoordinateProcessingDescription + display_name: NIRS Coordinate Processing Description + description: | + Has any post-processing (such as projection) been done on the optode + positions (for example, `"surface_projection"`, `"n/a"`). + type: string NonlinearGradientCorrection: name: NonlinearGradientCorrection display_name: Nonlinear Gradient Correction @@ -2602,6 +2719,18 @@ SamplingFrequency: regardless of their type (for example, `2400`). type: number unit: Hz +SamplingFrequency__nirs: + name: SamplingFrequency + display_name: Sampling Frequency + description: | + Sampling frequency (in Hz) of all the data in the recording, + regardless of their type (for example, `2400`). + anyOf: + - type: number + unit: Hz + - type: string + enum: + - n/a ScaleFactor: name: ScaleFactor display_name: Scale Factor @@ -2678,6 +2807,13 @@ SequenceVariant: - type: array items: type: string +ShortChannelCount: + name: ShortChannelCount + display_name: Short Channel Count + description: | + The number of short channels. 0 indicates no short channels. + type: integer + minimum: 0 SinglesRate: name: SinglesRate display_name: Singles Rate @@ -2831,11 +2967,18 @@ Sources: [DEPRECATED](SPEC_ROOT/02-common-principles.md#definitions). type: array items: - anyOf: - - type: string - format: dataset_relative - - type: string - format: bids_uri + type: string + format: dataset_relative +SourceType: + name: SourceType + display_name: Source Type + description: | + Type of source. Preferably a specific model/part number is supplied. + This is a freeform description, but the following keywords are suggested: + `"LED"`, `"LASER"`, `"VCSEL"`. If individual channels have different SourceType, + then the field here should be specified as "mixed" + and this column should be included in optodes.tsv. + type: string SpatialReference: name: SpatialReference display_name: Spatial Reference diff --git a/src/schema/objects/suffixes.yaml b/src/schema/objects/suffixes.yaml index 5146907cdb..d71525ed1a 100644 --- a/src/schema/objects/suffixes.yaml +++ b/src/schema/objects/suffixes.yaml @@ -669,6 +669,17 @@ meg: description: | Unprocessed MEG data stored in the native file format of the MEG instrument with which the data was collected. +nirs: + value: nirs + display_name: Near Infrared Spectroscopy + description: + Data associated with a Shared Near Infrared Spectroscopy Format file. +optodes: + value: optodes + display_name: Optodes + description: | + Either a light emitting device, sometimes called a transmitter, or a photoelectric transducer, sometimes called a + receiver. pet: value: pet display_name: Positron Emission Tomography diff --git a/src/schema/rules/checks/nirs.yaml b/src/schema/rules/checks/nirs.yaml new file mode 100644 index 0000000000..bbfa52cf7d --- /dev/null +++ b/src/schema/rules/checks/nirs.yaml @@ -0,0 +1,97 @@ +--- +NASamplingFreq: + selectors: + - suffix == "nirs" + - samplingFrequency == "n/a" + checks: + - associations.channels.sampling_frequency != null + +NIRSChannelCount: + selectors: + - datatype == "nirs" + - suffix == "nirs" + checks: + - | + sidecar.NIRSChannelCount + == count(associations.channels.type, "NIRSCWAMPLITUDE") + + count(associations.channels.type, "NIRSCWFLUORESCENSEAMPLITUDE") + + count(associations.channels.type, "NIRSCWOPTICALDENSITY") + + count(associations.channels.type, "NIRSCWHBO") + + count(associations.channels.type, "NIRSCWHBR") + + count(associations.channels.type, "NIRSCWMUA") + +ACCELChannelCountReq: + selectors: + - suffix == "nirs" + checks: + - sidecar.ACCELChannelCount == count(associations.channels.type, "ACCEL") + +GYROChannelCountReq: + selectors: + - suffix == "nirs" + checks: + - sidecar.GYROChannelCount == count(associations.channels.type, "GYRO") + +MAGNChannelCountReq: + selectors: + - suffix == "nirs" + checks: + - sidecar.MAGNChannelCount == count(associations.channels.type, "MAGN") + +ShortChannelCountReq: + selectors: + - suffix == "nirs" + checks: + - sidecar.ShortChannelCount == count(associations.channels.short_channel, true) + +OrientationComponent: + selectors: + - datatype == "nirs" + - suffix == "channels" + - extension == ".tsv" + - intersect(columns.type, ["ACCEL", "GYRO", "MAGN"]) + checks: + - columns.orientation_component != null + +RequiredChannels: + selectors: + - datatype == "nirs" + - suffix == "optodes" + - extension == ".tsv" + checks: + - associations.channels != null + +RequiredTemplateX: + selectors: + - datatype == "nirs" + - suffix == "optodes" + - extension == ".tsv" + - intersect(columns.x, ["n/a"]) + checks: + - columns.template_x != null + +RequiredTemplateY: + selectors: + - datatype == "nirs" + - suffix == "optodes" + - extension == ".tsv" + - intersect(columns.y, ["n/a"]) + checks: + - columns.template_y != null + +RequiredTemplateZ: + selectors: + - datatype == "nirs" + - suffix == "optodes" + - extension == ".tsv" + - intersect(columns.z, ["n/a"]) + checks: + - columns.template_z != null + +RequiredCoordsystem: + selectors: + - datatype == "nirs" + - suffix == "optodes" + - extension == ".tsv" + checks: + - associations.coordsystem != null diff --git a/src/schema/rules/datatypes/nirs.yaml b/src/schema/rules/datatypes/nirs.yaml new file mode 100644 index 0000000000..20941b2c8a --- /dev/null +++ b/src/schema/rules/datatypes/nirs.yaml @@ -0,0 +1,98 @@ +--- +nirs: + suffixes: + - nirs + extensions: + - .snirf + - .json + datatypes: + - nirs + entities: + subject: required + session: optional + task: required + acquisition: optional + run: optional + +channels: + suffixes: + - channels + extensions: + - .tsv + - .json + datatypes: + - nirs + entities: + subject: required + session: optional + task: required + acquisition: optional + run: optional + +coordsystem: + suffixes: + - coordsystem + extensions: + - .json + datatypes: + - nirs + entities: + subject: required + session: optional + acquisition: optional + +optodes: + suffixes: + - optodes + extensions: + - .tsv + - .json + datatypes: + - nirs + entities: + subject: required + session: optional + acquisition: optional + +events: + suffixes: + - events + extensions: + - .tsv + - .json + datatypes: + - nirs + entities: + subject: required + session: optional + task: required + acquisition: optional + run: optional + +photo: + suffixes: + - photo + extensions: + - .jpg + - .png + - .tif + entities: + subject: required + session: optional + +timeseries: + suffixes: + - physio + - stim + extensions: + - .tsv.gz + - .json + datatypes: + - nirs + entities: + subject: required + session: optional + task: required + acquisition: optional + run: optional + recording: optional diff --git a/src/schema/rules/sidecars/nirs.yaml b/src/schema/rules/sidecars/nirs.yaml new file mode 100644 index 0000000000..63c345d9ac --- /dev/null +++ b/src/schema/rules/sidecars/nirs.yaml @@ -0,0 +1,162 @@ +--- +CoordinateSystem: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + fields: + NIRSCoordinateSystem: required + NIRSCoordinateUnits: required + NIRSCoordinateProcessingDescription: recommended + +Fiducials: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + fields: + FiducialsDescription: optional + FiducialsCoordinates: recommended + FiducialsCoordinateUnits: recommended + FiducialsCoordinateSystem: recommended + +AnatomicalLandmark: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + fields: + AnatomicalLandmarkCoordinates: recommended + AnatomicalLandmarkCoordinateSystem: recommended + AnatomicalLandmarkCoordinateUnits: recommended + +CoordsystemGeneral: + selectors: + - modality == "nirs" + - datatype == "nirs" + - suffix == "coordsystem" + fields: + IntendedFor: + level: optional + description_addendum: | + This identifies the MRI or CT scan associated with the optodes, + landmarks, and fiducials. + +CoordinateSystemDescriptionRec: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + - json.NIRSCoordinateSystem != "other" + fields: + NIRSCoordinateSystemDescription: + level: recommended + level_addendum: required if NIRSCoordinateSystem is "other" + +CoordinateSystemDescriptionReq: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + - json.NIRSCoordinateSystem == "other" + fields: + NIRSCoordinateSystemDescription: required + +AnatomicalLandmarkCoordinateSystemDescriptionRec: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + - json.AnatomicalLandmarkCoordinateSystem != "other" + fields: + AnatomicalLandmarkCoordinateSystemDescription: + level: recommended + level_addendum: required if NIRSCoordinateSystem is "other" + +AnatomicalLandmarkCoordinateSystemDescriptionReq: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + - json.AnatomicalLandmarkCoordinateSystem == "other" + fields: + AnatomicalLandmarkCoordinateSystemDescription: required + +FiducialsCoordinateSystemDescriptionRec: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + - json.FiducialsCoordinateSystem != "other" + fields: + FiducialsCoordinateSystemDescription: + level: recommended + level_addendum: required if FiducialsCoordinateSystem is "other" + +FiducialsCoordinateSystemDescriptionReq: + selectors: + - datatype == "nirs" + - suffix == "coordsystem" + - json.FiducialsCoordinateSystem == "other" + fields: + FiducialsCoordinateSystemDescription: required + +NirsBase: + selectors: + - datatype == "nirs" + - suffix == "nirs" + fields: + TaskName: required + InstitutionName: recommended + InstitutionAddress: recommended + Manufacturer: recommended + ManufacturersModelName: recommended + SoftwareVersions: recommended + TaskDescription: recommended + Instructions: recommended + CogAtlasID: recommended + CogPOID: recommended + DeviceSerialNumber: recommended + RecordingDuration: recommended + HeadCircumference: recommended + HardwareFilters: recommended + SubjectArtefactDescription: recommended + +NirsRequired: + selectors: + - datatype == "nirs" + - suffix == "nirs" + fields: + SamplingFrequency__nirs: + level: required + description_addendum: | + Sampling frequency (in Hz) of all the data in the recording, regardless of their type (for example, `12`). If + individual channels have different sampling rates, then the field here MUST be specified as `n/a` and the + values MUST be specified in the `sampling_frequency` column in channels.tsv.") + NIRSChannelCount: required + NIRSSourceOptodeCount: required + NIRSDetectorOptodeCount: required + # Following counts required conditions enforced in checks + ACCELChannelCount: + level: optional + level_addendum: required if any channel type is ACC + GYROChannelCount: + level: optional + level_addendum: required if any channel type is GYRO + MAGNChannelCount: + level: optional + level_addendum: required if any channel type is MAGN + +NirsRecommend: + selectors: + - modality == "fnirs" + - suffix == "nirs" + fields: + CapManufacturer: + level: recommended + description_addendum: | + If no cap was used, such as with optodes + that are directly taped to the scalp, then the string `none` MUST be used and the `NIRSPlacementScheme` field + MAY be used to specify the optode placement. + CapManufacturersModelName: + level: recommended + description_addendum: | + If there is no official model number then a description may be provided (for example, `Headband with print + (S-M)`). If a cap from a manufacturer was modified, then the field MUST be set to `custom`. If no cap + was used, then the `CapManufacturer` field MUST be `none` and this field MUST be `n/a`.") + SourceType: recommended + DetectorType: recommended + ShortChannelCount: recommended + NIRSPlacementScheme: recommended diff --git a/src/schema/rules/tabular_data/nirs.yaml b/src/schema/rules/tabular_data/nirs.yaml new file mode 100644 index 0000000000..95cd16c844 --- /dev/null +++ b/src/schema/rules/tabular_data/nirs.yaml @@ -0,0 +1,64 @@ +--- +nirsChannels: + selectors: + - datatype == "nirs" + - suffix == "channels" + - extension == ".tsv" + initial_columns: + - name__channels + - type__nirs_channels + - source__channels + - detector__channels + - wavelength_nominal + - units__nirs + columns: + name__channels: required + type__nirs_channels: required + source__channels: required + detector__channels: required + wavelength_nominal: required + units__nirs: required + sampling_frequency: + level: optional + level_addendum: required if `SamplingFrequency` is `n/a` in `_nirs.json` + orientation_component: + level: optional + level_addendum: required if `type` is `ACCEL`, `GYRO` or `MAGN` + wavelength_actual: optional + description: optional + wavelength_emission_actual: optional + short_channel: optional + status: optional + status_description: optional + additional_columns: allowed_if_defined + +nirsOptodes: + selectors: + - datatype == "nirs" + - suffix == "optodes" + - extension == ".tsv" + initial_columns: + - name__optodes + - type__optodes + - x__optodes + - y__optodes + - z__optodes + columns: + name__optodes: required + type__optodes: required + x__optodes: required + y__optodes: required + z__optodes: required + template_x: + level: optional + level_addendum: required if `x` is `n/a` + template_y: + level: optional + level_addendum: required if `y` is `n/a` + template_z: + level: optional + level_addendum: required if `z` is `n/a` + description__optode: optional + detector_type: optional + source__optodes: optional + additional_columns: allowed_if_defined