Skip to content

fUSI-BIDS Integration

bids

FUSI-BIDS utilities for ConfUSIus.

This module provides utilities for working with fUSI-BIDS metadata, including conversion between ConfUSIus and fUSI-BIDS naming conventions, validation of metadata, and coordinate handling.

Examples:

>>> from confusius import bids
>>>
>>> # Convert ConfUSIus attributes to BIDS format
>>> attrs = {"repetition_time": 1.5, "task_name": "rest", "transmit_frequency": 15e6}
>>> bids_attrs = bids.to_bids(attrs)
>>> bids_attrs
{"RepetitionTime": 1.5, "TaskName": "rest", "UltrasoundTransmitFrequency": 15000000.0}
>>>
>>> # Convert BIDS metadata back to ConfUSIus format
>>> attrs_back = bids.from_bids(bids_attrs)
>>> attrs_back
{"repetition_time": 1.5, "task_name": "rest", "transmit_frequency": 15000000.0}
>>>
>>> # Validate fUSI-BIDS metadata
>>> metadata = bids.validate_metadata({
...     "TaskName": "rest",
...     "RepetitionTime": 1.5,
... })

Modules:

  • coordinates

    Coordinate handling for fUSI-BIDS timing metadata.

  • mapping

    FUSI-BIDS field mapping between ConfUSIus and BIDS naming conventions.

  • validation

    Pydantic models for fUSI-BIDS metadata validation.

Classes:

Functions:

Attributes:

DIM_TO_SLICE_ENCODING_DIRECTION module-attribute

DIM_TO_SLICE_ENCODING_DIRECTION: Final[dict[str, str]] = {
    "x": "i",
    "y": "j",
    "z": "k",
}

Mapping from ConfUSIus dimension names to fUSI-BIDS SliceEncodingDirection values.

EXPLICIT_BIDS_FIELD_MAPPINGS module-attribute

EXPLICIT_BIDS_FIELD_MAPPINGS: Final[dict[str, str]] = {
    "transmit_frequency": "UltrasoundTransmitFrequency",
    "pulse_repetition_frequency": "UltrasoundPulseRepetitionFrequency",
    "volume_acquisition_duration": "FrameAcquisitionDuration",
}

Explicit mappings for standard BIDS fields with non-automatic names.

Maps ConfUSIus internal names to fUSI-BIDS field names.

FUSI_BIDS_FIELDS module-attribute

FUSI_BIDS_FIELDS: frozenset[str] = frozenset(keys())

Set of all fUSI-BIDS field names derived from the Pydantic model.

This is the single source of truth for field names.

SLICE_ENCODING_DIRECTION_TO_DIM module-attribute

SLICE_ENCODING_DIRECTION_TO_DIM: Final[dict[str, str]] = {
    "i": "x",
    "i-": "x",
    "j": "y",
    "j-": "y",
    "k": "z",
    "k-": "z",
}

Mapping from fUSI-BIDS SliceEncodingDirection values to ConfUSIus dimension names.

FUSIBIDSMetadata

Bases: BaseModel

FUSI-BIDS metadata model for sidecar JSON validation.

This model represents the fUSI-BIDS sidecar metadata as defined in the fUSI-BIDS extension proposal (BEP) v0.0.12.

Examples:

>>> metadata = FUSIBIDSMetadata(
...     TaskName="rest",
...     RepetitionTime=1.5,
...     Manufacturer="Verasonics",
... )
Notes

At least one of RepetitionTime or VolumeTiming must be provided for valid fUSI-BIDS timing information.

Methods:

Attributes:

model_extra property

model_extra: dict[str, Any] | None

Get extra fields set during validation.

Returns: A dictionary of extra fields, or None if config.extra is not set to "allow".

model_fields_set property

model_fields_set: set[str]

Returns the set of fields that have been explicitly set on this model instance.

Returns: A set of strings representing the fields that have been set, i.e. that were not filled from defaults.

convert_numpy_array classmethod

convert_numpy_array(v: Any) -> Any

Convert numpy arrays to lists for JSON serialization.

