From 192a757531cad4075a9420fcf08d7a8bf253b0ff Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sun, 8 Sep 2024 09:49:37 +0000 Subject: [PATCH 1/7] allow dataset sagittal_slices setting it is unused, but avoid error if it exists --- petric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/petric.py b/petric.py index d23a230..18fb47a 100755 --- a/petric.py +++ b/petric.py @@ -141,7 +141,7 @@ def keys(self): class MetricsWithTimeout(cil_callbacks.Callback): """Stops the algorithm after `seconds`""" - def __init__(self, seconds=600, outdir=OUTDIR, transverse_slice=None, coronal_slice=None, **kwargs): + def __init__(self, seconds=600, outdir=OUTDIR, transverse_slice=None, coronal_slice=None, sagittal_slice=None, **kwargs): super().__init__(**kwargs) self._seconds = seconds self.callbacks = [ From 7ee18b7587d6f07301e91c68c18e5a1af35a75eb Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sun, 8 Sep 2024 09:50:30 +0000 Subject: [PATCH 2/7] data_QC improvements - optionally use slices from dataset_settings for data_QC - print VOI COMs --- SIRF_data_preparation/data_QC.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/SIRF_data_preparation/data_QC.py b/SIRF_data_preparation/data_QC.py index 77401e1..ac51090 100644 --- a/SIRF_data_preparation/data_QC.py +++ b/SIRF_data_preparation/data_QC.py @@ -11,12 +11,13 @@ --transverse_slice= idx [default: -1] --coronal_slice= idx [default: -1] --sagittal_slice= idx [default: -1] + --dataset= dataset name. if set, it is used to override default slices Note that -1 one means to use middle of image """ # Copyright 2024 University College London # Licence: Apache-2.0 -__version__ = '0.2.0' +__version__ = '0.3.0' import os import os.path @@ -29,6 +30,7 @@ from scipy import ndimage import sirf.STIR as STIR +from SIRF_data_preparation.dataset_settings import get_settings STIR.AcquisitionData.set_storage_scheme('memory') @@ -120,6 +122,7 @@ def VOI_checks(allVOInames, OSEM_image=None, reference_image=None, srcdir='.', * continue VOI = STIR.ImageData(filename) COM = np.rint(ndimage.center_of_mass(VOI.as_array())) + print(f"VOI: {VOIname}: COM: {COM}") plt.figure() plot_image(VOI, save_name=prefix, vmin=0, vmax=1, transverse_slice=int(COM[0]), coronal_slice=int(COM[1]), sagittal_slice=int(COM[2])) @@ -149,6 +152,7 @@ def VOI_checks(allVOInames, OSEM_image=None, reference_image=None, srcdir='.', * def main(argv=None): args = docopt(__doc__, argv=argv, version=__version__) + dataset = args['--dataset'] srcdir = args['--srcdir'] skip_sino_profiles = args['--skip_sino_profiles'] slices = {} @@ -156,6 +160,13 @@ def main(argv=None): slices["coronal_slice"] = literal_eval(args['--coronal_slice']) slices["sagittal_slice"] = literal_eval(args['--sagittal_slice']) + if (dataset): + settings = get_settings(dataset) + for key in slices.keys(): + if slices[key] == -1 and key in settings.slices: + slices[key] = settings.slices[key] + print(slices) + if not skip_sino_profiles: acquired_data = STIR.AcquisitionData(os.path.join(srcdir, 'prompts.hs')) additive_term = STIR.AcquisitionData(os.path.join(srcdir, 'additive_term.hs')) From 4f1e1cfc41f7b40cff3b5452a6d053e84324a2c9 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Sun, 8 Sep 2024 10:02:32 +0000 Subject: [PATCH 3/7] add Mediso_NEMA_IQ --- .../Mediso_NEMA_IQ/README.md | 33 +++++++++++++++++++ .../Mediso_NEMA_IQ/prepare.py | 10 ++++++ SIRF_data_preparation/dataset_settings.py | 3 ++ 3 files changed, 46 insertions(+) create mode 100644 SIRF_data_preparation/Mediso_NEMA_IQ/README.md create mode 100644 SIRF_data_preparation/Mediso_NEMA_IQ/prepare.py diff --git a/SIRF_data_preparation/Mediso_NEMA_IQ/README.md b/SIRF_data_preparation/Mediso_NEMA_IQ/README.md new file mode 100644 index 0000000..e1ff61c --- /dev/null +++ b/SIRF_data_preparation/Mediso_NEMA_IQ/README.md @@ -0,0 +1,33 @@ +# Data from the Mediso AnyScan at NPL +Initial processing steps +```sh +orgpath=/mnt/share/petric-wip/NEMA_Challenge +#orgpath=~/devel/PETRIC/orgdata/ +# cd $orgpath +#wget https://nplanywhere.npl.co.uk/userportal/?v=4.6.1#/shared/public/nrKe5vWynvyMsp7m/e9e3d4b8-8f83-47be-bf2d-17a96a7a8aac +#unzip NEMA_Challenge.zip +cd ~/devel/PETRIC/data +mkdir -p Mediso_NEMA_IQ +cd Mediso_NEMA_IQ/ +# trim sinograms to avoid "corner" problems in mult_factors +for f in additive_term.hs mult_factors.hs prompts.hs; do + SSRB -t 20 $f $orgpath/sinograms/$f; +done +#cp -rp $orgpath/sinograms/* . +# get rid of NaNs +python prepare.py +mkdir PETRIC +cp -rp $orgpath/VOIs/* PETRIC +# cp -rp $orgpath/README.md . +cd PETRIC +stir_math VOI_background.hv VOI_backgroung.hv +rm VOI_backgroung.* +rm *ahv +cd .. +python ../../SIRF_data_preparation/create_initial_images.py --template_image=PETRIC/VOI_whole_object.hv . + ``` + I needed to fix the OSEM header (manual): + ``` + !imaging modality := PT + ``` +python ../../SIRF_data_preparation/data_QC diff --git a/SIRF_data_preparation/Mediso_NEMA_IQ/prepare.py b/SIRF_data_preparation/Mediso_NEMA_IQ/prepare.py new file mode 100644 index 0000000..68c2f42 --- /dev/null +++ b/SIRF_data_preparation/Mediso_NEMA_IQ/prepare.py @@ -0,0 +1,10 @@ +# Set NaNs to zero in additive_term +import sirf.STIR +import numpy as np +additive = sirf.STIR.AcquisitionData('additive_term.hs') +add_arr = additive.as_array() +add_arr = np.nan_to_num(add_arr) +new_add = additive.clone() +new_add.fill(add_arr) +new_add.write('additive_term.hs') + diff --git a/SIRF_data_preparation/dataset_settings.py b/SIRF_data_preparation/dataset_settings.py index 0891e24..25b6ab8 100644 --- a/SIRF_data_preparation/dataset_settings.py +++ b/SIRF_data_preparation/dataset_settings.py @@ -19,6 +19,9 @@ def get_settings(scanID: str): elif scanID == 'NeuroLF_Hoffman_Dataset': slices = {'transverse_slice': 72} num_subsets = 16 + elif scanID == 'Mediso_NEMA_IQ': + slices = {'transverse_slice': 22, 'coronal_slice': 89, 'sagittal_slice': 66} + num_subsets = 12 else: # Vision slices = {} num_subsets = 5 From 9097ae2793561ee83faaa4dcebfe4dc98964966b Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Mon, 9 Sep 2024 06:36:29 +0000 Subject: [PATCH 4/7] restore running 400 OSEM updates in the evaluation scripts --- SIRF_data_preparation/run_OSEM.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SIRF_data_preparation/run_OSEM.py b/SIRF_data_preparation/run_OSEM.py index 500e63e..00885ee 100644 --- a/SIRF_data_preparation/run_OSEM.py +++ b/SIRF_data_preparation/run_OSEM.py @@ -42,7 +42,7 @@ data = get_data(srcdir=srcdir, outdir=outdir) # %% algo = main_OSEM.Submission(data, settings.num_subsets, update_objective_interval=20) -algo.run(20, callbacks=[MetricsWithTimeout(**settings.slices, seconds=5000, outdir=outdir)]) +algo.run(400, callbacks=[MetricsWithTimeout(**settings.slices, seconds=5000, outdir=outdir)]) # %% fig = plt.figure() data_QC.plot_image(algo.get_output(), **settings.slices) From 281d57e92a1677b8c803b004297c5f5a64d7b067 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Mon, 9 Sep 2024 07:03:24 +0000 Subject: [PATCH 5/7] small fixes to evualuation script - fix iteration interval - fix number of thresholds --- SIRF_data_preparation/plot_BSREM_metrics.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/SIRF_data_preparation/plot_BSREM_metrics.py b/SIRF_data_preparation/plot_BSREM_metrics.py index 6ff643f..a5d0606 100644 --- a/SIRF_data_preparation/plot_BSREM_metrics.py +++ b/SIRF_data_preparation/plot_BSREM_metrics.py @@ -1,4 +1,5 @@ -"""Preliminary file to check evolution of metrics as well as pass_index""" +#%% +# """Preliminary file to check evolution of metrics as well as pass_index""" # %load_ext autoreload # %autoreload 2 from pathlib import Path @@ -21,7 +22,8 @@ # scanID = 'Siemens_Vision600_thorax' # scanID = 'NeuroLF_Hoffman_Dataset' -scanID = 'Siemens_mMR_NEMA_IQ' +# scanID = 'Siemens_mMR_NEMA_IQ' +scanID = 'Mediso_NEMA_IQ' srcdir = SRCDIR / scanID outdir = OUTDIR / scanID @@ -69,8 +71,10 @@ reference_image = STIR.ImageData(str(datadir / 'iter_final.hv')) qm = QualityMetrics(reference_image, data.whole_object_mask, data.background_mask, tb_summary_writer=None, voi_mask_dict=data.voi_masks) +#%% get update ("iteration") numbers from objective functions last_iteration = int(objs[-1, 0] + .5) -iteration_interval = int(objs[-1, 0] - objs[-2, 0] + .5) +# find interval(don't use last value, as that interval can be smaller) +iteration_interval = int(objs[-2, 0] - objs[-3, 0] + .5) if datadir1.is_dir(): last_iteration = int(objs0[-1, 0] + .5) iteration_interval = int(objs0[-1, 0] - objs0[-2, 0] + .5) * 2 @@ -78,7 +82,8 @@ iters = range(0, last_iteration, iteration_interval) m = get_metrics(qm, iters, srcdir=datadir) # %% -OSEMiters = range(0, 400, 20) +OSEMobjs = read_objectives(OSEMdir) +OSEMiters = OSEMobjs[:, 0].astype(int) OSEMm = get_metrics(qm, OSEMiters, srcdir=OSEMdir) # %% fig = plt.figure() @@ -107,7 +112,7 @@ fig.savefig(outdir / f'{scanID}_metrics_BSREM.png') # %% -idx = pass_index(m, numpy.array([.01, .01, .005, .005, .005]), 10) +idx = pass_index(m, numpy.array([.01, .01] + [.005 for i in range(len(data.voi_masks))]), 10) iter = iters[idx] print(iter) image = STIR.ImageData(str(datadir / f"iter_{iter:04d}.hv")) From a1423492bd22ecca57d48d50a0bbfa5cc4ee72b7 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Mon, 9 Sep 2024 07:39:18 +0000 Subject: [PATCH 6/7] print VOI volume in data_QC --- SIRF_data_preparation/data_QC.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SIRF_data_preparation/data_QC.py b/SIRF_data_preparation/data_QC.py index ac51090..cdffe03 100644 --- a/SIRF_data_preparation/data_QC.py +++ b/SIRF_data_preparation/data_QC.py @@ -121,8 +121,10 @@ def VOI_checks(allVOInames, OSEM_image=None, reference_image=None, srcdir='.', * print(f"VOI {VOIname} does not exist") continue VOI = STIR.ImageData(filename) - COM = np.rint(ndimage.center_of_mass(VOI.as_array())) - print(f"VOI: {VOIname}: COM: {COM}") + VOI_arr = VOI.as_array() + COM = np.rint(ndimage.center_of_mass(VOI_arr)) + num_voxels = VOI_arr.sum() + print(f"VOI: {VOIname}: COM (in indices): {COM} voxels {num_voxels} = {num_voxels * np.prod(VOI.spacing)} mm^3") plt.figure() plot_image(VOI, save_name=prefix, vmin=0, vmax=1, transverse_slice=int(COM[0]), coronal_slice=int(COM[1]), sagittal_slice=int(COM[2])) From 2b8f10c03d26017583d6298c6b0864c978fd5758 Mon Sep 17 00:00:00 2001 From: Kris Thielemans Date: Mon, 9 Sep 2024 21:48:54 +0100 Subject: [PATCH 7/7] some extra comments in Mediso_NEMA_IQ/README --- SIRF_data_preparation/Mediso_NEMA_IQ/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SIRF_data_preparation/Mediso_NEMA_IQ/README.md b/SIRF_data_preparation/Mediso_NEMA_IQ/README.md index e1ff61c..670bce5 100644 --- a/SIRF_data_preparation/Mediso_NEMA_IQ/README.md +++ b/SIRF_data_preparation/Mediso_NEMA_IQ/README.md @@ -10,12 +10,15 @@ cd ~/devel/PETRIC/data mkdir -p Mediso_NEMA_IQ cd Mediso_NEMA_IQ/ # trim sinograms to avoid "corner" problems in mult_factors +# TODO for next data: use 30 (as some problems remain) for f in additive_term.hs mult_factors.hs prompts.hs; do SSRB -t 20 $f $orgpath/sinograms/$f; done +# alternative if we don't need to trim #cp -rp $orgpath/sinograms/* . # get rid of NaNs python prepare.py +# now handle VOIs mkdir PETRIC cp -rp $orgpath/VOIs/* PETRIC # cp -rp $orgpath/README.md .