Skip to content

oxytcmri.interface.mri.voxel_data_adapters

NIfTI adapter implementations.

Classes:

Name Description
InMemoryNumpyVoxelData

Voxel data stored in memory as a numpy array.

NiftiVoxelData

Implementation of VoxelData for NIfTI files.

NiftiAbnormalVoxelData

NiftiAbnormalVoxelData combines AbnormalVoxelData and NiftiVoxelData functionalities.

Attributes:

Name Type Description
T
logger

T = TypeVar('T') module-attribute

logger = logging.getLogger(__name__) module-attribute

InMemoryNumpyVoxelData(data=None, voxel_volume=None)

Bases: VoxelData[T]

Voxel data stored in memory as a numpy array.

Initialize the InMemoryNumpyVoxelData object.

Parameters:

Name Type Description Default
data ndarray

Numpy array containing voxel data.

None

Methods:

Name Description
get_value_at

Get the value at the specified coordinates.

set_value_at

Set the value of a voxel at a specific position.

get_dimensions

Get the dimensions of the voxel data.

get_voxel_volume_in_ml

Get the volume of a voxel in milliliters.

filter_values

Create a boolean representation of voxel data based on a filtering condition.

isin

Implements membership testing using numpy's isin function.

Attributes:

Name Type Description
value_type type[T]

Get the type of the voxel values.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
26
27
28
29
30
31
32
33
34
35
def __init__(self, data: np.ndarray = None, voxel_volume: float = None):
    """Initialize the InMemoryNumpyVoxelData object.

    Parameters
    ----------
    data : np.ndarray, optional
        Numpy array containing voxel data.
    """
    self._data = data
    self._voxel_volume = voxel_volume

value_type property

Get the type of the voxel values.

Returns:

Type Description
type[T]

Type of the voxel values.

get_value_at(x, y, z)

Get the value at the specified coordinates.

Parameters:

Name Type Description Default
x int

X coordinate.

required
y int

Y coordinate.

required
z int

Z coordinate.

required

Returns:

Type Description
T

Value at the specified coordinates.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def get_value_at(self, x: int, y: int, z: int) -> T:
    """Get the value at the specified coordinates.

    Parameters
    ----------
    x : int
        X coordinate.
    y : int
        Y coordinate.
    z : int
        Z coordinate.

    Returns
    -------
    T
        Value at the specified coordinates.
    """
    return self._data[x, y, z]

set_value_at(x, y, z, value)

Set the value of a voxel at a specific position.

Parameters:

Name Type Description Default
x int

x-coordinate of the voxel

required
y int

y-coordinate of the voxel

required
z int

z-coordinate of the voxel

required
value T

Value to set for the voxel

required

Returns:

Type Description
None
Source code in oxytcmri/interface/mri/voxel_data_adapters.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def set_value_at(self, x: int, y: int, z: int, value: T) -> None:
    """
    Set the value of a voxel at a specific position.

    Parameters
    ----------
    x : int
        x-coordinate of the voxel
    y : int
        y-coordinate of the voxel
    z : int
        z-coordinate of the voxel
    value : T
        Value to set for the voxel

    Returns
    -------
    None
    """
    self._data[x, y, z] = value

get_dimensions()

Get the dimensions of the voxel data.

Returns:

Type Description
Tuple[int, int, int]

Dimensions of the voxel data (x, y, z).

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
90
91
92
93
94
95
96
97
98
def get_dimensions(self) -> Tuple[int, int, int]:
    """Get the dimensions of the voxel data.

    Returns
    -------
    Tuple[int, int, int]
        Dimensions of the voxel data (x, y, z).
    """
    return self._data.shape

get_voxel_volume_in_ml()

Get the volume of a voxel in milliliters.

Returns:

Type Description
float

Volume of a voxel in milliliters.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
100
101
102
103
104
105
106
107
108
def get_voxel_volume_in_ml(self) -> float:
    """Get the volume of a voxel in milliliters.

    Returns
    -------
    float
        Volume of a voxel in milliliters.
    """
    return self._voxel_volume