Parameters:

  • v
    (Any) –

    Value to convert. If a numpy array, converted to list.

Returns:

  • Any

    List if input was numpy array, otherwise input unchanged.

copy

copy(
    *,
    include: AbstractSetIntStr
    | MappingIntStrAny
    | None = None,
    exclude: AbstractSetIntStr
    | MappingIntStrAny
    | None = None,
    update: Dict[str, Any] | None = None,
    deep: bool = False,
) -> Self

Returns a copy of the model.

Deprecated

This method is now deprecated; use model_copy instead.

If you need include or exclude, use:

data = self.model_dump(include=include, exclude=exclude, round_trip=True)
data = {**data, **(update or {})}
copied = self.model_validate(data)

Args: include: Optional set or mapping specifying which fields to include in the copied model. exclude: Optional set or mapping specifying which fields to exclude in the copied model. update: Optional dictionary of field-value pairs to override field values in the copied model. deep: If True, the values of fields that are Pydantic models will be deep-copied.

Returns: A copy of the model with included, excluded and updated fields as specified.

model_computed_fields classmethod

model_computed_fields() -> dict[str, ComputedFieldInfo]

A mapping of computed field names to their respective ComputedFieldInfo instances.

Warning

Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3. Instead, you should access this attribute from the model class.

model_construct classmethod

model_construct(
    _fields_set: set[str] | None = None, **values: Any
) -> Self

Creates a new instance of the Model class with validated data.

Creates a new model setting __dict__ and __pydantic_fields_set__ from trusted or pre-validated data. Default values are respected, but no other validation is performed.

Note

model_construct() generally respects the model_config.extra setting on the provided model. That is, if model_config.extra == 'allow', then all extra passed values are added to the model instance's __dict__ and __pydantic_extra__ fields. If model_config.extra == 'ignore' (the default), then all extra passed values are ignored. Because no validation is performed with a call to model_construct(), having model_config.extra == 'forbid' does not result in an error if extra values are passed, but they will be ignored.

Args: _fields_set: A set of field names that were originally explicitly set during instantiation. If provided, this is directly used for the model_fields_set attribute. Otherwise, the field names from the values argument will be used. values: Trusted or pre-validated data dictionary.

Returns: A new instance of the Model class with validated data.

model_copy

model_copy(
    *,
    update: Mapping[str, Any] | None = None,
    deep: bool = False,
) -> Self

Usage Documentation

model_copy

Returns a copy of the model.

Note

The underlying instance's __dict__ attribute is copied. This might have unexpected side effects if you store anything in it, on top of the model fields (e.g. the value of cached properties).

Args: update: Values to change/add in the new model. Note: the data is not validated before creating the new model. You should trust this data. deep: Set to True to make a deep copy of the model.

Returns: New model instance.

model_dump

model_dump(
    *,
    mode: Literal["json", "python"] | str = "python",
    include: IncEx | None = None,
    exclude: IncEx | None = None,
    context: Any | None = None,
    by_alias: bool | None = None,
    exclude_unset: bool = False,
    exclude_defaults: bool = False,
    exclude_none: bool = False,
    exclude_computed_fields: bool = False,
    round_trip: bool = False,
    warnings: bool
    | Literal["none", "warn", "error"] = True,
    fallback: Callable[[Any], Any] | None = None,
    serialize_as_any: bool = False,
) -> dict[str, Any]

Usage Documentation

model_dump

Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.

Args: mode: The mode in which to_python should run. If mode is 'json', the output will only contain JSON serializable types. If mode is 'python', the output may contain non-JSON-serializable Python objects. include: A set of fields to include in the output. exclude: A set of fields to exclude from the output. context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. exclude_unset: Whether to exclude fields that have not been explicitly set. exclude_defaults: Whether to exclude fields that are set to their default value. exclude_none: Whether to exclude fields that have a value of None. exclude_computed_fields: Whether to exclude computed fields. While this can be useful for round-tripping, it is usually recommended to use the dedicated round_trip parameter instead. round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, "error" raises a PydanticSerializationError. fallback: A function to call when an unknown value is encountered. If not provided, a PydanticSerializationError error is raised. serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.

