From dcc50a6eed4adb116d58725f19d309f88995699b Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Wed, 25 Sep 2024 13:24:27 +0100 Subject: [PATCH] addition of the GE_DMI3_Torso dataset --- SIRF_data_preparation/GE_DMI3_Torso/README.md | 25 ++++++ .../GE_DMI3_Torso/VOI_prep.py | 85 +++++++++++++++++++ SIRF_data_preparation/dataset_settings.py | 2 +- petric.py | 3 +- 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 SIRF_data_preparation/GE_DMI3_Torso/README.md create mode 100644 SIRF_data_preparation/GE_DMI3_Torso/VOI_prep.py diff --git a/SIRF_data_preparation/GE_DMI3_Torso/README.md b/SIRF_data_preparation/GE_DMI3_Torso/README.md new file mode 100644 index 0000000..689d246 --- /dev/null +++ b/SIRF_data_preparation/GE_DMI3_Torso/README.md @@ -0,0 +1,25 @@ +# Torso phantom scanned on a GE Discovery MI (3 ring) + +This data is from a scan performed by GE of the +Data Spectrum [Antropomorphic Torso Phantom](https://www.spect.com/pdf/anthropomorphic-torso-phantom.pdf) +with additional lesion inserts of diameter 10mm. + +Activity ratio between "liver" and "torso" is approximately 2. Lungs +are empty (air). Contrast of the lesions is unfortunately unknown. +,v +"Corrections" were obtained with GE Duetto and converted to STIR Interfile +using internal scripts. + +Due to data size issues and count levels, the supplied data is **reduced** from the original scan +by keeping only the "direct" sinograms (segment 0, corresponding to min/max ring differences -1/+1), e.g. on the command line +``` +SSRB prompts.hs prompts_f1b1.hs 1 1 0 0 1 +``` + +In addition, as current PETRIC examples do not cope with non-TOF multfactors for TOF data, the +multfactors were expanded to TOF (by duplication). + +## VOIs + +VOIs were determined by Kris Thielemans on reconstructed images and approximately placed +where some of the lesions are visible. They are not necessarily at the correct location. \ No newline at end of file diff --git a/SIRF_data_preparation/GE_DMI3_Torso/VOI_prep.py b/SIRF_data_preparation/GE_DMI3_Torso/VOI_prep.py new file mode 100644 index 0000000..3131c50 --- /dev/null +++ b/SIRF_data_preparation/GE_DMI3_Torso/VOI_prep.py @@ -0,0 +1,85 @@ +#%% file to prepare VOIs for the Torso phantom, using coordinates derived manually + +import matplotlib.pyplot as plt +import numpy as np +from pathlib import Path +import os +from scipy.ndimage import binary_fill_holes +import sirf.STIR as STIR +import SIRF_data_preparation.data_QC as data_QC +from SIRF_data_preparation.data_utilities import the_data_path, the_orgdata_path +from SIRF_data_preparation.dataset_settings import get_settings +#%% +scanID = 'GE_DMI3_Torso' +data_path = Path(the_data_path(scanID)) +output_path = Path(the_data_path(scanID, 'PETRIC')) +os.makedirs(output_path, exist_ok=True) +#%% +image_filename = str(data_path / 'OSEM_image.hv') +image = STIR.ImageData(image_filename) +# %% find whole phantom VOI by thresholding/filling +threshold = image.max() * .05 +im_arr = image.as_array() >= threshold +# we want to use binary_fill_holes to fill the spine and lung. However, +# that function does not fill holes connected to the boundary, so let's +# fill the boundary planes first +im_arr[0, :, :] = True +im_arr[-1, :, :] = True +im_arr = binary_fill_holes(im_arr) +# now exclude boundary +im_arr[0, :, :] = False +im_arr[-1, :, :] = False +VOI = image.allocate(0) +VOI.fill(im_arr.astype(np.float32)) +VOI.show() +VOI.write(str(output_path / 'VOI_whole_object.hv')) +#%% find centre image as KT got the coordinates from Amide +# note that geo.get_index_to_physical_point_matrix() is still unstable, so we won't use it. +# Warning: the following will likely fail for future versions of SIRF +zcentre = (image.shape[0] - 1)/2 * image.voxel_sizes()[0] +# %% background VOI (in approx middle of torso) +shape = STIR.EllipticCylinder() +shape.set_length(23) +shape.set_radii((120, 120)) +shape.set_origin((zcentre + 0, 0, 0)) +VOI.fill(0) +VOI.add_shape(shape, scale = 1) +VOI.write(str(output_path / 'VOI_background.hv')) +# %% spine (approximately top half of it) +shape = STIR.EllipticCylinder() +shape.set_length(100) +shape.set_radii((22, 22)) +shape.set_origin((zcentre + 42.6, 86.9, 3)) +VOI.fill(0) +VOI.add_shape(shape, scale = 1) +VOI.write(str(output_path / 'VOI_spine.hv')) +VOI.show() +# %% lung lesion +shape = STIR.Ellipsoid() +shape.set_radius_z(5) +shape.set_radius_y(5) +shape.set_radius_x(5) +shape.set_origin((zcentre + 25, -42.2, -105.3)) +VOI.fill(0) +VOI.add_shape(shape, scale = 1) +VOI.write(str(output_path / 'VOI_lung_lesion.hv')) +# %% +# %% liver lesion +shape = STIR.Ellipsoid() +shape.set_radius_z(5) +shape.set_radius_y(5) +shape.set_radius_x(5) +shape.set_origin((zcentre - 38.2, -38.9, -49.7)) +VOI.fill(0) +VOI.add_shape(shape, scale = 1) +VOI.write(str(output_path / 'VOI_liver_lesion.hv')) +# %% lung lesion +shape = STIR.Ellipsoid() +shape.set_radius_z(5) +shape.set_radius_y(5) +shape.set_radius_x(5) +shape.set_origin((zcentre + 47.6, 12.5, 151.5)) +VOI.fill(0) +VOI.add_shape(shape, scale = 1) +VOI.write(str(output_path / 'VOI_chest_lesion.hv')) + diff --git a/SIRF_data_preparation/dataset_settings.py b/SIRF_data_preparation/dataset_settings.py index a0a9628..1f8f827 100644 --- a/SIRF_data_preparation/dataset_settings.py +++ b/SIRF_data_preparation/dataset_settings.py @@ -5,7 +5,7 @@ DATA_SUBSETS = { 'Siemens_mMR_NEMA_IQ': 7, 'Siemens_mMR_NEMA_IQ_lowcounts': 7, 'Siemens_mMR_ACR': 7, 'NeuroLF_Hoffman_Dataset': 16, - 'Mediso_NEMA_IQ': 12, 'Siemens_Vision600_thorax': 5} + 'Mediso_NEMA_IQ': 12, 'Siemens_Vision600_thorax': 5, 'GE_DMI3_Torso': 8} @dataclass diff --git a/petric.py b/petric.py index 005d7dd..da6d49a 100755 --- a/petric.py +++ b/petric.py @@ -252,7 +252,8 @@ def get_image(fname): 'Siemens_mMR_ACR': {'transverse_slice': 99}, 'NeuroLF_Hoffman_Dataset': {'transverse_slice': 72}, 'Mediso_NEMA_IQ': {'transverse_slice': 22, 'coronal_slice': 89, 'sagittal_slice': 66}, - 'Siemens_Vision600_thorax': {}} + 'Siemens_Vision600_thorax': {}, + 'GE_DMI3_Torso': {}} if SRCDIR.is_dir(): # create list of existing data