filter_values(condition)

Create a boolean representation of voxel data based on a filtering condition.

Parameters:

Name Type Description Default
condition Callable[[T], bool]

Function that takes a voxel value and returns True if the voxel should be included in the filter

required

Returns:

Type Description
VoxelData[bool]

A boolean representation where voxels are True if they match the condition

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def filter_values(self, condition: Callable[[T], bool]) -> VoxelData[bool]:
    """Create a boolean representation of voxel data based on a filtering condition.

    Parameters
    ----------
    condition : Callable[[T], bool]
        Function that takes a voxel value and returns True if the voxel
        should be included in the filter

    Returns
    -------
    VoxelData[bool]
        A boolean representation where voxels are True if they match the condition
    """
    warnings.warn(
        "filter_values is deprecated. Use comparison operators instead.",
        DeprecationWarning,
        stacklevel=2
    )

    return InMemoryNumpyVoxelData(condition(self._data), self.get_voxel_volume_in_ml())

isin(values)

Implements membership testing using numpy's isin function.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
179
180
181
def isin(self, values: Collection[T]) -> VoxelData[bool]:
    """Implements membership testing using numpy's isin function."""
    return InMemoryNumpyVoxelData(np.isin(self._data, list(values)), self._voxel_volume)

NiftiVoxelData(nifti_path)

Bases: Generic[T], VoxelData[T]

Implementation of VoxelData for NIfTI files.

Parameters:

Name Type Description Default
nifti_path Path

Path to the NIfTI file.

required

Initialize the NiftiVoxelData object.

Parameters:

Name Type Description Default
nifti_path Path

Path to the NIfTI file.

required

Methods:

Name Description
create_with_same_metadata

Create a new NiftiVoxelData object using the metadata from an existing one.

get_nifti_image

Get the NIfTI image object.

get_data

Get the voxel data as a numpy array.

get_nifti_absolute_path_string

Get the absolute path to the NIfTI file as a string.

get_value_at

Get the value at the specified coordinates.

set_value_at
get_dimensions

Get the dimensions of the voxel data.

get_voxel_volume_in_ml

Get the volume of a single voxel in milliliters (mL).

filter_values

Filter the voxel data based on a condition.

get_parent_directory

Get the parent directory of the NIfTI file.

get_filename_without_extension

Get the filename of the NIfTI file without the extension.

isin

Implements membership testing using numpy's isin function.

Attributes:

Name Type Description
nifti_path
value_type type[T]

Get the type of the voxel values.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
193
194
195
196
197
198
199
200
201
202
def __init__(self, nifti_path: Path):
    """Initialize the NiftiVoxelData object.

    Parameters
    ----------
    nifti_path : Path
        Path to the NIfTI file.
    """
    self.nifti_path = nifti_path
    self._data = None

nifti_path = nifti_path instance-attribute

value_type property

Get the type of the voxel values.

Returns:

Type Description
type[T]

Type of the voxel values.

create_with_same_metadata(source_nifti, output_path, data=None) classmethod

Create a new NiftiVoxelData object using the metadata from an existing one.

This factory method creates a new NIfTI file with the same affine and header as the source, but with different data. Useful for creating derived images that need to maintain spatial reference.

Parameters:

Name Type Description Default
source_nifti NiftiVoxelData

The source NiftiVoxelData to copy metadata from

required
output_path Path

Path where to save the new NIfTI file.

required
data Optional[ndarray]

The new data array to use in the created NIfTI file. If None, a numpy array of zeros is created with the same shape as the source.

None

Returns:

Type Description
NiftiVoxelData

A new NiftiVoxelData object with the source's metadata and the provided data

Raises:

Type Description
ValueError