Returns: A dictionary representation of the model.

model_dump_json

model_dump_json(
    *,
    indent: int | None = None,
    ensure_ascii: bool = False,
    include: IncEx | None = None,
    exclude: IncEx | None = None,
    context: Any | None = None,
    by_alias: bool | None = None,
    exclude_unset: bool = False,
    exclude_defaults: bool = False,
    exclude_none: bool = False,
    exclude_computed_fields: bool = False,
    round_trip: bool = False,
    warnings: bool
    | Literal["none", "warn", "error"] = True,
    fallback: Callable[[Any], Any] | None = None,
    serialize_as_any: bool = False,
) -> str

Usage Documentation

model_dump_json

Generates a JSON representation of the model using Pydantic's to_json method.

Args: indent: Indentation to use in the JSON output. If None is passed, the output will be compact. ensure_ascii: If True, the output is guaranteed to have all incoming non-ASCII characters escaped. If False (the default), these characters will be output as-is. include: Field(s) to include in the JSON output. exclude: Field(s) to exclude from the JSON output. context: Additional context to pass to the serializer. by_alias: Whether to serialize using field aliases. exclude_unset: Whether to exclude fields that have not been explicitly set. exclude_defaults: Whether to exclude fields that are set to their default value. exclude_none: Whether to exclude fields that have a value of None. exclude_computed_fields: Whether to exclude computed fields. While this can be useful for round-tripping, it is usually recommended to use the dedicated round_trip parameter instead. round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, "error" raises a PydanticSerializationError. fallback: A function to call when an unknown value is encountered. If not provided, a PydanticSerializationError error is raised. serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.

Returns: A JSON string representation of the model.

model_fields classmethod

model_fields() -> dict[str, FieldInfo]

A mapping of field names to their respective FieldInfo instances.

Warning

Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3. Instead, you should access this attribute from the model class.

model_json_schema classmethod

model_json_schema(
    by_alias: bool = True,
    ref_template: str = DEFAULT_REF_TEMPLATE,
    schema_generator: type[
        GenerateJsonSchema
    ] = GenerateJsonSchema,
    mode: JsonSchemaMode = "validation",
    *,
    union_format: Literal[
        "any_of", "primitive_type_array"
    ] = "any_of",
) -> dict[str, Any]

Generates a JSON schema for a model class.

Args: by_alias: Whether to use attribute aliases or not. ref_template: The reference template. union_format: The format to use when combining schemas from unions together. Can be one of:

    - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
    keyword to combine schemas (the default).
    - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
    keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
    type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
    `any_of`.
schema_generator: To override the logic used to generate the JSON schema, as a subclass of
    `GenerateJsonSchema` with your desired modifications
mode: The mode in which to generate the schema.

Returns: The JSON schema for the given model class.

model_parametrized_name classmethod

model_parametrized_name(
    params: tuple[type[Any], ...],
) -> str

Compute the class name for parametrizations of generic classes.

This method can be overridden to achieve a custom naming scheme for generic BaseModels.

Args: params: Tuple of types of the class. Given a generic class Model with 2 type variables and a concrete model Model[str, int], the value (str, int) would be passed to params.

Returns: String representing the new class where params are passed to cls as type variables.

Raises: TypeError: Raised when trying to generate concrete names for non-generic models.

model_post_init

model_post_init(context: Any) -> None

Override this method to perform additional initialization after __init__ and model_construct. This is useful if you want to do some validation that requires the entire model to be initialized.

model_rebuild classmethod

model_rebuild(
    *,
    force: bool = False,
    raise_errors: bool = True,
    _parent_namespace_depth: int = 2,
    _types_namespace: MappingNamespace | None = None,
) -> bool | None

Try to rebuild the pydantic-core schema for the model.

This may be necessary when one of the annotations is a ForwardRef which could not be resolved during the initial attempt to build the schema, and automatic rebuilding fails.

