From f4f8895d49a2987419429827575faf4382fc02d7 Mon Sep 17 00:00:00 2001 From: Tom Close Date: Thu, 19 Oct 2023 17:30:59 +1100 Subject: [PATCH 1/3] added PET raw data --- fileformats/medimage/__init__.py | 12 +++-- fileformats/medimage/raw.py | 62 ------------------------ fileformats/medimage/raw/__init__.py | 16 ++++++ fileformats/medimage/raw/mri/__init__.py | 14 ++++++ fileformats/medimage/raw/pet/__init__.py | 14 ++++++ fileformats/medimage/raw/pet/base.py | 27 +++++++++++ fileformats/medimage/raw/pet/siemens.py | 29 +++++++++++ 7 files changed, 109 insertions(+), 65 deletions(-) delete mode 100644 fileformats/medimage/raw.py create mode 100644 fileformats/medimage/raw/__init__.py create mode 100644 fileformats/medimage/raw/mri/__init__.py create mode 100644 fileformats/medimage/raw/pet/__init__.py create mode 100644 fileformats/medimage/raw/pet/base.py create mode 100644 fileformats/medimage/raw/pet/siemens.py diff --git a/fileformats/medimage/__init__.py b/fileformats/medimage/__init__.py index 5c9f7c9..ef10886 100644 --- a/fileformats/medimage/__init__.py +++ b/fileformats/medimage/__init__.py @@ -30,9 +30,15 @@ # SiemensDicomDir, ) from .raw import ( # noqa: F401 - ListMode, Kspace, - TwixVb, - # CustomKspace, Rda, + PetListMode, + PetSinogram, + PetCountRate, + PetNormalisation, + Vnd_Siemens_PetRawData, + Vnd_Siemens_PetListMode, + Vnd_Siemens_PetSinogram, + Vnd_Siemens_PetCountRate, + Vnd_Siemens_PetNormalisation, ) diff --git a/fileformats/medimage/raw.py b/fileformats/medimage/raw.py deleted file mode 100644 index 32d3e38..0000000 --- a/fileformats/medimage/raw.py +++ /dev/null @@ -1,62 +0,0 @@ -from fileformats.generic import File - - -class ListMode(File): - - ext = ".bf" - binary = True - - -class Kspace(File): - - binary = True - iana_mime = None - - -class TwixVb(Kspace): - """The format that k-space data is saved in from Siemens scanners - with system version vB to (at least) vE""" - - ext = ".dat" - - -# class CustomKspace(Kspace): -# """A custom format for saving k-space data in binary amd JSON files. - -# Binary files -# ------------ -# primary : 5-d matrix -# Data from "data" scan organised in the following dimension order: -# channel, freq-encode, phase-encode, partition-encode (slice), echoes -# reference : 5-d matrix -# Data from calibration scan organised in the same dimension order as -# primary scan - -# JSON side-car -# ------------- -# dims : 3-tuple(int) -# The dimensions of the image in freq, phase, partition (slice) order -# voxel_size : 3-tuple(float) -# Size of the voxels in same order as dims -# num_channels : int -# Number of channels in the k-space -# num_echos : int -# Number of echoes in the acquisition -# T E : tuple(float) -# The echo times -# B0_strength : float -# Strength of the B0 field -# B0_dir : 3-tuple(float) -# Direction of the B0 field -# larmor_freq : float -# The central larmor row_frequency of the scanner""" - -# ext = ".ks" -# side_cars = ("ref", "json") - - -class Rda(File): - """MRS format""" - - ext = ".rda" - binary = True diff --git a/fileformats/medimage/raw/__init__.py b/fileformats/medimage/raw/__init__.py new file mode 100644 index 0000000..3a641e5 --- /dev/null +++ b/fileformats/medimage/raw/__init__.py @@ -0,0 +1,16 @@ +from .mri import ( # noqa: F401 + Kspace, + Rda, +) +from .pet import ( + PetRawData, + PetListMode, + PetSinogram, + PetCountRate, + PetNormalisation, + Vnd_Siemens_PetRawData, + Vnd_Siemens_PetListMode, + Vnd_Siemens_PetSinogram, + Vnd_Siemens_PetCountRate, + Vnd_Siemens_PetNormalisation, +) diff --git a/fileformats/medimage/raw/mri/__init__.py b/fileformats/medimage/raw/mri/__init__.py new file mode 100644 index 0000000..dd71cba --- /dev/null +++ b/fileformats/medimage/raw/mri/__init__.py @@ -0,0 +1,14 @@ +from fileformats.generic import File + + +class Kspace(File): + + binary = True + iana_mime = None + + +class Rda(File): + """MRS format""" + + ext = ".rda" + binary = True diff --git a/fileformats/medimage/raw/pet/__init__.py b/fileformats/medimage/raw/pet/__init__.py new file mode 100644 index 0000000..316170d --- /dev/null +++ b/fileformats/medimage/raw/pet/__init__.py @@ -0,0 +1,14 @@ +from .base import ( + PetRawData, + PetListMode, + PetSinogram, + PetCountRate, + PetNormalisation, +) +from .siemens import ( + Vnd_Siemens_PetRawData, + Vnd_Siemens_PetListMode, + Vnd_Siemens_PetSinogram, + Vnd_Siemens_PetCountRate, + Vnd_Siemens_PetNormalisation, +) diff --git a/fileformats/medimage/raw/pet/base.py b/fileformats/medimage/raw/pet/base.py new file mode 100644 index 0000000..024248d --- /dev/null +++ b/fileformats/medimage/raw/pet/base.py @@ -0,0 +1,27 @@ +from fileformats.generic import File + + +class PetRawData(File): + + binary = True + iana_mime = None + + +class PetListMode(PetRawData): + "raw projection data" + iana_mime = None + + +class PetSinogram(PetRawData): + "histogrammed projection data in a reconstruction-friendly format" + iana_mime = None + + +class PetCountRate(PetRawData): + "number of prompt/random/single events per unit time" + iana_mime = None + + +class PetNormalisation(PetRawData): + "normalisation scan or the current cross calibration factor" + iana_mime = None diff --git a/fileformats/medimage/raw/pet/siemens.py b/fileformats/medimage/raw/pet/siemens.py new file mode 100644 index 0000000..6557383 --- /dev/null +++ b/fileformats/medimage/raw/pet/siemens.py @@ -0,0 +1,29 @@ +from .base import ( + PetRawData, + PetListMode, + PetSinogram, + PetCountRate, + PetNormalisation, +) + + +class Vnd_Siemens_PetRawData(PetRawData): + + iana_mime = None + ext = ".ptd" + + +class Vnd_Siemens_PetListMode(Vnd_Siemens_PetRawData, PetListMode): + pass + + +class Vnd_Siemens_PetSinogram(Vnd_Siemens_PetRawData, PetSinogram): + "histogrammed projection data in a reconstruction-friendly format" + + +class Vnd_Siemens_PetCountRate(Vnd_Siemens_PetRawData, PetCountRate): + "number of prompt/random/single events per unit time" + + +class Vnd_Siemens_PetNormalisation(Vnd_Siemens_PetRawData, PetNormalisation): + "normalisation scan or the current cross calibration factor" From 13c515167267c3fde11d1bb8e8a8439147b3ca22 Mon Sep 17 00:00:00 2001 From: Tom Close Date: Sat, 28 Oct 2023 11:04:18 +1100 Subject: [PATCH 2/3] updated siemens biograph raw data to include model number + version --- fileformats/medimage/raw/pet/siemens.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fileformats/medimage/raw/pet/siemens.py b/fileformats/medimage/raw/pet/siemens.py index 6557383..522feef 100644 --- a/fileformats/medimage/raw/pet/siemens.py +++ b/fileformats/medimage/raw/pet/siemens.py @@ -7,23 +7,30 @@ ) -class Vnd_Siemens_PetRawData(PetRawData): - +class Vnd_Siemens_BiographVisionVr20b_PetRawData(PetRawData): iana_mime = None ext = ".ptd" -class Vnd_Siemens_PetListMode(Vnd_Siemens_PetRawData, PetListMode): +class Vnd_Siemens_BiographVisionVr20b_PetListMode( + Vnd_Siemens_BiographVisionVr20b_PetRawData, PetListMode +): pass -class Vnd_Siemens_PetSinogram(Vnd_Siemens_PetRawData, PetSinogram): +class Vnd_Siemens_BiographVisionVr20b_PetSinogram( + Vnd_Siemens_BiographVisionVr20b_PetRawData, PetSinogram +): "histogrammed projection data in a reconstruction-friendly format" -class Vnd_Siemens_PetCountRate(Vnd_Siemens_PetRawData, PetCountRate): +class Vnd_Siemens_BiographVisionVr20b_PetCountRate( + Vnd_Siemens_BiographVisionVr20b_PetRawData, PetCountRate +): "number of prompt/random/single events per unit time" -class Vnd_Siemens_PetNormalisation(Vnd_Siemens_PetRawData, PetNormalisation): +class Vnd_Siemens_BiographVisionVr20b_PetNormalisation( + Vnd_Siemens_BiographVisionVr20b_PetRawData, PetNormalisation +): "normalisation scan or the current cross calibration factor" From 4e5d91f3c873aaa635dc230073e081e59cf292f0 Mon Sep 17 00:00:00 2001 From: Tom Close Date: Mon, 13 Nov 2023 17:49:15 +1100 Subject: [PATCH 3/3] renamed siemens raw data to include full model name --- fileformats/medimage/__init__.py | 10 +++++----- fileformats/medimage/dicom.py | 17 ++++++++++------- fileformats/medimage/raw/__init__.py | 10 +++++----- fileformats/medimage/raw/pet/__init__.py | 10 +++++----- fileformats/medimage/raw/pet/siemens.py | 18 +++++++++--------- 5 files changed, 34 insertions(+), 31 deletions(-) diff --git a/fileformats/medimage/__init__.py b/fileformats/medimage/__init__.py index ef10886..7873030 100644 --- a/fileformats/medimage/__init__.py +++ b/fileformats/medimage/__init__.py @@ -36,9 +36,9 @@ PetSinogram, PetCountRate, PetNormalisation, - Vnd_Siemens_PetRawData, - Vnd_Siemens_PetListMode, - Vnd_Siemens_PetSinogram, - Vnd_Siemens_PetCountRate, - Vnd_Siemens_PetNormalisation, + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, + Vnd_Siemens_Biograph128Vision_Vr20b_PetListMode, + Vnd_Siemens_Biograph128Vision_Vr20b_PetSinogram, + Vnd_Siemens_Biograph128Vision_Vr20b_PetCountRate, + Vnd_Siemens_Biograph128Vision_Vr20b_PetNormalisation, ) diff --git a/fileformats/medimage/dicom.py b/fileformats/medimage/dicom.py index 7231f3b..e57bd03 100644 --- a/fileformats/medimage/dicom.py +++ b/fileformats/medimage/dicom.py @@ -39,7 +39,6 @@ class DicomDir(DicomCollection, DirectoryContaining[Dicom]): class DicomSeries(DicomCollection, SetOf[Dicom]): - @classmethod def from_paths( cls, fspaths: ty.Iterable[Path], common_ok: bool = False @@ -47,7 +46,7 @@ def from_paths( dicoms, remaining = Dicom.from_paths(fspaths, common_ok=common_ok) series_dict = defaultdict(list) for dicom in dicoms: - series_dict[dicom["SeriesNumber"]].append(dicom) + series_dict[(str(dicom["StudyInstanceUID"]), str(dicom["SeriesNumber"]))].append(dicom) return set([cls(s) for s in series_dict.values()]), remaining @@ -55,12 +54,16 @@ def from_paths( def dicom_collection_read_metadata(collection: DicomCollection) -> ty.Dict[str, ty.Any]: # Collated DICOM headers across series collated = copy(collection.contents[0].metadata) - if len(collection.contents) > 1: - for key, val in collection.contents[1].metadata.items(): - if val != collated[key]: # Turn field into list - collated[key] = [collated[key], val] - for dicom in collection.contents[2:]: + for i, dicom in enumerate(collection.contents[1:], start=1): for key, val in dicom.metadata.items(): if val != collated[key]: + # Check whether the value is the same as the values in the previous + # images in the series + if ( + not isinstance(collated[key], list) + or isinstance(val, list) + and not isinstance(collated[key][0], list) + ): + collated[key] = [collated[key]] * i + [val] collated[key].append(val) return collated diff --git a/fileformats/medimage/raw/__init__.py b/fileformats/medimage/raw/__init__.py index 3a641e5..ac2e3b4 100644 --- a/fileformats/medimage/raw/__init__.py +++ b/fileformats/medimage/raw/__init__.py @@ -8,9 +8,9 @@ PetSinogram, PetCountRate, PetNormalisation, - Vnd_Siemens_PetRawData, - Vnd_Siemens_PetListMode, - Vnd_Siemens_PetSinogram, - Vnd_Siemens_PetCountRate, - Vnd_Siemens_PetNormalisation, + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, + Vnd_Siemens_Biograph128Vision_Vr20b_PetListMode, + Vnd_Siemens_Biograph128Vision_Vr20b_PetSinogram, + Vnd_Siemens_Biograph128Vision_Vr20b_PetCountRate, + Vnd_Siemens_Biograph128Vision_Vr20b_PetNormalisation, ) diff --git a/fileformats/medimage/raw/pet/__init__.py b/fileformats/medimage/raw/pet/__init__.py index 316170d..14b8f70 100644 --- a/fileformats/medimage/raw/pet/__init__.py +++ b/fileformats/medimage/raw/pet/__init__.py @@ -6,9 +6,9 @@ PetNormalisation, ) from .siemens import ( - Vnd_Siemens_PetRawData, - Vnd_Siemens_PetListMode, - Vnd_Siemens_PetSinogram, - Vnd_Siemens_PetCountRate, - Vnd_Siemens_PetNormalisation, + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, + Vnd_Siemens_Biograph128Vision_Vr20b_PetListMode, + Vnd_Siemens_Biograph128Vision_Vr20b_PetSinogram, + Vnd_Siemens_Biograph128Vision_Vr20b_PetCountRate, + Vnd_Siemens_Biograph128Vision_Vr20b_PetNormalisation, ) diff --git a/fileformats/medimage/raw/pet/siemens.py b/fileformats/medimage/raw/pet/siemens.py index 522feef..ee13e1d 100644 --- a/fileformats/medimage/raw/pet/siemens.py +++ b/fileformats/medimage/raw/pet/siemens.py @@ -7,30 +7,30 @@ ) -class Vnd_Siemens_BiographVisionVr20b_PetRawData(PetRawData): +class Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData(PetRawData): iana_mime = None ext = ".ptd" -class Vnd_Siemens_BiographVisionVr20b_PetListMode( - Vnd_Siemens_BiographVisionVr20b_PetRawData, PetListMode +class Vnd_Siemens_Biograph128Vision_Vr20b_PetListMode( + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, PetListMode ): pass -class Vnd_Siemens_BiographVisionVr20b_PetSinogram( - Vnd_Siemens_BiographVisionVr20b_PetRawData, PetSinogram +class Vnd_Siemens_Biograph128Vision_Vr20b_PetSinogram( + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, PetSinogram ): "histogrammed projection data in a reconstruction-friendly format" -class Vnd_Siemens_BiographVisionVr20b_PetCountRate( - Vnd_Siemens_BiographVisionVr20b_PetRawData, PetCountRate +class Vnd_Siemens_Biograph128Vision_Vr20b_PetCountRate( + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, PetCountRate ): "number of prompt/random/single events per unit time" -class Vnd_Siemens_BiographVisionVr20b_PetNormalisation( - Vnd_Siemens_BiographVisionVr20b_PetRawData, PetNormalisation +class Vnd_Siemens_Biograph128Vision_Vr20b_PetNormalisation( + Vnd_Siemens_Biograph128Vision_Vr20b_PetRawData, PetNormalisation ): "normalisation scan or the current cross calibration factor"