If the shape of the provided data doesn't match the source dimensions

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
@classmethod
def create_with_same_metadata(cls,
                              source_nifti: NiftiVoxelData,
                              output_path: Path,
                              data: Optional[np.ndarray] = None,
                              ) -> 'NiftiVoxelData':
    """
    Create a new NiftiVoxelData object using the metadata from an existing one.

    This factory method creates a new NIfTI file with the same affine and header
    as the source, but with different data. Useful for creating derived images
    that need to maintain spatial reference.

    Parameters
    ----------
    source_nifti : NiftiVoxelData
        The source NiftiVoxelData to copy metadata from
    output_path : Path
        Path where to save the new NIfTI file.
    data : Optional[np.ndarray]
        The new data array to use in the created NIfTI file. If None, a numpy array of zeros is created with the
        same shape as the source.

    Returns
    -------
    NiftiVoxelData
        A new NiftiVoxelData object with the source's metadata and the provided data

    Raises
    ------
    ValueError
        If the shape of the provided data doesn't match the source dimensions
    """
    # Get the source NIfTI image
    source_img = source_nifti.get_nifti_image()

    # If source dimensions don't match the data dimensions, raise an error
    source_dims = source_nifti.get_dimensions()
    if data is not None and data.shape[:3] != source_dims:
        raise ValueError(f"Data shape {data.shape[:3]} doesn't match source dimensions {source_dims}")

    # If data is None, create a numpy array of zeros with the same shape as the source
    if data is None:
        data = np.zeros(source_dims)

    # Create a new NIfTI image with the data and the source affine and header
    nifti_img = nib.Nifti1Image(data, affine=source_img.affine, header=source_img.header.copy())

    # Save the image to the output path
    nib.save(nifti_img, output_path)

    # Create and return a new NiftiVoxelData object
    result = cls(output_path)
    result._data = data

    return result

get_nifti_image()

Get the NIfTI image object.

Returns:

Type Description
Nifti1Image

NIfTI image object.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
265
266
267
268
269
270
271
272
273
def get_nifti_image(self) -> FileBasedImage:
    """Get the NIfTI image object.

    Returns
    -------
    nib.Nifti1Image
        NIfTI image object.
    """
    return nib.load(self.nifti_path)

get_data()

Get the voxel data as a numpy array.

Returns:

Type Description
ndarray

Voxel data as a numpy array.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
275
276
277
278
279
280
281
282
283
284
285
286
def get_data(self) -> np.ndarray:
    """Get the voxel data as a numpy array.

    Returns
    -------
    np.ndarray
        Voxel data as a numpy array.
    """
    if self._data is None:
        # Load the data from the NIfTI file
        self._data = self.get_nifti_image().get_fdata()
    return self._data

get_nifti_absolute_path_string()

Get the absolute path to the NIfTI file as a string.

Returns:

Type Description
str

Path to the NIfTI file.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
288
289
290
291
292
293
294
295
296
def get_nifti_absolute_path_string(self) -> str:
    """Get the absolute path to the NIfTI file as a string.

    Returns
    -------
    str
        Path to the NIfTI file.
    """
    return str(self.nifti_path.resolve())

get_value_at(x, y, z)

Get the value at the specified coordinates.

Parameters:

Name Type Description Default
x int

X coordinate.

required
y int

Y coordinate.

required
z int

Z coordinate.

required

Returns:

Type Description
T

Value at the specified coordinates.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
def get_value_at(self, x: int, y: int, z: int) -> T:
    """Get the value at the specified coordinates.

    Parameters
    ----------
    x : int
        X coordinate.
    y : int
        Y coordinate.
    z : int
        Z coordinate.

    Returns
    -------
    T
        Value at the specified coordinates.
    """
    # Check if the coordinates are within bounds
    dimensions = self.get_dimensions()
    if 0 <= x < dimensions[0] and 0 <= y < dimensions[1] and 0 <= z < dimensions[2]:
        return cast(T, self.get_data()[x, y, z])
    else:
        raise ValueError(
            f"Coordinates ({x}, {y}, {z}) are out of bounds. Shape is {dimensions}"
        )

set_value_at(x, y, z, value)

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
324
325
def set_value_at(self, x: int, y: int, z: int, value: T) -> None:
    raise NotImplementedError("Setting values in NIfTI files is not (yet) implemented.")