Args: force: Whether to force the rebuilding of the model schema, defaults to False. raise_errors: Whether to raise errors, defaults to True. _parent_namespace_depth: The depth level of the parent namespace, defaults to 2. _types_namespace: The types namespace, defaults to None.

Returns: Returns None if the schema is already "complete" and rebuilding was not required. If rebuilding was required, returns True if rebuilding was successful, otherwise False.

model_validate classmethod

model_validate(
    obj: Any,
    *,
    strict: bool | None = None,
    extra: ExtraValues | None = None,
    from_attributes: bool | None = None,
    context: Any | None = None,
    by_alias: bool | None = None,
    by_name: bool | None = None,
) -> Self

Validate a pydantic model instance.

Args: obj: The object to validate. strict: Whether to enforce types strictly. extra: Whether to ignore, allow, or forbid extra data during model validation. See the extra configuration value for details. from_attributes: Whether to extract data from object attributes. context: Additional context to pass to the validator. by_alias: Whether to use the field's alias when validating against the provided input data. by_name: Whether to use the field's name when validating against the provided input data.

Raises: ValidationError: If the object could not be validated.

Returns: The validated model instance.

model_validate_json classmethod

model_validate_json(
    json_data: str | bytes | bytearray,
    *,
    strict: bool | None = None,
    extra: ExtraValues | None = None,
    context: Any | None = None,
    by_alias: bool | None = None,
    by_name: bool | None = None,
) -> Self

Usage Documentation

JSON Parsing

Validate the given JSON data against the Pydantic model.

Args: json_data: The JSON data to validate. strict: Whether to enforce types strictly. extra: Whether to ignore, allow, or forbid extra data during model validation. See the extra configuration value for details. context: Extra variables to pass to the validator. by_alias: Whether to use the field's alias when validating against the provided input data. by_name: Whether to use the field's name when validating against the provided input data.

Returns: The validated Pydantic model.

Raises: ValidationError: If json_data is not a JSON string or the object could not be validated.

model_validate_strings classmethod

model_validate_strings(
    obj: Any,
    *,
    strict: bool | None = None,
    extra: ExtraValues | None = None,
    context: Any | None = None,
    by_alias: bool | None = None,
    by_name: bool | None = None,
) -> Self

Validate the given object with string data against the Pydantic model.

Args: obj: The object containing string data to validate. strict: Whether to enforce types strictly. extra: Whether to ignore, allow, or forbid extra data during model validation. See the extra configuration value for details. context: Extra variables to pass to the validator. by_alias: Whether to use the field's alias when validating against the provided input data. by_name: Whether to use the field's name when validating against the provided input data.

Returns: The validated Pydantic model.

validate_slice_timing_consistency

validate_slice_timing_consistency() -> 'FUSIBIDSMetadata'

Validate that SliceTiming and SliceEncodingDirection are consistent.

Returns:

validate_timing

validate_timing() -> 'FUSIBIDSMetadata'

Validate timing field constraints per fUSI-BIDS spec.

Enforces: - RepetitionTime and VolumeTiming are mutually exclusive. - FrameAcquisitionDuration is REQUIRED when VolumeTiming is used AND SliceTiming is not set. - FrameAcquisitionDuration is mutually exclusive with RepetitionTime.

Returns:

create_bids_slice_timing_from_coordinate

create_bids_slice_timing_from_coordinate(
    slice_time_coord: DataArray, volume_times: ArrayLike
) -> tuple[ndarray, str]

Extract fUSI-BIDS SliceTiming metadata from a slice_time coordinate.

Parameters:

  • slice_time_coord

    ((time, slice_encoding_direction) xarray.DataArray) –

    Absolute slice time coordinate.

  • volume_times

    ((n_time,) array-like) –

    Absolute onset time of each volume, in the same units as slice_time_coord.

Returns:

  • slice_timing ( ndarray ) –

    The slice acquisition times for one volume.

  • slice_encoding_direction ( {i, j, k} ) –

    The direction of slice acquisition.

Raises:

  • ValueError

    If the coordinate is not 2D, has an invalid spatial dimension, or if the slice timing varies across volumes after converting to onset-relative offsets.

