Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Started to add in radlex classifiers and fixed up extras hooks #20

Merged
merged 11 commits into from
Dec 6, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ __pycache__
~*
*.venv
/fileformats/medimage/_version.py
/extras/fileformats/extras/medimage/_version.py
4 changes: 0 additions & 4 deletions extras/fileformats/extras/medimage/_version.py

This file was deleted.

12 changes: 6 additions & 6 deletions extras/fileformats/extras/medimage/dicom.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@


@MedicalImage.read_array.register
def dicom_read_array(collection: DicomCollection):
def dicom_read_array(collection: DicomCollection) -> np.ndarray:
image_stack = []
for dcm_file in collection.contents:
image_stack.append(pydicom.dcmread(dcm_file).pixel_array)
return np.asarray(image_stack)


@MedicalImage.vox_sizes.register
def dicom_vox_sizes(collection: DicomCollection):
def dicom_vox_sizes(collection: DicomCollection) -> ty.Tuple[float, float, float]:
return tuple(
collection.metadata["PixelSpacing"] + [collection.metadata["SliceThickness"]]
)


@MedicalImage.dims.register
def dicom_dims(collection: DicomCollection):
def dicom_dims(collection: DicomCollection) -> ty.Tuple[int, int, int]:
return tuple(
(
collection.metadata["Rows"],
Expand All @@ -37,12 +37,12 @@ def dicom_dims(collection: DicomCollection):


@DicomCollection.series_number.register
def dicom_series_number(collection: DicomCollection):
def dicom_series_number(collection: DicomCollection) -> str:
return str(collection.metadata["SeriesNumber"])


@FileSet.generate_sample_data.register
def dicom_dir_generate_sample_data(dcmdir: DicomDir, dest_dir: Path, seed: ty.Union[int, Random], stem: ty.Optional[str]):
def dicom_dir_generate_sample_data(dcmdir: DicomDir, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None) -> ty.Iterable[Path]:
dcm_dir = medimages4tests.dummy.dicom.mri.t1w.siemens.skyra.syngo_d13c.get_image()
# Set series number to random value to make it different
if isinstance(seed, Random):
Expand All @@ -60,7 +60,7 @@ def dicom_dir_generate_sample_data(dcmdir: DicomDir, dest_dir: Path, seed: ty.Un


@FileSet.generate_sample_data.register
def dicom_set_generate_sample_data(dcm_series: DicomSeries, dest_dir: Path, seed: int, stem: ty.Optional[str]):
def dicom_set_generate_sample_data(dcm_series: DicomSeries, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None) -> ty.Iterable[Path]:
rng = Random(seed)
dicom_dir = dicom_dir_generate_sample_data(dcm_series, dest_dir=mkdtemp(), seed=rng, stem=None)[0]
stem = gen_filename(rng, stem=stem)
Expand Down
6 changes: 3 additions & 3 deletions extras/fileformats/extras/medimage/diffusion.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import numpy as np
from fileformats.medimage.diffusion import DwiEncoding, Bval, Bvec
from fileformats.medimage import DwiEncoding, Bval, Bvec


@Bval.read_array.register
def bval_read_array(bval: Bval):
def bval_read_array(bval: Bval) -> np.ndarray: # noqa
return np.asarray([float(ln) for ln in bval.read_contents().split()])


@DwiEncoding.read_array.register
def bvec_read_array(bvec: Bvec):
def bvec_read_array(bvec: Bvec) -> np.ndarray: # noqa
bvals = bvec.b_values_file.read_array()
directions = np.asarray(
[[float(x) for x in ln.split()] for ln in bvec.read_contents().splitlines()]
Expand Down
38 changes: 26 additions & 12 deletions extras/fileformats/extras/medimage/nifti.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,65 @@
from pathlib import Path
import typing as ty
from random import Random
import nibabel
import numpy as np
from fileformats.core import FileSet
from fileformats.core.utils import gen_filename
from fileformats.medimage import MedicalImage, Nifti, NiftiGz, Nifti1, NiftiGzX, NiftiX
import medimages4tests.dummy.nifti


@FileSet.read_metadata.register
def nifti_read_metadata(nifti: Nifti):
def nifti_read_metadata(nifti: Nifti) -> ty.Mapping[str, ty.Any]:
return dict(nibabel.load(nifti.fspath).header)


@MedicalImage.read_array.register
def nifti_data_array(nifti: Nifti):
def nifti_data_array(nifti: Nifti) -> np.ndarray: # noqa
return nibabel.load(nifti.fspath).get_data()


@MedicalImage.vox_sizes.register
def nifti_vox_sizes(nifti: Nifti):
def nifti_vox_sizes(nifti: Nifti) -> ty.Tuple[float, float, float]:
# FIXME: This won't work for 4-D files
return nifti.metadata["pixdim"][1:4]
return tuple(float(d) for d in nifti.metadata["pixdim"][1:4])


@MedicalImage.dims.register
def nifti_dims(nifti: Nifti):
def nifti_dims(nifti: Nifti) -> ty.Tuple[int, int, int]:
# FIXME: This won't work for 4-D files
return nifti.metadata["dim"][1:4]
return tuple(int(d) for d in nifti.metadata["dim"][1:4])


@FileSet.generate_sample_data.register
def nifti_generate_sample_data(nifti: Nifti1, dest_dir: Path, seed: int, stem: ty.Optional[str]):
return medimages4tests.dummy.nifti.get_image(out_file=dest_dir / "nifti.nii")
def nifti_generate_sample_data(
nifti: Nifti1, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None
) -> ty.Iterable[Path]:
return medimages4tests.dummy.nifti.get_image(
out_file=dest_dir / gen_filename(seed, file_type=Nifti1, stem=stem)
)


@FileSet.generate_sample_data.register
def nifti_gz_generate_sample_data(nifti: NiftiGz, dest_dir: Path, seed: int, stem: ty.Optional[str]):
def nifti_gz_generate_sample_data(
nifti: NiftiGz, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None
) -> ty.Iterable[Path]:
return medimages4tests.dummy.nifti.get_image(
out_file=dest_dir / "nifti.nii.gz", compressed=True
out_file=dest_dir / gen_filename(seed, file_type=NiftiGz, stem=stem),
compressed=True,
)


@FileSet.generate_sample_data.register
def nifti_gz_x_generate_sample_data(nifti: NiftiGzX, dest_dir: Path, seed: int, stem: ty.Optional[str]):
def nifti_gz_x_generate_sample_data(
nifti: NiftiGzX, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None
) -> ty.Iterable[Path]:
return medimages4tests.mri.neuro.t1w.get_image()


@FileSet.generate_sample_data.register
def nifti_x_generate_sample_data(nifti: NiftiX, dest_dir: Path, seed: int, stem: ty.Optional[str]):
def nifti_x_generate_sample_data(
nifti: NiftiX, dest_dir: Path, seed: ty.Union[int, Random] = 0, stem: ty.Optional[str] = None
) -> ty.Iterable[Path]:
nifti_gz_x = NiftiGzX(medimages4tests.mri.neuro.t1w.get_image())
return NiftiX.convert(nifti_gz_x)
14 changes: 9 additions & 5 deletions fileformats/medimage/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from ._version import __version__
from .base import MedicalImage
from fileformats.application import Dicom # imported to alias it here as well
from ._version import __version__ # noqa: F401
from .base import MedicalImage # noqa: F401
# import Dicom to alias to the medimage namespace it here as well
from fileformats.application import Dicom # noqa: F401
from .misc import ( # noqa: F401
Analyze,
Mgh,
MghGz,
)
from .nifti import (
from .nifti import ( # noqa: F401
Nifti,
Nifti1,
Nifti2,
NiftiGz,
NiftiX,
NiftiGzX,
)
from .diffusion import (
from .diffusion import ( # noqa: F401
DwiEncoding,
Bvec,
Bval,
Expand Down Expand Up @@ -42,3 +43,6 @@
Vnd_Siemens_Biograph128Vision_Vr20b_PetCountRate,
Vnd_Siemens_Biograph128Vision_Vr20b_PetNormalisation,
)
from .surface import (
Gifti # noqa: F401
)
6 changes: 3 additions & 3 deletions fileformats/medimage/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ class MedicalImage(FileSet):
binary = True

@hook.extra
def read_array(self):
def read_array(self) -> "numpy.ndarray": # noqa
"""
Returns the binary data of the image in a numpy array
"""
raise NotImplementedError

@hook.extra
def vox_sizes(self) -> ty.Tuple[float]:
def vox_sizes(self) -> ty.Tuple[float, float, float]:
"""The length of the voxels along each dimension"""
raise NotImplementedError

@hook.extra
def dims(self) -> ty.Tuple[int]:
def dims(self) -> ty.Tuple[int, int, int]:
"""The dimensions of the image"""
raise NotImplementedError
4 changes: 2 additions & 2 deletions fileformats/medimage/dicom.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __len__(self):
return len(self.contents)

@hook.extra
def series_number(self):
def series_number(self) -> str:
raise NotImplementedError

@cached_property
Expand All @@ -51,7 +51,7 @@ def from_paths(


@FileSet.read_metadata.register
def dicom_collection_read_metadata(collection: DicomCollection) -> ty.Dict[str, ty.Any]:
def dicom_collection_read_metadata(collection: DicomCollection) -> ty.Mapping[str, ty.Any]:
# Collated DICOM headers across series
collated = copy(collection.contents[0].metadata)
for i, dicom in enumerate(collection.contents[1:], start=1):
Expand Down
10 changes: 5 additions & 5 deletions fileformats/medimage/diffusion.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ class DwiEncoding(File):
iana_mime: ty.Optional[str] = None

@hook.extra
def read_array(self):
def read_array(self) -> "numpy.ndarray": # noqa
"Both the gradient direction and weighting combined into a single Nx4 array"
raise NotImplementedError

@property
def array(self):
def array(self) -> "numpy.ndarray": # noqa
return self.read_array()

@property
def directions(self):
def directions(self) -> "numpy.ndarray": # noqa
"gradient direction and weighting combined into a single Nx4 array"
return self.array[:, :3]

@property
def b_values(self):
def b_values(self) -> "numpy.ndarray": # noqa
"the b-value weighting"
return self.array[:, 3]

Expand All @@ -34,7 +34,7 @@ class Bval(File):
ext = ".bval"

@hook.extra
def read_array(self):
def read_array(self) -> "numpy.ndarray": # noqa
raise NotImplementedError


Expand Down
6 changes: 6 additions & 0 deletions fileformats/medimage/radlex/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from fileformats.core import ClassifierCategory


class AnatomicalEntity(ClassifierCategory):
pass

5 changes: 5 additions & 0 deletions fileformats/medimage/radlex/anatomical/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from fileformats.core import ClassifierCategory


class AnatomicalEntity(ClassifierCategory):
pass
5 changes: 5 additions & 0 deletions fileformats/medimage/radlex/imaging/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from fileformats.core import ClassifierCategory


class ImagingSpecialty(ClassifierCategory):
pass
Loading
Loading