get_dimensions()

Get the dimensions of the voxel data.

Returns:

Type Description
Tuple[int, int, int]

Dimensions of the voxel data (x, y, z).

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
327
328
329
330
331
332
333
334
335
336
337
338
339
340
def get_dimensions(self) -> Tuple[int, int, int]:
    """Get the dimensions of the voxel data.

    Returns
    -------
    Tuple[int, int, int]
        Dimensions of the voxel data (x, y, z).
    """
    # Get the shape of the data array
    shape = self.get_data().shape

    # Return the first three dimensions (x, y, z)
    # Some NIfTI files might have a 4th dimension (time), which we ignore here
    return shape[0], shape[1], shape[2]

get_voxel_volume_in_ml()

Get the volume of a single voxel in milliliters (mL).

1 mL = 1000 mm³

Returns:

Type Description
float

Volume of a single voxel in milliliters (mL).

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
def get_voxel_volume_in_ml(self) -> float:
    """Get the volume of a single voxel in milliliters (mL).

    1 mL = 1000 mm³

    Returns
    -------
    float
        Volume of a single voxel in milliliters (mL).
    """
    # Get the spatial dimensions of each voxel in mm
    # nibabel stores this information in the NIfTI file header
    # The header contains "zooms" which are the physical dimensions of voxels
    zooms = self.get_nifti_image().header.get_zooms()

    # Calculate the volume by multiplying the 3 dimensions
    # We only consider the first 3 dimensions (x, y, z)
    # because some files might have a 4th dimension (time)
    # Convert from mm³ to mL (division by 1000)
    volume = (zooms[0] * zooms[1] * zooms[2]) / 1000

    return float(volume)

filter_values(condition)

Filter the voxel data based on a condition.

Parameters:

Name Type Description Default
condition Callable[[T], bool]

A function that takes a voxel value and returns True if the voxel should be included in the filter.

required

Returns:

Type Description
VoxelData[bool]

A boolean representation where voxels are True if they match the condition

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
def filter_values(self, condition: Callable[[T], bool]) -> VoxelData[bool]:
    """
    Filter the voxel data based on a condition.

    Parameters
    ----------
    condition : Callable[[T], bool]
        A function that takes a voxel value and returns True if the voxel
        should be included in the filter.

    Returns
    -------
    VoxelData[bool]
        A boolean representation where voxels are True if they match the condition
    """
    warnings.warn(
        "filter_values is deprecated. Use comparison operators instead.",
        DeprecationWarning,
        stacklevel=2
    )

    # Create a boolean numpy array based on the condition applied to the data
    vectorized_condition = np.vectorize(condition)
    numpy_array_bool = vectorized_condition(self.get_data())

    # Return a new InMemoryNumpyVoxelData object with the filtered data
    return InMemoryNumpyVoxelData(numpy_array_bool, self.get_voxel_volume_in_ml())

get_parent_directory()

Get the parent directory of the NIfTI file.

Returns:

Type Description
Path

Parent directory of the NIfTI file.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
393
394
395
396
397
398
399
400
401
def get_parent_directory(self) -> Path:
    """Get the parent directory of the NIfTI file.

    Returns
    -------
    Path
        Parent directory of the NIfTI file.
    """
    return self.nifti_path.parent

get_filename_without_extension()

Get the filename of the NIfTI file without the extension.

Returns:

Type Description
str

Filename without the extension.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
403
404
405
406
407
408
409
410
411
def get_filename_without_extension(self) -> str:
    """Get the filename of the NIfTI file without the extension.

    Returns
    -------
    str
        Filename without the extension.
    """
    return self.nifti_path.name.removesuffix(".nii.gz")

isin(values)

Implements membership testing using numpy's isin function.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
472
473
474
def isin(self, values: Collection[T]) -> VoxelData[bool]:
    """Implements membership testing using numpy's isin function."""
    return InMemoryNumpyVoxelData(np.isin(self.get_data(), list(values)), self.get_voxel_volume_in_ml())