Examples:

>>> slice_timing, direction = extract_slice_timing_from_coordinate(coord, volume_times)
>>> slice_timing
array([0. , 0.1, 0.2, 0.3])
>>> direction
'k'

create_slice_time_coordinate_from_bids

create_slice_time_coordinate_from_bids(
    volume_times: ArrayLike,
    slice_timing: ArrayLike,
    slice_encoding_direction: Literal[
        "i", "j", "k", "i-", "j-", "k-"
    ],
    units: str = "s",
) -> DataArray

Create a slice_time coordinate from fUSI-BIDS SliceTiming metadata.

The slice_time coordinate stores the absolute acquisition time of each slice for each volume.

Parameters:

  • volume_times

    ((n_time,) array-like) –

    Absolute onset time of each volume, in the same units as slice_timing.

  • slice_timing

    ((n_slices,) array-like) –

    Array of slice acquisition times relative to the onset of each volume.

  • slice_encoding_direction

    ((i, j, k, 'i-', 'j-', 'k-'), default: "i" ) –

    Direction of slice acquisition: "i"x, "j"y, "k"z. A trailing - indicates that slice_timing is defined in reverse order (the first entry corresponds to the slice with the largest index). The values are reversed internally so the stored coordinate is always aligned with natural slice index order.

  • units

    (str, default: 's' ) –

    Units of the slice timing values.

Returns:

  • (n_time, n_slices) xarray.DataArray

    Absolute slice time coordinates.

Examples:

>>> import numpy as np
>>> slice_timing = np.array([0.0, 0.1, 0.2, 0.3])  # 4 slices
>>> volume_times = np.array([0.0, 1.0])
>>> coord = create_slice_time_coordinate(
...     volume_times, slice_timing, slice_encoding_direction="k"
... )
>>> coord.dims
('time', 'z')
>>> coord.shape
(2, 4)

from_bids

from_bids(
    bids_attrs: Mapping[str, object],
) -> dict[str, object]

Convert fUSI-BIDS metadata to ConfUSIus format.

Known fUSI-BIDS fields are converted to snake_case. ConfUSIus-prefixed attributes are converted back to internal names. Unknown fields are preserved as-is to ensure round-trip safety.

Parameters:

  • bids_attrs

    (Mapping[str, object]) –

    Dictionary with fUSI-BIDS (PascalCase) field names.

Returns:

  • dict[str, object]

    Dictionary with ConfUSIus (snake_case) attribute names.

Examples:

>>> bids_attrs = {"RepetitionTime": 1.5, "TaskName": "rest", "CustomField": 123}
>>> from_bids(bids_attrs)
{'repetition_time': 1.5, 'task_name': 'rest', 'CustomField': 123}

to_bids

to_bids(attrs: Mapping[str, object]) -> dict[str, object]

Convert ConfUSIus attributes to fUSI-BIDS format.

Only converts known fUSI-BIDS fields. Internal ConfUSIus attributes are prefixed with "ConfUSIus". Unknown fields are preserved as-is.

Parameters:

  • attrs

    (Mapping[str, object]) –

    Dictionary with ConfUSIus (snake_case) attribute names.

Returns:

  • dict[str, object]

    Dictionary with fUSI-BIDS (PascalCase) field names.

Examples:

>>> attrs = {"repetition_time": 1.5, "task_name": "rest", "custom_field": 123}
>>> to_bids(attrs)
{'RepetitionTime': 1.5, 'TaskName': 'rest', 'custom_field': 123}

validate_metadata

validate_metadata(
    metadata: Mapping[str, Any],
) -> "FUSIBIDSMetadata"

Validate fUSI-BIDS metadata dictionary.

Parameters:

  • metadata

    (Mapping[str, Any]) –

    Dictionary containing fUSI-BIDS metadata fields.

Returns:

Examples:

>>> metadata = validate_metadata({
...     "TaskName": "rest",
...     "RepetitionTime": 1.5,
... })
>>> metadata.TaskName
'rest'

Raises:

  • ValidationError

    If the metadata fails validation.