NiftiAbnormalVoxelData(source_voxel_data, nifti_path, abnormal_voxels=None)

Bases: AbnormalVoxelData, NiftiVoxelData[int]

NiftiAbnormalVoxelData combines AbnormalVoxelData and NiftiVoxelData functionalities. This class allows storing, manipulating, and persisting abnormal data in NIfTI files.

Initialize a NiftiAbnormalVoxelData instance.

Parameters:

Name Type Description Default
source_voxel_data VoxelData[float]

Source voxel data

required
nifti_path Path

Path to the NIfTI file

required
abnormal_voxels Optional[Dict[Tuple[int, int, int], AbnormalValueType]]

Dictionary of abnormal voxels (optional)

None

Methods:

Name Description
from_abnormal_voxel_data

Create a NiftiAbnormalVoxelData from an existing AbnormalVoxelData.

Attributes:

Name Type Description
abnormal_voxels Dict[Tuple[int, int, int], AbnormalValueType]

Get abnormal voxels with lazy loading.

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
def __init__(self,
             source_voxel_data: VoxelData[float],
             nifti_path: Path,
             abnormal_voxels: Optional[Dict[Tuple[int, int, int], AbnormalValueType]] = None) -> None:
    """
    Initialize a NiftiAbnormalVoxelData instance.

    Parameters
    ----------
    source_voxel_data : VoxelData[float]
        Source voxel data
    nifti_path : Path
        Path to the NIfTI file
    abnormal_voxels : Optional[Dict[Tuple[int, int, int], AbnormalValueType]]
        Dictionary of abnormal voxels (optional)
    """
    # Initialize both parent classes
    AbnormalVoxelData.__init__(self, source_voxel_data)
    NiftiVoxelData.__init__(self, nifti_path)

    # Initialize lazy loading flags
    self._data_loaded = False
    self._abnormal_voxels_cache = abnormal_voxels

abnormal_voxels property writable

Get abnormal voxels with lazy loading.

from_abnormal_voxel_data(abnormal_voxel_data, nifti_path) classmethod

Create a NiftiAbnormalVoxelData from an existing AbnormalVoxelData.

Parameters:

Name Type Description Default
abnormal_voxel_data AbnormalVoxelData

Abnormal data to convert

required
nifti_path Path

Path where to save the NIfTI file

required

Returns:

Type Description
NiftiAbnormalVoxelData

Instance with the converted abnormal data

Source code in oxytcmri/interface/mri/voxel_data_adapters.py
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
@classmethod
def from_abnormal_voxel_data(cls,
                             abnormal_voxel_data: AbnormalVoxelData,
                             nifti_path: Path) -> "NiftiAbnormalVoxelData":
    """
    Create a NiftiAbnormalVoxelData from an existing AbnormalVoxelData.

    Parameters
    ----------
    abnormal_voxel_data : AbnormalVoxelData
        Abnormal data to convert
    nifti_path : Path
        Path where to save the NIfTI file

    Returns
    -------
    NiftiAbnormalVoxelData
        Instance with the converted abnormal data
    """
    # Convert the AbnormalVoxelData to a numpy array of integers
    integer_data = cls._convert_to_integer_numpy_array(abnormal_voxel_data)

    source_voxel_data = abnormal_voxel_data.get_source_voxel_data()
    if not isinstance(source_voxel_data, NiftiVoxelData):
        raise ValueError("Source voxel data must be of type NiftiVoxelData")

    # Create a NIfTI file with the metadata from the source file
    NiftiVoxelData.create_with_same_metadata(
        source_nifti=cast(NiftiVoxelData, source_voxel_data),
        output_path=nifti_path,
        data=integer_data,
    )

    # Create and return a new NiftiAbnormalVoxelData
    return cls(
        source_voxel_data=source_voxel_data,
        nifti_path=nifti_path,
        abnormal_voxels=abnormal_voxel_data.abnormal_voxels.copy()
    )